[Tutor] Iterator vs. iterable cheatsheet, was Re: iter class

Peter Otten __peter__ at web.de
Fri Jan 24 18:44:31 CET 2014


spir wrote:

> On 01/24/2014 10:22 AM, Peter Otten wrote:
>>
>> There's an odd outlier that I probably shouldn't tell you about [...]
> 
> I guess there is a whole class of outliers; not really sure how to
> classify them. 

I think you are focusing on the details too much. In

> class Angles:
>      def __getitem__ (self, i):
>          return self.angles[i] * 360 / TAU
>      def __iter__ (self):
>          return iter(self.angles)

iter(self.angles) is just an implementation detail. The important point is 
that __iter__() returns a new iterator on each invocation, thus making 
Angles an "iterable" according to the naming scheme I was trying to 
establish. The __getitem__() method can be ignored as it is used for element 
lookup only, not for iteration.

    
> Side question: what is the point of __iter__ on iterators? Take a 'for'
> loop like: for x in xs: f(x)
> In the case where xs is not an iterator (no __next__), python calls
> iter(xs), which IIUC may call xs.__iter__() unless it is a builtin. But if
> xs is an iterator (__next__ is there), then Python uses it directly, thus
> what is the point of __iter__ there? In any case, python must check
> whether xs is an iterator (__next__). So there is no sense in calling
> __iter__ on an iterator. Logically, this would lead to an infinite
> recursion (calling __iter__ again and again). But python does it anyway
> (and stops after the first call, indeed):

There is no infinite recursion. The for loop is currently implemented as

# expect an iterable
# handle iterators through an idempotent iter()
tmp = iter(xs)
while True:
    try:
        x = next(tmp)
    except StopIteration:
        break
    # use x

If I understand you correctly you suggest the following:

# expect an iterator
# fall back to getting an iterator through iter()
try:
    tmp = xs.__next__
except AttributeError:
    tmp = iter(xs).__next__
while True:
    try:
        x = tmp()
    except StopIteration:
        break

How is that simpler?

> The only theoretical case I can find is iterators which do implement the
> protocol (__next__) but are not to be used (!), instead delegate to
> another iterator. 

Again: the important differentiation is between iterator and iterable, not 
how the iterator is implemented.

> Then, why do they bear __next__ at all? why are they
> iterators at all?

Python allows you to do things that make no sense from the point of view of 
a human being. You can also implement an integer type with a negative abs():

>>> class Int(int):
...     def __abs__(self): return self
... 
>>> abs(Int(-1))
-1

My advice: don't do it unless you have a good reason.



More information about the Tutor mailing list