首页 > 解决方案 > Python:生成器和过滤器如何在使用 filter() 生成素数列表的代码中工作

问题描述

注意:这个问题与在 python 中使用过滤器和生成器生成无限素数不同,尽管它们都与 Python 代码查找所有素数达到给定限制有关。

核心代码其实很简单,但我很难理解它是如何工作的。这就是我添加一些调试打印的原因。

def _odd_number_generator():
    x = 1
    while True:
        x += 2
        print('    _odd_number_generator, x=', x)
        yield x

def _not_divisible(n):
    def func(x):
        print("      filter calling on x:", x, ", n:", n)
        return x % n > 0
    return func


def _primes():
    yield 2                                 # return first prime: 2
    odd_numbers = _odd_number_generator()   
    print("  in _primes, #a:  odd_numbers=", odd_numbers)
    while True:
        print("  in _primes, #b:         before next(filter) odd_numbers=", odd_numbers)

        # I know this line calling _odd_number_generator and _not_divisible, 
        # but how it works
        n = next(odd_numbers)   

        print("  in _primes, #c:         begin yield n:", n)
        yield n
        print("  in _primes, #d: n=", n, ", after yield  odd_numbers=", odd_numbers)
        odd_numbers = filter(_not_divisible(n), odd_numbers)    
        print("  in _primes, #e: n=", n, ", after filter odd_numbers=", odd_numbers)


def print_prime_numbes():
    for n in _primes():                                          
        print("  in print_prime_numbes, n = ", n)
        if n < 30:
            print(n)
            print()
            print("print_prime_numbes, begin next loop: n=", n)
        else:
            break

def test_print_prime_numbes():
    print("test_output_triangles() >>>")
    print_prime_numbes()

在python中使用过滤器和生成器生成无限素数的答案对于理解链式迭代器非常有帮助。但是,我仍然难以理解在处理数字 25 时如何调用 _odd_number_generator 和 _not_divisible。

例如,这是运行时的一段输出:

print_prime_numbes, begin next loop: n= 23
  in _primes, #d: n= 23 , after yield  odd_numbers= <filter object at 0x000002B0E02366D8>
  in _primes, #e: n= 23 , after filter odd_numbers= <filter object at 0x000002B0E0236F98>
  in _primes, #b:         before next(filter) odd_numbers= <filter object at 0x000002B0E0236F98>
    _odd_number_generator, x= 25
      filter calling on x: 25 , n: 3
      filter calling on x: 25 , n: 5
    _odd_number_generator, x= 27
      filter calling on x: 27 , n: 3
    _odd_number_generator, x= 29
      filter calling on x: 29 , n: 3
      filter calling on x: 29 , n: 5
      filter calling on x: 29 , n: 7
      filter calling on x: 29 , n: 11
      filter calling on x: 29 , n: 13
      filter calling on x: 29 , n: 17
      filter calling on x: 29 , n: 19
      filter calling on x: 29 , n: 23
  in _primes, #c:         begin yield n: 29
  in print_prime_numbes, n =  29
29

在这里,因为 25 是可整除的,所以正在生成下一个数字 27。我想知道是什么让调用生成 27?

[已编辑]

在 yield 23 之后,进入下一个循环,奇数应该是这样的: filter(_not_divisible(23),filter(_not_divisible(19) ...filter(_not_divisible(7),filter(_not_divisible(5),filter( _not_divisible(5),过滤器(_not_divisible(3),_odd_generator()))。

运行“yield n”时,正在生成下一个数字 25,并检查可除性,_not_divisible 返回 False。在这里,看起来'yield'将在 _odd_generator() 上运行,并检查新数字是否可以除以 3,4,5,..23 直到它得到一个素数。但我想详细了解这里的机制。

标签: pythongeneratorprimesiterable-unpacking

解决方案


为了更好地理解,我们也可以将filter其视为生成器:

def filter(condition, iterable):
    for value in iterable:      # 1. consume values from `iterable`
        if condition(value):    # 2. test `condition` for `value`
            yield value         # 3. yield any valid `value`

换句话说,odd_numbers = filter(_not_divisible(n), odd_numbers)是一个生成器 ( filter) 包装了另一个生成器 ( _odd_number_generator)。对于每个质数,一个新filter的包裹在现有的包裹过滤器周围。看看最初的案例之一,我们有这样的设置:

odd_numbers = filter(_not_divisible(n=7),  # <filter A>
    filter(_not_divisible(n=5),            # <filter B>
        filter(_not_divisible(n=3),        # <filter C>
            _odd_number_generator()        # <odd_numbers @ x=7>
))

现在,如果我们调用 会发生什么next(odd_numbers)

  • <filter A>:1value通过调用获取 anext(<filter B>)
    • <filter B>:1value通过调用获取 anext(<filter C>)
      • <filter C>:1value通过调用获取 anext(<odd_numbers @ x=7>)
        • <odd_numbers @ x=7>递增x+=2x=9并产生它
      • <filter C>:2测试_not_divisible(n=3)(9)发现无效
      • <filter C>:3跳过并且循环继续
      • <filter C>:1value通过调用获取 anext(<odd_numbers @ x=9>)
        • <odd_numbers @ x=9>递增x+=2x=11并产生它
      • <filter C>:2测试_not_divisible(n=3)(11)并发现它有效
      • <filter C>:3产量 11
    • <filter B>:2测试_not_divisible(n=5)(11)并发现它有效
    • <filter B>:3产量 11
  • <filter A>:2测试_not_divisible(n=7)(11)并发现它有效
  • <filter A>:3产量 11

重要的部分是_not_divisible(n=3)不要让值 9 通过。相反,循环 in<filter C>获取另一个值而不屈服于<filter B>and <filter A>

随着越来越多filter(_not_divibible(n), ...)的层被包裹起来_odd_number_generator(),还有额外的层可以“跳过yield并请求新value”。中间生成器在产生之前可以消耗多个值的一般原则保持不变。


推荐阅读