一个生成器是否知道自己是一个生成器函数?

3

生成器函数返回的生成器对象是否会保留对函数对象的引用?换句话说,是否可以实现magic_fn来实现这一点:

>>> def gen():
...     yield 1
...     yield 2
...     
>>> gen.attr = 'potato'
>>> g = gen()
>>> del gen
>>> next(g)
1
>>> magic_fn(g, 'attr')
'potato'

生成器有一个对代码对象(g.gi_code)、帧(g.gi_frame)和名称(g.__name__)的引用。代码对象甚至具有与gen.__code__相同的内存地址。
但是,假设它尚未被垃圾回收,我找不到访问gen.__dict__的方法。这是可能的吗,还是链接在生成器创建时就已经丢失了?
1个回答

4
生成器迭代器不指向生成器函数。您可以通过使用weakref.ref保持对函数的弱引用来查看此内容:
>>> import weakref
>>> def gen():
...     yield 1
...
>>> ref = weakref.ref(gen)
>>> gen_iter = gen()
>>> del gen
>>> ref() is None
True

与普通引用不同,weakref.ref 不会延迟其所引用对象的回收。如果被引用对象(gen)仍然存活,ref() 将返回 gen。如果被引用对象已经被回收,ref() 将返回 None。正如你所看到的,弱引用已被清除,如果 gen_iter 仍然引用 gen,这种情况是不会发生的。
类似地,你可以证明生成器迭代器不会保留对函数的 __dict__ 或任何其他引用链的引用,这些引用链将使其检索存储在函数的 __dict__ 中的项目。
>>> class Dummy(object):
...     pass
...
>>> def gen():
...     yield 1
...
>>> gen.attr = Dummy()
>>> ref = weakref.ref(gen.attr)
>>> gen_iter = gen()
>>> del gen
>>> ref() is None
True

谢谢,我还能通过在Dummy上实现一个打印'goodbye'的__del__来看到它被收集。你能否对weakref.ref的使用方式和工作原理进行更详细的解释? - wim
@wim:添加了一些关于弱引用的解释。 - user2357112

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