Reputation:
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
Reputation: 2576
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
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
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
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
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