Terry J. Reedy added the comment:

The problem with the Iterable ABC is that 'iterable' and 'iterator' are 
*dynamically* defined, with a possibly infinite time required to possibly 
destructively check either definition.  In general, an algorithmic *static* 
check can only guess whether an object is iterable, though humans analyzing 
enough code can potentially get it right.  Therefore, using isinstance(ob, 
Iterable) is not 100% reliable, and in my opinion *should not be used* as the 
definition of lower-case 'iterable'.

Definition: Object ob is iterable if 'iter(ob)' returns an iterator. For the 
reasons given above, iter may return a non-iterator, but it will if ob 
implements either the old or new iterator protocol.  If ob has .__iter__, iter 
returns ob.__iter__().  If ob has .__getitem__, iter returns iterator(ob), 
where iterator is a hidden internal class that embodies the old iterator 
protocol by defining a .__next__ method that calls .__getitem__.  In both 
cases, iter does the best it can by assuming that the methods are correctly 
written as per one of the two protocols.

Loose definition: Object 'it' is iterable if it can be looped over.
Python definition: Object 'it' is iterable if repeated 'next(it)' calls either 
return an object or raise StopIteration.  This means that

try:
    while True:
        next(it)
except StopIteration:
   pass

runs, possibly forever, without raising.

As Raymond noted, an iterator can be created multiple ways: IteratorClass(), 
iter(ob), iter(func, sentinal), generator_func().
---

Iterable versus iter with respect to classes with __getitem__:

Iter was added in 2.2.  Built-in iterables were only gradually converted from 
old to new protocol, by adding a new .__iter__.  So even ignoring user classes, 
iter *had* to respect .__getitem__.  Even today, though only a small fraction 
of classes with .__getitem__ are iterable, people do not generally call iter() 
on random objects.  

Iterable (added 2.6) is documented as the "ABC for classes that provide the 
__iter__() method."  In other words, isinstance(ob, Iterable) replaces 
hasattr(ob, '__iter__').  Except that the former is more than that.  The magic 
word 'register' does not appear in the collections.ABC doc, and I think that 
this is the omission to be remedied.

"ABC for classes that provide the __iter__() method, or that provide a 
__getitem__ method that implements the old iterator protocol and register 
themselves as Iterable."

An example could be given using a patched version of IsIterable.

If one adds two lines of code

from collections.abc import Iterable
...
Iterable.register(IsIterable)

then isinstance(IsIterable(3), Iterable) is True, except that this is a lie in 
the other direction.

Traceback (most recent call last):
  File "F:\Python\mypy\tem.py", line 17, in <module>
    for i in it2:
  File "F:\Python\mypy\tem.py", line 7, in __getitem__
    return self.data[key]
TypeError: 'int' object is not subscriptable

Either IsIterable.__init__ must check that data itself has .__getitem__ or 
IsIterable.__next__ must capture exceptions and raise IndexError instead.

        def __getitem__(self, key):
            try:
                return self.data[key]
            except Exception:
                raise IndexError

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue18558>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to