Author terry.reedy
Recipients Zero, docs@python, r.david.murray, rhettinger, terry.reedy, veky
Date 2017-07-17.19:43:02
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1500320582.74.0.673160767114.issue18558@psf.upfronthosting.co.za>
In-reply-to
Content
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
History
Date User Action Args
2017-07-17 19:43:02terry.reedysetrecipients: + terry.reedy, rhettinger, r.david.murray, Zero, docs@python, veky
2017-07-17 19:43:02terry.reedysetmessageid: <1500320582.74.0.673160767114.issue18558@psf.upfronthosting.co.za>
2017-07-17 19:43:02terry.reedylinkissue18558 messages
2017-07-17 19:43:02terry.reedycreate