2

Java's Set.add function will return a boolean value, which is true if the set did not already contain the specified element.

Python's Set.add does not have a return value.

Seems like in Python if I want to do the same thing, I have to check if it is in the set first and then add it if not.

Is there a simpler way to do that (preferably a one-liner)?

Ref:
https://docs.oracle.com/javase/7/docs/api/java/util/Set.html#add(E) https://docs.python.org/2/library/sets.html#sets.Set

dontloo
  • 10,067
  • 4
  • 29
  • 50
  • In python, if you add sometthing that's already in the set, it wont add a duplicate. – pwnsauce Jul 25 '16 at 08:52
  • 2
    @pwnsauce: it won't in Java either. But the `Set.add()` method tells you if the element was already there, which can simplify code a little. Java needs all the help it can get when it comes to code simplicity, of course. – Martijn Pieters Jul 25 '16 at 08:55

4 Answers4

4

If you want a one-liner, you could use or to add the element only if it is not already in the set (short-circuit evaluation), and not to inverse the returned value and at the same time coerce to bool, turning the None returned by add, if it is called, into True, and otherwise just inverting the result of the in check:

>>> s = set()
>>> not(42 in s or s.add(42))
True
>>> not(42 in s or s.add(42))
False
>>> s
set([42])

However, since that one-liner might not be very easy to grasp, and you have to write the value to be inserted twice, you should probably make it a function, and then it does not matter much how many lines it uses.

def in_or_add(s, x):
    return not(x in s or s.add(x))
tobias_k
  • 81,265
  • 12
  • 120
  • 179
3

No, Python's set implementation has no such method; as you noted you'll have to test for presence separately:

if obj not in setobj:
    setobj.add(obj)

or, what I usually do:

if obj in setobj:
    return  # or break out of a loop, etc.

# handle the case where the set doesn't have the object yet.

You can always subclass the set type:

class SetWithPresenceCheck(set):
    def add(self, value):
        not_present = value not in self
        super(SetWithPresenceCheck, self).add(value)
        return not_present

Note that the real reason Set.add() returns a boolean is to make adding and testing an atomic operation; implementations of the interface can (optionally) make the method synchronised and let callers avoid race conditions. Python's built-in set doesn't make any thread-safety promises anyway.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • The problem with the Python approach is it forces a double lookup for each element that you're adding in the case where you have few (if any) duplicates in the input values. It's not a very expensive lookup normally, of course, but being able to hoist that into the actual implementation so that a single lookup is done would be a nice little efficiency boost. – Donal Fellows Jun 21 '20 at 10:38
  • @DonalFellows: that cost is negligible in the vast majority of cases, but if you are worried about it, and a benchmark shows that it would actually make a difference for your use cases, then you could use `oldlen = len(self)`, `super().add(value)`, `return len(self) > oldlen` as the `add` implementation in a subclass. The set length is O(1) lookup of a cached value updated each time the number of elements changes. I don't think it'll make a difference unless the `__hash__` implementation of your elements is costly and can't be cached, or the `__eq__` implementation is costly. – Martijn Pieters Jun 21 '20 at 10:59
  • @DonalFellows: again, the primary reason that Java (and other languages, like Rust) return a bool (or the old value that was stored when updating a key-value pair in a map) is to support concurrency, not performance. Python is not a language built for speed, in any case, it is a language built for readability, maintainability, and development speed. By the time that double lookups in a set start to matter, you probably want to have moved the critical sections to a compiled language extension, anyway. – Martijn Pieters Jun 21 '20 at 11:02
-1

The union operator is much faster than add anyway.

>>> set_a = set('pqrs')
>>> set_b = ['t', 'u', 'v']
>>> set_a |= set(set_b)
>>> set_a
set(['p','q','r','s','t','u','v'])
Shravan40
  • 8,922
  • 6
  • 28
  • 48
-1

maybe with ternary conditional operator :

the_set.add(what_ever) or True if what_ever not in the_set else False

This will return False if what_ever was in the set, True if it wasn't

pwnsauce
  • 416
  • 4
  • 14
  • This is invalid syntax. Assignment (`bool_already_in=True`) is a *statement*, not an expression, and can't be nested inside another expression. – Martijn Pieters Jul 25 '16 at 09:05
  • 1
    So now you need to test for `None` or `False` in a separate statement. That's no more helpful than a separate containment check. – Martijn Pieters Jul 25 '16 at 09:06
  • @MartijnPieters yes, but you would have had to dothe same in `java : set.add()` no ? set.add() add the value if it isn't in the set and return True or False right ? Just like this – pwnsauce Jul 25 '16 at 09:08
  • Besides, do you really think that an extended conditional expression like this is more readable and clearer? – Martijn Pieters Jul 25 '16 at 09:08
  • Exactly, `Set.add()` returns a boolean value. You return either `False` (a boolean), or `None` (a false-y value). You can't use this in a `if` condition, you need to add `is None` or similar first. – Martijn Pieters Jul 25 '16 at 09:09
  • That's what I am saying; no, it is not close enough. Even if it was, it is not actually useful, as it is unreadable. – Martijn Pieters Jul 25 '16 at 09:14
  • @MartijnPieters now it's does return a real boolean... But okay it's ugly – pwnsauce Jul 25 '16 at 09:18