0

I am aware of this question:

Difference between Python's Generators and Iterators

It is more broadly and less technically based. And none of the answers have been selected. I also read through those answers and with one possible exception did not find what I was looking for. I wish to ask a more precise question to help me understand some details.

I asked this question earlier:

What is the difference between a python itterator and a python generator?

Perhaps the title or the way I asked the question was misleading, since the response I did get was not on target to my intentions and the question was closed within seconds.

So I will try to clarify here.

Consider the following code:

p = [k for k in range(1,1000)]
i = iter(p)
g = (k for k in p)

Is there some operation that can be done on i and g that will distinguish between these two constructions. Something that I can do with i that I can't do with g, that sort of thing. Their type comes out as list iterator versus generator object, but it is unclear to me that this has any pragmatic impact on what one can do with it, or its efficiency. I deliberately have constructed the list first to emphasize that the issue of generating the list up front or on demand is not what the question is about.

At the moment, I suspect that the answer to the more general question is this - generators are a special case of iterators and whatever you can do with generator construction using either comprehensions or yield can be done by explicitly writing the corresponding iterator. The justification for using a generator rather than an iterator is that sometimes it is easier to write.


Later I found this question that lead to some good exposition on the topic.

How to write a generator class?

  • 1
    Well, yes, generators were added *as a convenient way to create iterators*. They are also quite expressive, often providing much cleaner code. Generators can also function as coroutines, so you can `.send` values into a generator... if you simply want to distinguish the types, you can use `Generator = type((None for _ in range(0)))` then just check `isinstance(some_object, Generator)` – juanpa.arrivillaga May 27 '20 at 10:10
  • 1
    [Here's](https://docs.python.org/3/howto/functional.html#generators) some good reading. – juanpa.arrivillaga May 27 '20 at 10:13
  • Thanks all of you. I would like to state for the record that I understand and have used the concepts involved in several languages. I was looking for the straight forward technical details - because I seemed to be getting nowhere wading through the documentation that seemed to spend a lot of time explaining the concepts rather than the implementation. IMHO. – Ponder Stibbons May 28 '20 at 01:35

2 Answers2

2

g supports send, as all generators do, while i doesn't. (sending to g isn't useful, but you can do it.)

>>> g.send(None)
1
>>> i.send(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list_iterator' object has no attribute 'send'

You can also throw exceptions into g or close it, which you can't do with i.

i can be pickled, while g can't.

Aside from that, you can do all sorts of explicit checks and introspection to distinguish them. Checking the types, examining the str output, looking for attributes that only exist on one or the other (like g.gi_frame), etc.

Most of this is implementation details or incidental, not something you should think of as "the difference between generators and iterators". Generators are a kind of iterator.

user2357112
  • 260,549
  • 28
  • 431
  • 505
2

Here are some excerpts from the documentation:

4.5.1 Generator type:
Python’s generators provide a convenient way to implement the iterator protocol.

and later:

6.2.9. Yield expressions:
When a generator function is called, it returns an iterator known as a generator.

=> a generator function creates an iterator

The next paragraphs gives the additional methods of a generator:

6.2.9.1. Generator-iterator methods
This subsection describes the methods of a generator iterator.

generator.__next__(): ...

same behaviour as iterator.next()

generator.send(value)
Resumes the execution and “sends” a value into the generator function...

`generator.throw(type[, value[, traceback]])``
Raises an exception of type type at the point where the generator was paused, and returns the next value yielded by the generator function...

generator.close()
Raises a GeneratorExit at the point where the generator function was paused...

That means that a generator is a special case of an iterator with some additional methods.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks, I liked this answer too - but had to make a choice. The responses here have definitely cleared up my thinking on the matter. – Ponder Stibbons May 28 '20 at 01:32