Плутають списки python: це вони чи ні, ітератори?


81

Я вивчаю "Пітон" Алекса Мартелі в короткій оболонці, і книга свідчить, що будь-який об'єкт, який має next()метод, є (або, принаймні, може бути використаний як) ітератором . Це також передбачає, що більшість ітераторів будуються за допомогою неявних або явних викликів викликаного методу iter.

Прочитавши це в книзі, я відчув бажання спробувати. Я запустив інтерпретатор python 2.7.3 і зробив це:

>>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for number in range(0, 10):
...     print x.next()

Однак результат був такий:

Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'list' object has no attribute 'next'

В розгубленості я спробував вивчити структуру x-об'єкта через dir(x)і помітив, що в ньому є __iter__функційний об'єкт. Тож я зрозумів, що його можна використовувати як ітератор, якщо він підтримує такий тип інтерфейсу.

Тому, коли я спробував ще раз, цього разу дещо інакше, намагаючись зробити це:

>>> _temp_iter = next(x)

Я отримав цю помилку:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list object is not an iterator

Але як список може НЕ бути ітератором, оскільки він, схоже, підтримує цей інтерфейс, і, безумовно, може бути використаний як такий у наступному контексті:

>>> for number in x:
...     print x

Хтось може допомогти мені пояснити це на мій погляд?

Відповіді:


107

Їх можна повторити , але вони не є ітераторами . Їм можна передати, щоб iter()отримати для них ітератор або неявно (наприклад, через for), або явно, але вони самі по собі не є ітераторами.


11
Зверніть увагу, що всі ітератори (які добре поводяться) також є ітерабельними - вони nextпросто повертаються self, тому ви можете зателефонувати iter(iter(iter(iter(x))))і отримати те саме, що і iter(x). Ось чому forпрацює як з ітераторами, так і з ітераторами без нюху типу (ну, не враховуючи оптимізацію продуктивності).

13
@delnan Я думаю, ви мали на увазі, що "їх iterпросто повертається self".
Lauritz V. Thaulow

Наступне питання, звичайно; чому вони призначені не бути ітераторами?
gerrit

2
@gerrit: Оскільки ітератори не підтримують довільний доступ.
Ігнасіо Васкес-Абрамс

1
@ IgnacioVazquez-Abrams Чи є вимога "не підтримувати"? Я вважав, що вимоги / контракти зазвичай позитивні, і що все одно можна "бути" X (у сенсі: бути здатним на все, на що здатний X), маючи при цьому можливості, яких у X немає.
gerrit

26

Спочатку потрібно перетворити список на ітератор, використовуючи iter():

In [7]: x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [8]: it=iter(x)

In [9]: for i in range(10):
    it.next()
   ....:     
   ....:     
Out[10]: 0
Out[10]: 1
Out[10]: 2
Out[10]: 3
Out[10]: 4
Out[10]: 5
Out[10]: 6
Out[10]: 7
Out[10]: 8
Out[10]: 9

In [12]: 'next' in dir(it)
Out[12]: True

In [13]: 'next' in dir(x)
Out[13]: False

перевірка того, є об'єкт ітератором чи ні:

In [17]: isinstance(x,collections.Iterator)
Out[17]: False

In [18]: isinstance(x,collections.Iterable)
Out[18]: True

In [19]: isinstance(it,collections.Iterable) 
Out[19]: True

In [20]: isinstance(it,collections.Iterator)
Out[20]: True

22

Про всяк випадок вас бентежить, яка різниця між ітераторами та ітераторами. Ітератор - це об’єкт, що представляє потік даних. Він реалізує протокол ітератора:

  • __iter__ метод
  • next метод

Повторні виклики методу next () ітератора повертають послідовні елементи в потоці. Коли більше даних немає, об’єкт ітератора вичерпується, і будь-які подальші виклики до його методу next () просто знову піднімають StopIteration.

З іншого боку ітерабельні об'єкти реалізують __iter__метод, який при виклику повертає ітератор, що дозволяє здійснювати багаторазові передачі даних. Об'єкти, що піддаються ітерації, можна використовувати багаторазово, після вичерпання їх можна повторити знову. Їх можна перетворити на ітератори за допомогоюiter функції.

Отже, якщо у вас є список (ітеративний), ви можете зробити:

>>> l = [1,2,3,4]
>>> for i in l:
...     print i,
1 2 3 4
>>> for i in l:
...     print i,
 1 2 3 4

Якщо ви перетворюєте свій список на ітератор:

>>> il = l.__iter__()  # equivalent to iter(l)
>>> for i in il:
...     print i,
 1 2 3 4
>>> for i in il:
...     print i,
>>> 

7

Список не є ітератором, але список містить об'єкт-ітератор, __iter__тому при спробі використовувати цикл for у будь-якому списку, __iter__метод викликів циклу і отримує об'єкт-ітератор, а потім використовує метод next () списку.

x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
it = x.__iter__()

Тепер itмістить об’єкт-ітератор, xяким ви можете користуватися як it.next()до викиду виключення StopIteration


3
Що призводить до…AttributeError: 'list' object has no attribute 'iter'
е-суші

так, я знаю, що немає атрибута iter, вам потрібно поставити 2 символи підкреслення до і після методу iter __iter__, переповнення стека перетворює його на напівжирний символ, замінюючи підкреслення, яке я згадав у своїй відповіді. Я не знав про це
Каушаль

У python3 it.next()підвищує AttributeError: 'list_iterator' object has no attribute 'next'. Натомість єnext(it)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.