Search code examples
pythonlist-comprehension

How to address current state of list comprehension in its if condition?


I would like to turn the loop over the items in L in following code:

L = [1,2,5,2,1,1,3,4]
L_unique = []
for item in L:
    if item not in L_unique: 
        L_unique.append(item)

to a list comprehension like:

L_unique = [ item for item in L if item not in ???self??? ]

Is this possible in Python? And if it is possible, how can it be done?


Solution

  • It's possible. Here's a hack that does it, but I wouldn't use this in practice as it's nasty and depends on implementation details that might change and I believe it's not thread-safe, either. Just to demonstrate that it's possible.

    You're mostly right with your "somewhere must exist an object storing the current state of the comprehension" (although it doesn't necessarily have to be a Python list object, Python could store the elements some other way and create the list object only afterwards).

    We can find the new list object in the objects tracked by garbage collection. Collect the IDs of lists before the comprehension's list is created, then look again and take the one that wasn't there before.

    Demo:

    import gc
    
    L = [1,2,5,2,1,1,3,4]
    
    L_unique = [
        item
    
        # the hack to get self
        for ids in ({id(o) for o in gc.get_objects() if type(o) is list},)
        for self in (o for o in gc.get_objects() if type(o) is list and id(o) not in ids)
    
        for item in L
        if item not in self
    ]
    
    print(L_unique)
    

    Output (Attempt This Online!):

    [1, 2, 5, 3, 4]
    

    Tested and worked in several versions from Python 3.7 to Python 3.11.

    For an alternative with the exact style you asked, only replacing your ???self???, see Mechanic Pig's updated answer.