1

In Python 3 documentation, the iterator protocol is defined as follows:

The iterator objects themselves are required to support the following two methods, which together form the iterator protocol:

iterator.__iter__()

Return the iterator object itself. This is required to allow both containers and iterators to be used with the for and in statements. This method corresponds to the tp_iter slot of the type structure for Python objects in the Python/C API.

iterator.__next__()

Return the next item from the container. If there are no further items, raise the StopIteration exception. This method corresponds to the tp_iternext slot of the type structure for Python objects in the Python/C API.

I experimented with implementing it through individual object attributes:

class Empty: pass
o = Empty()
o.__iter__ = lambda: o
o.__next__ = lambda: 42
for i in o: print(i)

and got a TypeError:

TypeError: 'Empty' object is not iterable

What is wrong? Are there any precise rules somewhere about what objects can be used as iterable in for loop?

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
Alexey
  • 3,843
  • 6
  • 30
  • 44
  • 1
    The linked question explains why you can't just assign a function to an attribute and expect it to work as a bound method. In addition, correct versions of your lambdas would be `__iter__ = lambda self: self` and `__next__ = lambda self: 42` – Zero Piraeus Oct 23 '17 at 17:33
  • @ZeroPiraeus, you are wrong about `lambda`: with you definition, `o.__iter__` would require one positional argument. – Alexey Oct 23 '17 at 20:30
  • What do you notice about the number of positional arguments listed for `__iter__` in Python's [documentation](https://docs.python.org/3/reference/datamodel.html#object.__iter__)? – Zero Piraeus Oct 23 '17 at 20:34
  • The answers to the linked question may indeed answer my question, but i do not agree that my question is a duplicate of the other one. My question is about "iterator protocol", and it is not obvious to me that i should have searched for "Overriding special methods on an instance". – Alexey Oct 23 '17 at 20:35
  • @ZeroPiraeus, i also quoted Python documentation. To be sure, try calling `[1,2,3].__iter__` with an argument. – Alexey Oct 23 '17 at 20:37
  • Thanks for the link to the other question anyway. – Alexey Oct 23 '17 at 20:39
  • 1. You thought your question was about the iterator protocol, but it was actually about method binding. Don't worry about it. 2. Ok, so you don't know about `self` in Python yet. That's fine too ... Google ought to find you a whole bunch of learning material about it; here are some questions to start off: [1](https://stackoverflow.com/questions/6990099/explaining-the-python-self-variable-to-a-beginner), [2](https://stackoverflow.com/questions/2709821/what-is-the-purpose-of-self), [3](https://stackoverflow.com/questions/23944657/typeerror-method-takes-1-positional-argument-but-2-were-given). – Zero Piraeus Oct 23 '17 at 20:44
  • @ZeroPiraeus, please read carefully my question and comments. Your last comment was nonsense (i know what my question was about, i know what `self` is, etc.). – Alexey Oct 23 '17 at 20:51
  • [This](https://docs.python.org/3/reference/datamodel.html#special-method-lookup) answers my question indeed. – Alexey Oct 23 '17 at 20:56
  • Hrm. I'm not going to respond to anything further because this is getting silly, but: it is a plain and simple fact that `__iter__` takes `self` as its sole argument. That means, for iterators, either `def __iter__(self): return self` or `__iter__ = lambda self: self` (which are functionally identical). If you disagree, that is because **you don't properly understand**, and apparently refuse to accept that you don't understand something when it is pointed out to you. As such: I'm done here. – Zero Piraeus Oct 23 '17 at 20:57
  • @ZeroPiraeus, well, i see that "you don't properly understand, and apparently refuse to accept that you don't understand" -- please see the linked Python documentation in my previous comment. How do you think, why in their example they have `c.__len__ = lambda: 5` and not `c.__len__ = lambda self: 5`? – Alexey Oct 23 '17 at 21:01

0 Answers0