27

I think this may be related to set being mutable.

Basically, I can remove an element from a set using set.discard(element). However, set.discard(element) itself returns None. But I'd like to get a copy of the updated set. For example, if I have a list of sets, how can I get an updated copy conveniently using list comprehension operations?

Sample code:

test = [{'', 'a'}, {'b', ''}]
print [x.discard('') for x in test]
print test

will return

[None, None]
[set(['a']), set(['b'])]
Georgy
  • 12,464
  • 7
  • 65
  • 73
Zhen Sun
  • 817
  • 3
  • 13
  • 20
  • SOF is such an amazing place! Both @thefourtheye and @roippi answers work nicely! @roippi answer has a speed advantage and may have broader application too. I accepted @thefourtheye answer only because it works conveniently in my case, where the list of sets is generated from a list of lists. So I can code simply `[set(x) - {''} for x in list]`. But many thanks to both of you! – Zhen Sun Apr 05 '14 at 06:18
  • Should the list comprehension result in an independent list of sets, with no modification to the original sets, or should it modify the original? If you want to modify the original, it's best not to use a list comprehension at all; a `for` loop is more suitable. – user2357112 Apr 05 '14 at 06:30
  • @user2357112 I'd like to modify the original (it doesn't matter really but since the dataset is large, I don't want to create copies). What's the reason `for loop` is better than list comprehension in this case? – Zhen Sun Apr 05 '14 at 06:38
  • If you want to modify the original, then when the modifications are done, `test` will be your list of modified sets. Building another with a list comprehension would be redundant. Also, list comprehensions are generally expected not to have side effects. – user2357112 Apr 05 '14 at 06:57
  • @user2357112, I see your point. So this is only relevant if the modification occurs inplace, right? Otherwise, I still need to assign the values back to `test` in a `for loop` (`for x in test: func(x)` does not modify `test`); while in list comprehension one can conveniently use `test = [func(x) for x in test]`. – Zhen Sun Apr 05 '14 at 07:19
  • If `func` doesn't modify its argument, you still need to assign the values back or put them in a new list. A list comprehension allows you to do `test = [func(x) for x in test]`, though depending on the situation, reusing the `test` variable may or may not be advisable. – user2357112 Apr 05 '14 at 07:27

3 Answers3

16

Whenever you feel constrained by a method that only works in-place, you can use the behavior of or/and to achieve the semantics that you want.

[x.discard('') or x for x in test]

This technique is occasionally useful for achieving things in a lambda (or other situations where you are restricted to a single expression) that are otherwise impossible. Whether it's the most "readable" or "pythonic" is debatable :-)

roippi
  • 25,533
  • 4
  • 48
  • 73
  • 6
    Be careful not to put `x` on the left side in this case or the `or` expression will short-circuit and the mutation will never occur. `[x or x.discard('') for x in test] == [{'', 'a'}, {'', 'b'}]` – machine yearning Apr 05 '14 at 06:27
14

You can use set difference operator, like this

test, empty = [{'', 'a'}, {'b', ''}], {''}
print [x - empty for x in test]
# [set(['a']), set(['b'])]
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
4
>>> s = set( ['a' , 'b', 'c' , 'd' ] )
>>> print(s)
set(['a', 'c', 'b', 'd'])
>>>
>>> s -= {'c'}
>>> print(s)
set(['a', 'b', 'd'])
>>>
>>> s -= {'a'}
>>> print(s)
set(['b', 'd'])
jxramos
  • 7,356
  • 6
  • 57
  • 105