在Python生成器中访问局部变量

6 投票
3 回答
2788 浏览
提问于 2025-04-16 10:15

你怎么能从外部访问一个在Python生成器内部定义的局部变量呢?

我有一个情况,我的生成器在处理一些局部状态,而在单元测试中,我想检查这个状态,以确保它包含正确的值。

我不能把状态存储到实例变量里(比如 self.state = blah),因为我可能会从同一个类实例创建多个生成器,这样的话,生成器之间可能会互相覆盖状态。我也不能在 yield 表达式中返回状态,因为状态的名称可能会变化,或者因为每个生成器实例的不同而有所不同。

例如,我想做类似这样的事情(不过这段代码是不能工作的)

from random import random

class MyIter(object):
    def __iter__(self):
        context = {}
        for i in xrange(10):
            context[random()] = random()
            yield i

obj = MyIter()
i1 = iter(obj)
i2 = iter(obj)
while 1:
    try:
        i1.next()
        i2.next()
        print i1.context
        print i2.context
    except StopIteration:
        break

有没有办法通过检查Python的执行栈来访问局部变量呢?

3 个回答

0

如果你真的想这么做,可以把迭代器类和容器类分开:

from random import random

class MyContainer(object):
    def __iter__(self):
        return MyIter(self)

class MyIter(object):
    def __init__(self, container):
        self.container = container
        self.context = {}
        self.it = iter(xrange(10))
    def next(self):
        self.context[random()] = random()
        return next(self.it)
    def __iter__(self):
        return self

obj = MyContainer()
# ...

不过我觉得这样做并不是特别有用...

1

你应该把生成器当作一个黑箱子来看待。单元测试不应该关注它内部的状态,因为那只是实现的细节;它们只需要关注预期的行为就可以了。

4

抱歉我自己回答自己的问题,不过在深入研究生成器接口后,我找到了访问生成器本地变量的确切路径:

from random import random

class MyIter(object):
    def __iter__(self):
        context = {}
        for i in xrange(10):
            context[random()] = random()
            yield i

obj = MyIter()
i1 = iter(obj)
i2 = iter(obj)
while 1:
    try:
        i1.next()
        i2.next()
        print i1.gi_frame.f_locals['context']
        print i2.gi_frame.f_locals['context']
    except StopIteration:
        break

撰写回答