Ітератори
Я почну з розгляду особливості мови Python, яка є важливою основою для написання програм в функціональному стилі: ітератори.
Ітератор — це об'єкт, що представляє потік даних; цей об'єкт повертає дані по одному елементу за раз. Ітератор в Python повинен підтримувати метод __next__()
, який не приймає аргументів і завжди повертає наступний елемент потоку. Якщо в потоці більше немає елементів, __next__()
повинен викликати виняток StopIteration
. Ітератори не обов'язково повинні бути кінцевими; цілком розумно написати ітератор, який генерує нескінченний потік даних.
Вбудована функція iter()
приймає довільний об'єкт і намагається повернути ітератор, який поверне вміст або елементи об'єкта, викликаючи TypeError
, якщо об'єкт не підтримує ітерацію. Декілька вбудованих типів даних Python підтримують ітерацію, найпоширенішими з них є списки і словники. Об'єкт називається iterable
, якщо для нього можна отримати ітератор.
Ви можете експериментувати з інтерфейсом ітерації вручну:
L = [1, 2, 3]
it = iter(L)
print(it) # <...iterator object at ...>
print(it.__next__()) # Теж саме що й next(it)
print(next(it))
print(next(it))
print(next(it)) # Помилка StopIteration
Python очікує ітерабельні об'єкти в кількох різних контекстах, найважливішим з яких є оператор for
.
У виразі for X in Y
, Y має бути ітератором або об'єктом, для якого iter()
може створити ітератор.
Ці два вирази еквівалентні:
obj = [1, 2, 3]
for i in iter(obj):
print(i)
for i in obj:
print(i)
Ітератори можуть бути матеріалізовані як списки або кортежі за допомогою конструкторських функцій list
або tuple
:
L = [1, 2, 3]
iterator = iter(L)
t = tuple(iterator)
t
Розпакування послідовностей також підтримує ітератори: якщо ви знаєте, що ітератор поверне N елементів, ви можете розпакувати їх у N-кортеж:
L = [1, 2, 3]
iterator = iter(L)
a, b, c = iterator
a, b, c
Вбудовані функції, такі як max
і min
, можуть приймати один аргумент-ітератор і повернути найбільший або найменший елемент. Оператори "in"
і "not in"
також підтримують ітератори: X in iterator
є істинним, якщо X знайдено в потоці, що повертається ітератором. Ви зіткнетеся з очевидними проблемами, якщо ітератор нескінченний; max
, min
ніколи не повернуть значення, і якщо елемент X ніколи не з'явиться в потоці, оператори "in"
і "not in"
також не повернуть значення.
Зверніть увагу, що ви можете рухатися вперед в ітераторі; немає способу отримати попередній елемент, скинути ітератор або зробити його копію. Об'єкти-ітератори можуть опціонально надавати ці додаткові можливості, але протокол ітератора визначає лише метод ~iterator.__next__
. Отже, функції можуть споживати весь вихід ітератора, і якщо вам потрібно зробити щось інше з тим же потоком, вам доведеться створити новий ітератор.
Типи даних, що підтримують ітератори
Ми вже бачили, як списки і кортежі підтримують ітератори. Насправді, будь-який тип послідовності в Python, такий як рядки, автоматично підтримує створення ітератора.
Виклик iter()
на словнику повертає ітератор, який буде проходити по ключах словника:
m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}
for key in m:
print(key, m[key])
Зверніть увагу, що починаючи з Python 3.7, порядок ітерації по словнику гарантовано буде таким самим, як і порядок вставки. У більш ранніх версіях поведінка була невизначеною і могла відрізнятися між реалізаціями.
Застосування iter()
до словника завжди циклічно проходить по ключах, але словники мають методи, які повертають інші ітератори. Якщо ви хочете ітерувати по значеннях або парах ключ/значення, ви можете явно викликати методи dict.values()
або dict.items()
для отримання відповідного ітератора.
Конструктор dict
може приймати ітератор, який повертає кінцевий потік кортежів (ключ, значення)
:
L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')]
dict(iter(L))
Файли також підтримують ітерацію, викликаючи метод io.TextIOBase.readline()
до тих пір, поки у файлі більше не залишиться рядків. Це означає, що ви можете читати кожен рядок файлу ось так:
for line in file:
# робити щось для кожного рядка
...
Множини можуть отримувати свій вміст з ітерабельного об'єкта і дозволяти вам ітерувати по елементах множини:
S = {2, 3, 5, 7, 11, 13}
for i in S:
print(i)
Текст на цій сторінці є перекладом "Functional Programming HOWTO", автор: A. M. Kuchling. Інформація про копірайт: History and License.