Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Accessing list as it is comprehended

Is there a way to access the list as it is comprehended? In particular I'd like to iterate once again over elements already added.

for example, im looking for something like this:

[x for x in range(foo) if x not in self]

or

[x for x in range(foo) if any(y for y in self)]

where self would be the just-comprehended list itself.

like image 610
Slimior Avatar asked Jul 28 '26 07:07

Slimior


2 Answers

In short: No. You cannot do this. Python's list comprehensions (and related constructs like generator expressions) are not as powerful as Haskell's lazy lists (which could do what you want). The syntax is similar, but Python is not pure a functional language with lazy, recursive evaluation as a syntax feature; its interpreter isn't sophisticated enough to understand and handle behaviors like this.

More complicated answer: You could hack your way around it in specific cases by misusing list comprehensions (e.g. the set updating hack used in acushner's answer). But list comprehensions should generally strive to be transformations and filters of existing data; they should not perform other work by side-effect, because it makes the code much more complicated, confusing maintainers. List comprehensions are a functional pattern; side-effects are distinctly non-functional, so people don't look for or expect them. If you need to do something like this, it's a much better idea to write a function (possibly a generator function) that can isolate the complexity and maintain internal state that tracks the values produced so far. The various uniquifying recipes provided in the itertools recipes are a good example.

Getting rather advanced: Haskell can do this because its list comprehensions can be lazy, yet reusable (it has memory). So Haskell can have its cake and eat it too; a list can define elements in terms of previous elements, because those elements will be generated if needed, and can be read more than once. Python separates laziness from reusability. A list comprehension is evaluated eagerly; it finishes being populated before it's actually bound to a name you could use to access it (so it can't read values from the intermediate list as its being built). A generator expression is mostly lazy (it binds the iterable it's working on eagerly, but everything else is lazy) so by the time it's evaluated, it could be bound to a name, but it has no memory; you can't use values from the genexpr inside the genexpr because doing so would necessarily advance the generator (and in most cases I can think of, lead to infinite recursion as it tried to repeatedly define the next result in terms of the next result, over and over).

like image 174
ShadowRanger Avatar answered Jul 30 '26 20:07

ShadowRanger


first, any(y for y in self) is just any(self).

second, your function won't do anything. what you're effectively writing is:

res = []
for x in range(foo):
    if any(res):
        res.append(x)

which is just an empty list.

what you want is a set.

res = set(values)

if you care about order, you can do something like:

seen = set()
[x for x in values if x not in seen and not seen.add(x)]
like image 21
acushner Avatar answered Jul 30 '26 20:07

acushner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!