[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