在Python生成器中是否有类似“self”的东西?

时间:2014-02-16 06:32:45

标签: python coroutine

有没有办法在生成器的定义中获取对返回的生成器对象的引用?这类似于传递给迭代器的self方法内的方法的__next__参数。浏览Python的文档后,我没有找到类似的东西。

这个问题出现的时候我正在探索以下论文的大部分内容,我可以使用生成器作为协同程序在Python中实现。论文:http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.19.79

我能做的最接近的是使用装饰器,它基于David Beazley的coroutine装饰器,但感觉有点像黑客。

from functools import wraps


def coroutine(func):
    @wraps(func)
    def decorated(*args, **kwargs):
        f = func(*args, **kwargs)
        next(f)
        f.send(f)
        return f
    return decorated


@coroutine
def A():
    self = yield
    # do stuff...

编辑:基于下面的答案,下面的类可以用作装饰器,让生成器接收对self的引用作为其第一个参数。它具有额外的好处,任何用它装饰的生成器都将具有类型coroutine

class coroutine(object):
    """Decorator class for coroutines with a self parameter."""
    def __new__(cls, func):
        @wraps(func)
        def decorated(*args, **kwargs):
            o = object.__new__(cls)
            o.__init__(func, args, kwargs)
            return o
        return decorated

    def __init__(self, generator, args, kw):
        self.generator = generator(self, *args, **kw)
        next(self.generator)

    def __iter__(self):
        return self

    def __next__(self):
        return next(self.generator)

    next = __next__

    def send(self, value):
        return self.generator.send(value)


# Usage:

@coroutine
def A(self):
    while True:
        message = yield
        print self is message


a = A()
b = A()
a.send(a)  # outputs True
a.send(b)  # outputs False

1 个答案:

答案 0 :(得分:3)

以下是使用代理的建议。

def selfDecorator(func):
    def wrap(*args, **kw):
        return SelfGenerator(func, args, kw)
    return wrap

class SelfGenerator(object):
    """This class implements the generator interface"""
    def __init__(self, generator, args, kw):
        self.generator = generator(self, *args, **kw)
    def __iter__(self):
        return self
    def __next__(self):
        return next(self.generator)
    next = __next__
    def send(self, value):
        return self.generator.send(value)

@selfDecorator
def gen(self, x): # your generator function with self
    for i in range(x):
        yield self


for x in gen(5):
    print x # prints <__main__.SelfGenerator object at 0x02BB16D0>

由于SelfGenerator是原始生成器的代理,因此它具有相同的接口,可以完全用作Pythons自己的生成器。

第一个答案

您无法自行调用生成器:

>>> def generator():
    for value in g:
        yield value


>>> g = generator()
>>> next(g)

Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    next(g)
  File "<pyshell#11>", line 2, in generator
    for value in g:
ValueError: generator already executing

猜测:论文中的自我可能并不意味着生成器本身而是一个对象,一些状态持有者可能在某些生成器之间共享。

  

更确切地说,当被视为无向图时,约束的有向图需要无周期。

这让我觉得生成器没有引用它的“相同”执行。为什么它应该通过迭代来获得自己的价值?它可以使用局部变量。

协同程序源自Simula。也许要了解自我是什么,你可以看看语言。