生成器在内存中占用的空间是簿记信息。在它中,一个对frame对象的引用(对于正在运行的Python代码的管理,比如locals),不管它现在是否正在运行,对code对象的引用也会保留。没别的了:>>> x=(i for i in range(1,11))

>>> dir(x)

['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

>>> x.gi_frame

>>> x.gi_running

0

>>> x.gi_code

at 0x1051af5b0, file "", line 1>

这仅仅是3个引用,加上通常的Python对象类型信息(想想引用计数)和一个弱引用列表;所以大约有4个指针,一个整数和一个结构,在您的系统上需要40个字节(在我的64位OS X系统上,是80个字节)。sys.getsizeof()报告了在C中实现的结构的大小,它不在指针上递归。在

因此,当你运行生成器时,内存量不会改变。被引用的帧可能会改变所使用的内存量(如果生成器表达式指向一端或另一端引用大型对象),但是在生成器对象上的sys.getsizeof()的结果中,您不会看到这一点;而是查看帧局部变量:

^{pr2}$

.0对象是生成器在for循环中使用的range()迭代器,i是{}循环目标。listiterator是另一个iterable对象,它有对生成的列表range()的私有引用以及一个位置计数器,因此每次您请求它时它都可以生成下一个元素。在

您不能查询生成器的元素大小;它们根据需要生成元素,您无法先验地“知道”它们将产生多少,也不知道它们在运行后产生了多少。sys.getsizeof()当然不会告诉你;它是一个测量内存占用的工具,如果你想知道内存占用的总量,你必须递归地测量所有被引用的对象。在

您可以看到生成器已从帧完成运行;一旦运行完毕,它将被清除:>>> x.gi_frame

>>> list(x)

[2, 3, 4, 5, 6, 7, 8, 9, 10]

>>> x.gi_frame is None

True

因此,最后,生成器使用的内存驻留在框架中的结构中(局部变量,可能还有全局变量,这些名称空间中的每个对象都可能再次引用其他对象),当生成器完成时,框架被清除,生成器.gi_frame指针被修改为指向None单例,如果引用计数下降到0,则保留要清除的帧。在

所有这些只适用于生成器,而不适用于一般的iterable;生成器是Python代码,因此可以对此进行深入的反思。在