Is there any way to get a reference to the returned generator object, inside the generator's definition? This would be akin to the self
argument passed to the method inside the __next__
method of an iterator. After browsing Python's documentation, I have not found anything similar to it.
This question arose while I was exploring how of much of the following paper's ideas I can implement in Python using generators as coroutines. Paper: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.19.79
The closest I could do was the following, using a decorator, which builds on David Beazley's coroutine
decorator, but it feels like a bit of a hack.
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: The following class, based on the answer below, can be used as a decorator to have the generator receive a reference to self
as its first paramter. It has the added benefit that any generator decorated with it will have the type 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
A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.
Python Generator functions allow you to declare a function that behaves likes an iterator, allowing programmers to make an iterator in a fast, easy, and clean way. An iterator is an object that can be iterated or looped upon. It is used to abstract a container of data to make it behave like an iterable object.
You can carry out the unpacking procedure for all kinds of iterables like lists, tuples, strings, iterators and generators. There are 2 ways to unpack iterables in Python. For known length iterables - Providing the exact number of variables to unpack as the number of elements in the sequence/iterable.
In Python, yield is a keyword that turns a function into a generator. Unlike a list, a generator does not store values. Instead, it knows the current value and how to get the next one. This makes a generator memory-efficient.
Here is an suggestion using a proxy.
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>
Since SelfGenerator
is a proxy to the original generator it has the same interface and can be used totally as the Pythons own generator.
First Answer
You can not call a generator in itself:
>>> 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
Guessing: The self in the paper may not mean the generator itself but an object, some state holder that maybe is shared between some generators.
More precisely, the directed graph of constraints is required to be cycle-free when it is regarded as an undirected graph.
This makes me think that the generator does not refer to its 'identical' execution. Why should it get its own value out of itself by iterating? It can use a local variable.
Coroutines originate from Simula. Maybe to understand what the self is you can have a look at the language.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With