0

I'd have assumed the results of purge and purge2 would be the same in the following code (remove duplicate elements, keeping the first occurrences and their order):

def purge(a):
    l = []
    return (l := [x for x in a if x not in l])
def purge2(a):
    d = {}
    return list(d := {x: None for x in a if x not in d})
t = [2,5,3,7,2,6,2,5,2,1,7]
print(purge(t), purge2(t))

But it looks like with dict comprehensions, unlike with lists, the value of d is built incrementally. Is this what's actually happening? Do I correctly infer the semantics of dict comprehensions from this sample code and their difference from list comprehensions? Does it work only with comprehensions, or also with other right-hand sides referring to the dictionary being assigned to (e.g. comprehensions nested inside other expressions, something involving iterators, comprehensions of types other than dict)? Where is it specified and full semantics can be consulted? Or is it just an undocumented behaviour of the implementation, not to be relied upon?

ByteEater
  • 885
  • 4
  • 13
  • 2
    A dictionary can't have duplicate keys. It's just ignoring the duplicates that you're trying to add, the `if` has no effect. – Barmar Mar 11 '21 at 00:22
  • 2
    You get the same result with `return list({x: None for x in a})` – Barmar Mar 11 '21 at 00:23
  • Oh, now that you mention it, it's so obvious. Thanks! Care to make it an answer, so I'll accept? – ByteEater Mar 11 '21 at 00:28
  • I took it from [an answer to another question](https://stackoverflow.com/questions/89178/in-python-what-is-the-fastest-algorithm-for-removing-duplicates-from-a-list-so/25958705#25958705) and simplified. Which made me not notice the possibility of further simplification. – ByteEater Mar 11 '21 at 00:31
  • 1
    Moreover, the value of d is not updated during the comprehensions. To achieve an incremental, usually you want to write explictly a for loop, or use a lambda that captures d in its scope. – Bing Wang Mar 11 '21 at 00:31

1 Answers1

2

There's nothing "incremental" going on here. The walrus operator doesn't assign to the variable until the dictionary comprehension completes. if x not in d is referring to the original empty dictionary, not the dictionary that you're building with the comprehension, just as the version with the list comprehension is referring to the original l.

The reason the duplicates are filtered out is simply because dictionary keys are always unique. Trying to create a duplicate key simply ignores the second one. It's the same as if you'd written:

return {2: None, 2: None}

you'll just get {2: None}.

So your function can be simplified to

def purge2(a):
    return list({x: None for x in a})
Barmar
  • 741,623
  • 53
  • 500
  • 612