К этому моменту мы много раз использовали цикл for с разными типами данных. Списки, словари, кортежи, строки, файлы — всё это можно обрабатывать с помощью цикла:
for element in [1, 2, 3]:
print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open('myfile.txt'):
print(line, end='')
Как же это всё работает? 🤔 Все типы данных, которые позволяют перебирать себя с помощью цикла, используют итераторы.
<aside> 💡 Итератор — это объект, который позволяет проходить по элементам, используя встроенные средства языка.
</aside>
Под капотом для работы цикла for Python использует две встроенные функции: iter() и next(). Первая функция получает итератор, вторая проходит по этому итератору:
data = [1, 2, 3] # Создаём список из трёх элементов
iterator = iter(data) # Берём его итератор
print(next(iterator)) # Берём первое число из итератора, выведется 1
print(next(iterator)) # Берём второе число из итератора, выведется 2
print(next(iterator)) # Берём третье число из итератора, выведется 3
Этот код можно переписать с помощью цикла while так, чтобы в нём вызывалась функция next():
data = [1, 2, 3] # Создаём список из трёх элементов
iterator = iter(data) # Берём его итератор
while True:
print(next(iterator)) # Берём очередной элемент из итератора
Однако если запустить этот код, мы получим не совсем то, что ожидали увидеть:
1
2
3
Traceback (most recent call last):
File "main.py", line 6, in <module>
print(next(iterator)) # Берём очередной элемент из итератора
StopIteration
Здесь мы получаем последний кусок пазла: если функция next() вызовется, когда итератор дошёл до конца, то Python выбросит исключение StopIteration, которое как раз и говорит о том, что итерироваться больше не нужно.
С учётом этого дополним нашу программу:
data = [1, 2, 3] # Создаём список из трёх элементов
iterator = iter(data) # Берём его итератор
while True:
try:
print(next(iterator)) # Берём очередной элемент из итератора
except StopIteration: # Когда получаем исключение StopIteration,
break # то выходим из цикла
Вот так любой цикл for можно развернуть в цикл while. Само по себе это немного бесполезное умение, однако здесь мы увидели механизмы, которые позволят нам чуть дальше создать свой класс, объекты которого смогут итерироваться в цикле.