【发布时间】:2021-10-17 12:40:46
【问题描述】:
这是一个关于 Python 内部的问题。 以下代码取自this video about laziness in python:
def nats(n):
yield n
yield from nats(n + 1)
def sieve(s):
n = next(s)
yield n
yield from sieve(i for i in s if i % n != 0)
s = sieve(nats(2))
print(next(s), next(s), next(s), next(s), next(s)) # Primes: 2, 3, 5, 7, 11...
sieve 函数懒惰地生成素数(请阅读this 了解原始概念)。从概念上讲,我们将过滤器添加到“筛子”中,因此每个数字(例如 10)都针对所有先前找到的素数(例如 2、3、5 和 7)进行测试,直到找到下一个素数,即 11。11 是然后添加到过滤器的“列表”中,依此类推。
这部分(i for i in s if i % n != 0)是generator expression (or "comprehension")。
我的问题与 Python 用来“嵌套”这些生成器表达式的机制有关。例如,让我们使用两个可能的通过表达式:
第一次检查时,我们取 nats(用于自然数)并向其添加 2 过滤器。
第二次,我们使用已经“通过”nats和2过滤器的生成器,并将3过滤器添加到它。我们从[3,2,nats] 产生,从[2,nats] 产生,从[nats] 产生。关键是,很明显,每层传递都需要保留一些变量上下文,例如,每一层“看到”不同的n。
但是 Python 到底在做什么呢?以下是一些我认为可行的选择:
- 是否为每个嵌套的生成器调用添加堆栈帧?
- 是否只需要创建一个闭包对象?
- 也许 python 将生成器“压缩”成一个类似于此表达式的生成器:
i % 2 != 0 and i % 3 != 0 and i % 4 !=0?
或者我可能遗漏了一些关于这里发生的事情的基本信息?
【问题讨论】:
-
您在此处添加到堆栈。事实上,通过尝试确定第 500 个素数之类的东西很容易溢出堆栈。
标签: python closures python-internals sieve-of-eratosthenes generator-expression