4

So here a trivial example that is probably better executed some other way.

Here is the regular for-loop version:

lst1 = ['abc', 'abc', 'cde', 'cde']
lst2 = []
for i in lst1:
    if i not in lst2:
        lst2.append(i)

And the non-working list comprehension approximation:

lst2 = [i for i in lst1 if i not in lst2]
# NameError: name 'lst2' is not defined

So the question: is it possible to access the list being produced by a list comprehension as is it is being made?

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
anthropomo
  • 4,100
  • 1
  • 24
  • 39

4 Answers4

4

No.

But if order is important, you want this answer from another question:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(['abc', 'abc', 'cde', 'cde']))
['abc', 'cde']
Community
  • 1
  • 1
mhlester
  • 22,781
  • 10
  • 52
  • 75
2

No, that is not possible. But you can do something like this:

>>> seen = set()
>>> [x for x in lst1 if x not in seen and not seen.add(x)]
['abc', 'cde']

If order doesn't matter, then simply use set(lst1).

Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
2

TL;DR: There's no easy way to do a recursive list comprehension.

Why? It's because when the interpreter reachs this line, it'll first evaluate the right side of the operation (the list comprehension) and try to construct the list. After the construction it'll affect the list created to lst2. BUT, when you're trying to construct the list, you're calling lst2 which isn't defined yet.

You can look at the bytecode generated:

>>> def test(lst1):
...     lst2 = [i for i in lst1 if i not in lst2]
...
>>> dis.dis(test)
  2           0 BUILD_LIST               0
              3 LOAD_FAST                0 (lst1)
              6 GET_ITER
        >>    7 FOR_ITER                24 (to 34)    # the list comprehension is converted into a for loop
             10 STORE_FAST               1 (i)
             13 LOAD_FAST                1 (i)
             16 LOAD_FAST                2 (lst2)     # try to load lst2, which doesn't exist yet
             19 COMPARE_OP               7 (not in)
             22 POP_JUMP_IF_FALSE        7
             25 LOAD_FAST                1 (i)
             28 LIST_APPEND              2
             31 JUMP_ABSOLUTE            7
        >>   34 STORE_FAST               2 (lst2)
             37 LOAD_CONST               0 (None)
             40 RETURN_VALUE

Solution: What you want to do is to define a set:

>>> lst1 = ['abc', 'abc', 'cde', 'cde']
>>> set(lst1)
set(['cde', 'abc'])

(I hope you doesn't matter about the order of elements :-) ) If the order matters:

>>> tmp = set()  # create a set of already added elements
>>> [x for x in lst1 if x not in tmp and not tmp.add(x)]
['abc', 'cde']
Maxime Lorant
  • 34,607
  • 19
  • 87
  • 97
  • So this is probably my best way to do what I want to do: `kinds = set([f[:f.find('.')] for f in files])` Translates to: do some operation that creates non-unique members, and make that a set. Thanks. – anthropomo Jan 22 '14 at 19:42
  • If you want to get unique names of your files without the extension, yep, I guess. – Maxime Lorant Jan 22 '14 at 19:44
  • The "solution" part of the answer is Awesome ! – 2020 Jul 24 '19 at 19:19
1

No, inside the list comprehension lst2 is [] until it returns, try this:

print id(lst2)
lst2 = [ (i,id(lst2)) for i in lst1 if i not in lst2]
print lst2
print id(lst2)
Alvaro Fuentes
  • 16,937
  • 4
  • 56
  • 68