Python generators; two apparently identical programs work differently
Asked Answered
G

1

6

The program below [Python 3.4] is a simple Eratosthenes sieve:

from itertools import *
def excl(ns,pr):
    return (i for i in ns if i%pr)
def sieve(ns):
    while True:
        pr=next(ns)
        yield pr
        ns=excl(ns,pr)
        # ns=(i for i in ns if i%pr)
r=list(islice(sieve(count(2)),10))

which produces [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]. OK. Uncommenting the line which inlines excl(), and commenting the call, gives [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]. Why?

Is it related to troubles expected when modyfing a sequence inside a loop which iterates over it?

Thank you for any hint.

Gnosticize answered 17/10, 2015 at 20:14 Comment(0)
O
2

Your problem is that the pr referred to by the generator expression is the same pr that you modify in the next iteration of your while loop, so every number that is not divisible by the previous 'prime' number is treated as 'prime'. Which itself modifies pr and so in. In the excl function, the pr that you refer to is the one passed as an argument, which never changes.

Opalopalesce answered 17/10, 2015 at 20:33 Comment(2)
I am not sure that I understand this answer. The prime pr doesn't change in excl (nor in the gen.expression), it changes within the loop, and this context is identical for the inlined and 'call' version. BTW I removed an erroneous remark about filter().Gnosticize
@JerzyKarczmarczuk According to PEP 227, if a name is used within a code block(nested function), but it is not bound there and is not declared global, the use is treated as a reference to the nearest enclosing function region. As it applies to your case, the generator expression is a function under the hood, and it does not define the variable pr, so its pr is a reference to the pr in the enclosing function (sieve). Which means that when the pr in sieve changes, so does the pr in the generator expression.Opalopalesce

© 2022 - 2024 — McMap. All rights reserved.