It's not just that single line of code, it's that line being run repeatedly, with different values of n
.
Basically, it
is an iterator that yields candidate prime numbers which have not yet been ruled out by the sieve. You start by making all odd numbers candidates.
it = _odd_iter()
Then you repeatedly take the first remaining candidate,
while True: n = next(it)
remove all numbers that are multiples of that candidate,
filter(_not_divisible(n), it)
and replace your candidate primes with everything that is left after removing multiples.
it = ...
If you pretend filter
returns a list of numbers, rather than an iterable, and also pretend _odd_iter()
returns a list of odd numbers instead of an iterable, you can trace through the loop and determine what's in the list at each point. For example, after running
it = _odd_iter()
you start with
it = 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, ...
Then run
n = next(it) # 3
which pulls the first item off the front, leaving you with
it = 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, ...
and run
it = filter(_not_divisible(3), it)
filter out all the multiples of 3,
it = 5, 7, 11, 13, 17, 19, 23, 25, ...
Then go back to the top of the loop and pull the new first number off the front
n = next(it) # 5
leaving
it = 7, 11, 13, 17, 19, 23, 25, ...
and then filter out all the multiples of 5,
it = filter(_not_divisible(5), it)
which gives
it = 7, 11, 13, 17, 19, 23, ...
and so on.
In practice, because filter()
returns an iterator, not a list, you wind up getting a nested sequence of iterators. In particular, you start with
it = _odd_iter()
then after the first iteration of the loop, you have basically
it = filter(_non_divisible(3), _odd_iter())
except that 3
has been taken from the iterator, and then after the second iteration of the loop you have
it = filter(_non_divisible(5), filter(_non_divisible(3), _odd_iter()))
except that 5
has also been taken from the iterator, and then
it = filter(_non_divisible(7), filter(_non_divisible(5), filter(_non_divisible(3), _odd_iter())))
and so on.