4

To my understanding, adding elements to a set does not return anything. eg:

>>> s=set([1,2,3])
>>> s.add(1)
>>> b = s.add(1)
>>> b
>>> b = s.add(5)
>>> b
>>> print b
None
>>> b = s.add(5)
>>> print b
None

but I am confused how this function to remove duplicates of a list while preserving order works:

def f7(seq):
    seen = set()
    seen_add = seen.add
    return [ x for x in seq if x not in seen and not seen_add(x)]

not seen_add(x) return true always irrespective of if I add duplicates or not. The only real check is if x not in seen, since if that fails, it will not perform not see_add(x).

Is my understanding here or am I missing something?

however, here, not seen_add(1) return true if I add duplicate or a unique element. This is puzzling, so is the not here just do an empty check? but seen_add() is not returning a set to do an empty check. and why seen_add(2) does not return anything but not seen_add(1) returns the boolean True

>>> seen=set()
>>> seen_add=seen.add
>>> seen_add(1)
>>> seen
set([1])
>>> seen.add(2)
>>> seen
set([1, 2])
>>> not seen_add(1)
True
>>> not seen_add(4)
True
Community
  • 1
  • 1
eagertoLearn
  • 9,772
  • 23
  • 80
  • 122

4 Answers4

4

As you have discovered, seen_add(x) always returns None.

Now, not None is True:

>>> not None
True

This is explained in the documentation:

5.1. Truth Value Testing

Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:

  • None
  • False
  • ...

Since not seen_add(x) is always True, it has no effect on the result of the if. It is only used for the side effect of adding x to seen.

Put another way, the following:

seen_add = seen.add
return [ x for x in seq if x not in seen and not seen_add(x)]

is a shorter (and likely more performant) way to write:

result = []
for x in seq:
  if x not in seen:
    result.append(x)
    seen.add(x)
return result
Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • while this works, is this right way. because the `not seen_add(x)` is always `True`, then why check it on `if` – eagertoLearn Jan 02 '14 at 20:33
  • 1
    @eagertoLearn: Because you can't put statements inside a list comprehension, only expressions. – NPE Jan 02 '14 at 20:34
  • Perhaps you should explain how boolean tests shortcut. – Steven Rumbalski Jan 02 '14 at 20:35
  • @StevenRumbalski: I was going to, but then decided it was not relevant here (since calling `seen_add(x)` is a no-op if `x` is already in `seen`). I have a feeling that trying to explain short-circuiting would muddy the waters even further... – NPE Jan 02 '14 at 20:40
  • @NPE: Thanks, I do know about `short-circuiting`. I was just confused as to why something that always return `True` is part of the `if`. but it is simply because `list comprehension takes expressions` as you pointed out. and pointing out that `not None` is `True` which is not same in Java, that I am familiar with – eagertoLearn Jan 02 '14 at 20:48
3

As said by the others, not seen_add(x) always evaluates to True, so it is used only as a hack to add the addition statement into the list comprehension. Since the logic value of x and True is equal to x, this does not change the behavior of the if.

Another thing that this does, due to the short-circuit behavior of and, is that the right-hand argument to and is only executed if the left-hand argument is True. So the call to seen.add() is only done when the element is not yet in the set.

The equivalent python code would be:

def f7(seq):
    seen = set()
    result = []
    for x in seq:
        if x not in seen:
            result.append(x)
            seen.add(x)
    return result

The solution with the list comprehension might be faster, but it is too cryptic to my taste.

Bas Swinckels
  • 18,095
  • 3
  • 45
  • 62
1

This is because not None is True.

Hyperboreus
  • 31,997
  • 9
  • 47
  • 87
0

maybe this can help you

def add_ok(set, value):
    return len(set) != (set.add(value), len(set))[1]


print(add_ok(a, 1))
print(add_ok(a, 1))