发电机的大小由什么决定?

3
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个字节?

4
由于这种行为是CPython实现细节,因此添加了CPython标记。顺便说一句,在CPython 3.8和3.9中所有情况都打印112,在CPython 3.10中都打印104,在CPython 3.11中出现了给定的行为,在我的系统上在CPython 3.12中打印192x2和200x3。 - undefined
4
@Brian61354270 啊,没错,这里有两个问题。我按照标题上的那个来回答,因为它更广泛。换句话说,“发电机的大小由什么决定?具体来说,为什么下面的四个发电机使用了额外的8个字节?” - undefined
1
@henveloper 你能澄清一下你想要什么样的答案吗?你是在问为什么同一类型的物体的尺寸会有所不同,还是为什么那些具体的发电机有它们的尺寸? - undefined
3
这个问题是与具体实现相关的。生成器的概念,或者说一般的对象,是否具有大小都与实现相关。在像PyPy这样的实现中,检查sys.getsizeof这样的东西是没有意义的,因为对象可能根本不存在,或者在JIT预热期间具有不同的实际内存大小。 - undefined
看起来尺寸的差异是由与帧对象相关的发电机尺寸的变化所解释的。 - undefined
显示剩余3条评论
1个回答

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接