k dingwall
k dingwall

Reputation:

can a method call be chained to the 'set()' built-in? (and why not?)

If I try:

mi_list = ['three', 'small', 'words']
mi_set = set(mi_list)
mi_set.remove('small')
print mi_set  

I get:

set(['three', 'words'])  

which is what I expect. Whereas If I try:

mi_list = ['three', 'small', 'words']
mi_set = set(mi_list).remove('small')
print mi_set

I get:

None

Why?

I suspect there's a clue in that if I try to remove an element that isn't present - eg 'big' - an error gets reported:

KeyError: 'big'

Upvotes: 3

Views: 223

Answers (6)

The way to go, in your case, would be to use the difference member:

>>> a = set(["a", "b", "c"])
>>> a = a.difference(["a"])
>>> print a
set(['c', 'b'])

The difference is that remove acts on the current set (python library object member functions that modify an instance usually return None), whereas difference creates and returns a new set.

Upvotes: 1

allyourcode
allyourcode

Reputation: 22623

remove modifies the original set without returning anything (or rather, it returns None). This example shows what happens to the original object when you call remove on it:

Python 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> lst = [1,2,3]
>>> s1 = set(lst)
>>> s1
{1, 2, 3}
>>> s2 = s1.remove(2)  # instead of reassigning s1, I save the result of remove to s2
>>> s1
{1, 3}  # *** 2 is not an element in the original set ***
>>> s2  # s2 is not a set at all!
>>> 

To answer the other part of your question, the exception indicates that remove tried to remove the argument from the set, but couldn't because the argument is not in the set. Conversely, remove returns None to indicate success.

Upvotes: 0

John Fouhy
John Fouhy

Reputation: 42243

There is a general convention in python that methods which cause side-effects return None. Examples include list.sort, list.append, set.add, set.remove, dict.update, etc.

This is essentially to help you avoid bugs. Say you had a set called mi_set. If you could write:

mi_set2 = mi_set.remove('small')

then a reader might think: "mi_set2 is different from mi_set". But this would not be the case! And the confusion might lead to subtle bugs caused by mistakenly sharing data structures. So by returning None, python forces you to remember that methods like those I listed above modify objects, rather than creating new ones.

See also long discussion here. Although note that methods like sorted() and reversed() have been added since that thread.

[note that list.pop is an exception to this rule, for historical reasons – it lets you use a list like a stack. And even then, it returns the object removed, rather than the list.]

Upvotes: 8

viraptor
viraptor

Reputation: 34205

Why does it return None? Because .remove, .add, etc. return None :) That's it. They do not support chaining.

set is using methods that change it in place. You could create your own version of set that uses chaining, but that can cause some problems:

class chain_set(set):
   def chain_add(self, x):
      newself = self.copy()
      newself.add(x)
      return newself

cs = chain_set([1,2,3,4])
cs.chain_add(5)
# chain_set([1, 2, 3, 4, 5])
cs.chain_add(7)
# chain_set([1, 2, 3, 4, 7])
cs.chain_add(7).chain_add(8)
# chain_set([1, 2, 3, 4, 7, 8])

The problem is - do you expect cs itself to change?

Do you always want to modify the original set (might create some hard to find bugs) or do you want to copy the set every time (might be slow with bigger sets). If you know what behaviour you need and you remember about it - just go ahead with your own set implementation.

Upvotes: 1

codeape
codeape

Reputation: 100914

set.remove returns nothing (None).

Your code assigns the return value of set.remove to the variable mi_set. Therefore, mi_set is None.

Upvotes: 19

arul
arul

Reputation: 14094

Are you sure that the remove function returns a value?

Upvotes: 0

Related Questions