2015-09-26 3 views
4

Сначала я хочу уточнить, я НЕ спрашиваю, что такое «итератор».Что означает «итерируемый» в Python?

Это как термин «итератор» определяется в doc Пайтона:

итерацию
Объект может возвращать своим членам по одному. Примеры итераций включают в себя все типы последовательностей (такие как list, str, и кортеж) и некоторые типы без последовательности, такие как dict, файловые объекты и объекты любых классов, которые вы определяете с помощью __iter __() или __getitem __() способ. Итераторы могут использоваться в цикле for и во многих других местах , где необходима последовательность (zip(), map(), ...). Когда объект iterable передается как аргумент встроенной функции iter(), он возвращает итератор для объекта. Этот итератор хорош за один проход по множеству значений. При использовании итераций обычно не требуется для вызова iter() или для работы с объектами итератора. Оператор для этого делает это автоматически для вас, создавая временную переменную без названия, чтобы удерживать итератор в течение всего цикла. См. Также итератор, последовательность и генератор.

Как other people suggested, используя isinstance(e, collections.Iterable) самый вещий способ проверить, является ли объект итерации.
Так что я сделал некоторые испытания с Python 3.4.3:

from collections.abc import Iterable 

class MyTrain: 
    def __getitem__(self, index): 
     if index > 3: 
      raise IndexError("that's enough!") 

     return index 

for name in MyTrain(): 
    print(name) # 0, 1, 2, 3 

print(isinstance(MyTrain(), Iterable)) # False 

Результат довольно странно: MyTrain определил __getitem__ метод, но он не рассматривается в качестве итератора объекта, не говоря уже, что он способен вернуть одно число за раз.

Затем я удалил __getitem__ и добавил __iter__ метод:

from collections.abc import Iterable 

class MyTrain:  
    def __iter__(self): 
     print("__iter__ called") 
     pass 

print(isinstance(MyTrain(), Iterable)) # True 

for name in MyTrain(): 
    print(name) # TypeError: iter() returned non-iterator of type 'NoneType' 

Сейчас рассматриваются как «истинный» итерация объект, несмотря на это ничего не может во время прохода производства.

Так что я что-то неправильно понял или неверно?

+6

'isinstance' не будет проверять, что интерфейс правильно реализован, что не получает узнал, пока вы на самом деле не попробовать перебрать его, только что соответствующий метод (ы) (в данном случае [только' __iter__' ] (https://docs.python.org/2/library/collections.html#collections.Iterable)). – jonrsharpe

+0

https://docs.python.org/3/library/collections.abc.html#collections.abc.Iterable –

+9

* ", используя' isinstance (е, collections.Iterable) 'самый вещий способ проверить, если объект iserable "* - нет, я бы сказал, что ** пытается перебрать его ** - это самый Pythonic путь! – jonrsharpe

ответ

2

Я думаю, что точка путаницы здесь является то, что, хотя осуществление __getitem__делает позволит вам перебрать объект, он не является частью интерфейса, определенного Iterable.

abstract base classes позволяет форму виртуальных подклассов, где классы, реализующие указанные методы (в случае Iterable только __iter__) рассматриваются isinstance и issubclass быть подклассами азбуки , даже если они явно не наследовать от них. Он не проверяет, действительно ли реализует метод , но только он предоставлен или нет.

Для получения дополнительной информации см. PEP-3119, в которой представлены Азбука.


использование isinstance(e, collections.Iterable) является наиболее вещим способом , чтобы проверить, является ли объект итерации

Я не согласен; Я бы использовал duck-typing и просто попытался выполнить итератор по объекту. Если объект не является итерабельным, будет поднят TypeError, который вы можете поймать в своей функции, если вы хотите иметь дело с непереходными входами или разрешить просачиваться до вызывающего абонента, если нет. Это полностью соответствует тому, как объект решил реализовать итерацию, и просто выясняет, делает он это в самое подходящее время.


Чтобы добавить немного больше, я думаю, что документы вы указаны немного в заблуждение. Чтобы процитировать iter docs, который, возможно, ясно, что это до:

объект должен быть объект коллекции, который поддерживает протокол итераций (метод __iter__()), или он должен поддерживать протокол последовательности (в __getitem__() метод с целым числом аргументы начиная с по адресу 0).

Это ясно показывает, что, хотя оба протокол делает объект итератор, только один фактический «протокол итерации» , и именно это isinstance(thing, Iterable) тестов для. Поэтому мы можем заключить, что один способ проверить для «вещей, которые вы можете выполнять итерацию над» в самом общем случае будет:

isinstance(thing, (Iterable, Sequence)) 

хотя это также требует от вас осуществлять __len__ вместе с __getitem__ к «практически подкласс "Sequence.

+0

Thx jorsharpe, ваш ответ очень помогает. Пока я действительно думаю, что документ, который я цитировал, вводит в заблуждение. Глоссарий «итерируемый» в словаре глоссарий означает, что «может быть повторен» независимо от того, как это делается, в то время как «collection.abc.Iterable» Python представляет собой действительный контейнер итерации (я сделал это слово), это любой объект, который определяет ' __iter__'. – laike9m

+0

Так что 'collections.abc.Iterable' не обязательно« итерируется », так как он может вернуть недопустимый итератор, который не реализует' __next__'. Между тем, «итерируемый» объект не может иметь никакого отношения к 'collections.abc.Iterable', определяя только' __getitem__'. Как вы думаете? – laike9m

+0

@ laike9m это правильно, но тогда просто определение метода '__getitem__' также не гарантирует, что он на самом деле * работает *! Вот почему я предпочитаю использовать утиный набор [* "EAFP" *] (https://docs.python.org/3/glossary.html#term-eafp) - учитывая, что вы не можете действительно знать, является ли объект * на самом деле iterable *, пока вы это сделаете, тогда это самое подходящее время, чтобы справиться с этим не было. В идеале документация должна сказать что-то вроде * «объект итерабельен, если он реализует итератор или протоколы последовательности» *, возможно, хотя кажется, что '__len__' на практике не требуется. – jonrsharpe

0

Это is истребитель. Однако вы не унаследовали от abc.Iterable, поэтому, естественно, Python не будет сообщать об этом как происходящем из этого класса. Две вещи - итерабельность и спуск из этого базового класса - совершенно разные.

+0

Я думаю, что смущает OP то, что классы, реализующие '__iter__', являются виртуальными подклассами' Iterable', даже если они явно не наследуют его. – jonrsharpe

-1

Iterable - это что-то (коллекция что-либо), что позволяет использовать некоторую итерацию на своих элементах. Но каков общий способ итерации в python? То есть используется ключевое слово in, которое использует метод объекта __iter__. Таким образом, в этом выражении любой объект, который определяет __iter__, может использоваться с in и является Итерируемым.

Так, большинство «утка-typish» способ проверить, является ли объект итератора, если объект находится в этом, (Да, я знаю, неявно, что это то, что происходит в isinstance случае, а также, за счет виртуальных классов)

hasattr(train, '__iter__') 

потому что в соответствии с утиным типом мы заботимся о поведении, предоставляемом объектом вместо его предков.

Если у вас есть неправильная реализация __iter__, которая не означает, что объект не является итерируемым, это просто означает, что у вас есть ошибка в коде.

Примечание: - Объекты, которые не определяют __iter__, все еще могут быть итерируемыми в общем смысле, используя какой-либо другой метод, просто они не могут использоваться с ключевым словом in. Пример: -NumberList экземпляр iserable over each метод, но не может быть итерабельным в смысле python.

class NumberList: 

    def __init__(self, values): 
     self.values = values 

    def each(self): 
     return self.values 
+0

1. Это ** не ** утка набрав. 2. 'hasattr (train, '__iter __')' примерно то, что 'isststance (train, Iterable)' уже делает. – jonrsharpe

+0

@jonrshare Я знаю, но это немного вводит в заблуждение. Разве вы не думаете, что isststance должен проверять наследование вместо – hspandher

+0

Если объект предоставляет метод «__iter__», нам все равно, является ли он экземпляром Iterable или нет. Разве это не то, что утка типирование означает – hspandher

Смежные вопросы