Есть ли что-то похожее на "я" внутри генератора Python?

Есть ли способ получить ссылку на возвращаемый объект генератора внутри определения генератора? Это было бы похоже на аргумент self, переданный методу внутри метода __next__ итератора. После просмотра документации Python я не нашел ничего похожего на нее.

Этот вопрос возник, когда я изучал, как много из следующих бумажных идей я могу реализовать в Python, используя генераторы в виде сопрограмм. Бумага: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.19.79

Ближайшим, что я мог сделать, было следующее, используя декоратор, который строится на декораторе Дэвида Безли 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...

EDIT: Следующий класс, основанный на ответе ниже, может использоваться в качестве декоратора, чтобы генератор получал ссылку на 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

Вот предложение с использованием прокси.

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

Угадывание: самость в документе может не означать сам генератор, а объект, какой-то государственный держатель, который, возможно, разделяется между некоторыми генераторами.

Точнее, ориентированный граф ограничений требуется без циклов, когда он рассматривается как неориентированный граф.

Это заставляет меня думать, что генератор не ссылается на его "идентичное" выполнение. Почему он должен получать свою собственную ценность из-за итерации? Он может использовать локальную переменную.

Корутинцы происходят от Симулы. Возможно, чтобы понять, что собой представляет собой, вы можете взглянуть на язык.