Comportamiento extraño de iterador de Python

Estaba jugando con los generadores de Python y el módulo de itertools e intenté hacer una versión infinita del Tamiz de Eratóstenes. Aquí está mi código:

 from itertools import count, ifilter, islice def sieve_broken(): candidates = count(start=2) while True: prime = next(candidates) yield prime candidates = ifilter(lambda n: n % prime, candidates) 

Cuando lo pruebo, me sale esto:

 >>> print list(islice(sieve_broken(), 10)) [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 

Pero si sustituyo la reasignación de candidates con una función como esta:

 def sieve_fixed(): def exclude_multiples(factor, numbers): return ifilter(lambda n: n % factor, numbers) candidates = count(start=2) while True: prime = next(candidates) yield prime candidates = exclude_multiples(prime, candidates) 

Yo obtengo:

 >>> print list(islice(sieve_fixed(), 10)) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 

No puedo entender por qué la primera versión no funciona. Por lo que puedo decir, las dos versiones deben ser equivalentes. ¿Alguien sabe por qué no son lo mismo?

Has caído en una trampa muy común cuando usas cierres en Python : los cierres tienen su scope, y sigues reemplazando el valor en el mismo scope.

 candidates = ifilter(lambda n, prime=prime: n % prime, candidates)