I can do this:
>>> x = [2,3,4]
>>> y = (v * 2 for v in x)
>>> del x # x is deleted
>>> print(list(y)) # y still exists
[4, 6, 8]
This may let me think generator y is independent from list x. But I can also do this:
>>> a = [2, 3, 4]
>>> b = (v * 2 for v in a)
>>> a.append(5) # change a
>>> print(list(b)) # b is also changed
[4, 6, 8, 10]
This makes me feel that generator b is still pointing to list a. So I am wondering how generator is actually constructed. Or maybe there is something about how x is deleted in the first case.
del
doesn't delete objects. It deletes names. Objects will still exist as long as there is any reference to them. The name x
and your generator y
both reference a single object (the list). If you do del x
you remove the name x
, but the generator still holds its reference. If you modify x
, the generator sees it, because it is referring to the same object.
Generator expressions work on the concept of lazy evaluation.
Instead of storing the entire list [4, 6, 8]
, in memory, the generator stores a definition for (x * 2 for x in <some list>)
and computes the next value only when needed.
One of the things stored in the definition is the reference to all source variables to be used in computing the expression. When x
is used in the generator expression, its reference is stored and later dereferenced on a per-need basis.
Now, doing
del x
Will only decrement the reference counter associated with this value. In both cases, there are two references (x
as well as the reference in the generator) until you delete one of them. The generator reference still exists, which is why it can be evaluated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With