什么决定了发电机的尺寸?

What determines the size of a generator?

提问人:henveloper 提问时间:11/5/2023 最后编辑:wjandreahenveloper 更新时间:11/5/2023 访问量:84

问:

import sys

num_gen = (num for num in range(1))
print(sys.getsizeof(num_gen))
num_gen = (num for num in range(100))
print(sys.getsizeof(num_gen))

num_gen = (num+1 for num in range(100))
print(sys.getsizeof(num_gen))
num_gen = (num+1+1 for num in range(100))
print(sys.getsizeof(num_gen))
num_gen = (num+num for num in range(100))
print(sys.getsizeof(num_gen))
num_gen = (num+num+1 for num in range(100))
print(sys.getsizeof(num_gen))

输出

200
200
208
208
208
208

为什么下面的 4 个生成器多用了 8 个字节?

python 生成器

评论

4赞 Brian61354270 11/5/2023
添加了 CPython 标记,因为此行为是 CPython 实现细节。FWIW,所有情况都打印在 CPython 3.8 和 3.9 中,全部打印在 CPython 3.10 中,在我的系统上打印 CPython 3.11 中的给定行为,以及 CPython 3.12 中的 x2 x3。112104192200
0赞 wjandrea 11/5/2023
@Brian61354270 问题本身不是特定于实现的,即使特定行为是。
4赞 wjandrea 11/5/2023
@Brian61354270啊,没错,这里有两个问题。那么我就用标题中的那个,因为它更广泛。换句话说,“什么决定了发电机的大小?具体来说,为什么下面的 4 个生成器多用了 8 个字节?
1赞 Brian61354270 11/5/2023
@henveloper 你能澄清一下你在寻找什么样的答案吗?你是在问为什么通常相同类型的对象的大小会有所不同,或者为什么这些特定的生成器具有它们的大小?
3赞 MisterMiyagi 11/5/2023
这个问题是特定于实现的。生成器(或一般对象)的概念本身,甚至具有大小都与实现有关。在像 PyPy 这样的实现中检查类似的东西是没有意义的,因为在 PyPy 中,对象可能根本不存在,或者根据 JIT 预热具有不同的实际内存大小。sys.getsizeof

答:

1赞 chepner 11/5/2023 #1

生成器对象的“大小”不是它将生成的元素(如列表或其他类型的序列)的函数,而是它执行以生成元素的代码的函数。

评论

2赞 Jeff Mercado 11/5/2023
还可能发现在向表达式正文添加更多代码时需要更多代码的阈值,并填充以进行对齐。表达式正文中的附加 和 将需要额外的指令,并且对于某些版本来说,可能需要额外的开销。+1+num
0赞 Brian61354270 11/5/2023
@JeffMercado AFAIK,生成器对象的大小与其代码对象的大小无关。生成器本身只需要存储一个指向它的指针。如果查看问题中的每个生成器,您可以看到代码对象大小在报告相同 .如果你看一下,你可以看到指令的数量变化比可以解释的要多。num_gen.gi_codesys.getsizeof(num_size)len(g.gi_code.co_code)sys.getsizeof(num_gen)
0赞 Jeff Mercado 11/5/2023
@Brian61354270嗯,这似乎是我系统上表达式复杂性的函数。3.11.5 x64 i.stack.imgur.com/njeFA.png 如果你看一下拆卸,可能会计算出它的数学。
0赞 Brian61354270 11/5/2023
@JeffMercado我可以复制这些结果。但有趣的是,导致差异的不是代码对象大小。如果你创建一个像 这样的大体,即使代码对象有几千字节,你仍然会得到。如果以不影响指令数的方式在括号周围移动,则大小也会发生变化。而这只发生在 CPython 3.11+ 中。CPython 3.{8.9.10} 无论主体复杂程度如何,都不会看到生成器对象的大小有任何变化。AA+AB+AC+...+ZY+ZZ for _ in range(l)sys.getsizeof(num_gen) == 200
0赞 Brian61354270 11/5/2023
我猜这是 CPython 3.11/12 引入的改进异常回溯的产物。生成器可能需要存储有关括号/子表达式布局的额外信息,以便知道在发生异常时用 s 下划线的正文的哪个部分。^