-1

when we look at the Python documentation we could see that generators are always defined using yield statement, but in the Internet we could see that some people are trying to implement generators using classes (eg. here How to write a generator class?).

Here is example generator implementation using classes:

from collections import Generator
class Fib(Generator):
    def __init__(self):
        self.a, self.b = 0, 1        
    def send(self, ignored_arg):
        return_value = self.a
        self.a, self.b = self.b, self.a+self.b
        return return_value
    def throw(self, type=None, value=None, traceback=None):
        raise StopIteration

When we execute it in repl we can see it is not the generator, but ordinary object. It only tries to behave like generator.

>>> x = Fib()
>>> x
<__main__.Fib object at 0x7f05a61eab70>

When we look at PEP 342:

  1. Add a close() method for generator-iterators, which raises GeneratorExit at the point where the generator was paused.

I think it is not possible to meet that condition using own implementation with classes.

Am I wrong? Is it really possible to implement real generator using classes?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Adam
  • 639
  • 9
  • 22
  • It's not acting like a generator. You printed an object. https://stackoverflow.com/questions/4932438/how-to-create-a-custom-string-representation-for-a-class-object – OneCricketeer Sep 09 '17 at 17:47

1 Answers1

3

Generators are simply a type of iterator. From the datamodel documentation:

Generator functions
A function or method which uses the yield statement [...] is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned.

You can't tell by the repr() output if something is a generator. Python looks for the iterator methods, and you can implement your own send and throw methods on top of those, as you have done.

As such, your implementation works as designed, it is valid iterator:

>>> x = Fib()
>>> next(x)
0
>>> next(x)
1

Without the collections.abc.Generator base, you can also implement your own __iter__ method (this has to return self), and a __next__ method that produces the next value when called or raises StopIteration when done.

The base collections.abc.Generator implementation defines the __next__ method for you and supplies an __iter__ method that simply calls self.send(None).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • The question is a using coroutine object – OneCricketeer Sep 09 '17 at 17:50
  • @cricket_007: no it is not. – Martijn Pieters Sep 09 '17 at 17:52
  • @cricket_007: a coroutine is a specialised iterable, but the object definition in the question is no such object. – Martijn Pieters Sep 09 '17 at 17:53
  • Ah, so read the documentation wrong? https://docs.python.org/3/reference/datamodel.html#coroutine-objects – OneCricketeer Sep 09 '17 at 17:54
  • 1
    @cricket_007: *Coroutines **also** have the methods listed below, which are **analogous to those of generators***. – Martijn Pieters Sep 09 '17 at 17:54
  • @cricket_007: You didn't read the [section *before* that](https://docs.python.org/3/reference/datamodel.html#coroutines), the OP has no `__await__` method. – Martijn Pieters Sep 09 '17 at 17:55
  • My bad. Just the linked question pointed at the coroutine data model – OneCricketeer Sep 09 '17 at 17:59
  • So, how we can meet that condition "3. Add a new throw() method for generator-iterators, which **raises an exception at the point where the generator was paused,**."? – Adam Sep 10 '17 at 05:45
  • @Adam: why do you need that? You don't actually have a generator here, you have an *iterator* with the same methods. There is no actual paused block of code with an exception handler. Just change the state of your object so that it'll no longer produce new values, or has changed what values are produced, or whatever behaviour the *consumers* of your object would need to see. – Martijn Pieters Sep 10 '17 at 09:53
  • **You don't actually have a generator here, you have an iterator with the same methods** This is the point of my question. Could we say that class which has this same methods like generator is generator? That's why I gave the example - it has methods like generator (even behaves almost like generator), but it doesn't work like generator defined by PEPs (especially 342). So is it the generator or not? – Adam Sep 10 '17 at 13:02
  • Here we have generator methods and description of them https://docs.python.org/3/reference/expressions.html#generator-methods . When I read PEPs I see that generator can be created only by generator-function, I can't find other ways to create generator. That's why I think that (formally) Fib is not generator, even when it derives from Generator ABC. – Adam Sep 10 '17 at 13:18
  • 2
    @Adam: that's a philosophical question. Is an object that has all the same methods as a list, really a list? Python *doesn't care*; as long as other code can use it *just like you can use a generator*, it's all fine. No, it's not a generator, but it walks and talks like one. – Martijn Pieters Sep 10 '17 at 14:37
  • 1
    @Adam: the `collections.abc` objects have two functions: letting you create objects that walk and talk like those types, *and* testing if for those types. If your goal is to produce an object that can stand in for a generator, then using `collections.abc.Generator` as a base is a great way of doing that. – Martijn Pieters Sep 10 '17 at 14:39