Python "для" циклов (определенная итерация)

Python "для" циклов (определенная итерация)

Это руководство покажет вам, как выполнитьdefinite iteration с помощью цикла Pythonfor.

Вprevious tutorial этой вводной серии вы узнали следующее:

  • Повторяющееся выполнение одного и того же блока кода снова и снова обозначается какiteration.

  • Существует два типа итерации:

    • Definite итерация, в которой количество повторений указывается заранее явно

    • Indefinite итерация, в которой блок кода выполняется до тех пор, пока не будет выполнено какое-либо условие

  • В Python неопределенная итерация выполняется с помощью циклаwhile.

Вот что вы расскажете в этом руководстве:

  • Вы начнете со сравнения некоторых различных парадигм, используемых языками программирования для реализации определенной итерации.

  • Затем вы узнаете оiterables иiterators, двух концепциях, которые составляют основу определенной итерации в Python.

  • Наконец, вы свяжете все это вместе и узнаете о циклах Pythonfor.

Free Bonus:Click here to get access to a chapter from Python Tricks: The Book, который демонстрирует вам лучшие практики Python на простых примерах, которые вы можете мгновенно применить для написания более красивого кода Pythonic.

Обзор определенной итерации в программировании

Определенные итерационные циклы часто называют цикламиfor, потому чтоfor - это ключевое слово, которое используется для их представления почти во всех языках программирования, включая Python.

Исторически языки программирования предлагали несколько разновидностей циклаfor. Они кратко описаны в следующих разделах.

Цикл числового диапазона

Самый простой циклfor - это простой оператор числового диапазона с начальным и конечным значениями. Точный формат зависит от языка, но обычно выглядит примерно так:

for i = 1 to 10
    

Здесь тело цикла выполняется десять раз. Переменнаяi принимает значение1 на первой итерации,2 на второй и так далее. Этот вид циклаfor используется в языках BASIC, Algol и Pascal.

Цикл с тремя выражениями

Другая форма циклаfor, популяризированная языком программирования C, состоит из трех частей:

  • Инициализация

  • Выражение, указывающее конечное условие

  • Действие, которое нужно выполнить в конце каждой итерации.

Этот тип имеет следующую форму:

for (i = 1; i <= 10; i++)
    

Technical Note: В языке программирования Ci++ увеличивает значение переменнойi. Это примерно эквивалентноi += 1 в Python.

Этот цикл интерпретируется следующим образом:

  • Инициализироватьi как1.

  • Продолжайте цикл, покаi <= 10.

  • Увеличивайтеi на1 после каждой итерации цикла.

Циклы с тремя выражениямиfor популярны, потому что выражения, указанные для трех частей, могут быть почти любыми, так что это имеет немного больше гибкости, чем простая форма числового диапазона, показанная выше. Эти циклыfor также представлены в языках C ++, Java, PHP и Perl.

Цикл на основе коллекции или итератора

Этот тип цикла перебирает коллекцию объектов, а не указывает числовые значения или условия:

for i in 
    

Каждый раз при прохождении цикла переменнаяi принимает значение следующего объекта в<collection>. Этот тип циклаfor, пожалуй, самый общий и абстрактный. Perl и PHP также поддерживают этот тип цикла, но он вводится ключевым словомforeach вместоfor.

Further Reading: См. страницу в ВикипедииFor loop для более глубокого изучения реализации определенной итерации в языках программирования.

Цикл Pythonfor

Из перечисленных выше типов циклов Python реализует только последнюю: основанную на коллекции итерацию. На первый взгляд, это может показаться сырой сделкой, но будьте уверены, что реализация Python определенной итерации настолько универсальна, что вы не будете чувствовать себя обманутым!

Вскоре вы подробно изучите внутреннюю часть цикла Pythonfor. Но сейчас давайте начнем с быстрого прототипа и примера, просто чтобы познакомиться.

Цикл Pythonfor выглядит так:

for  in :
    

<iterable> - это набор объектов, например список или кортеж. <statement(s)> в теле цикла обозначаются отступом, как и все управляющие структуры Python, и выполняются один раз для каждого элемента в<iterable>. Переменная цикла<var> принимает значение следующего элемента в<iterable> каждый раз в цикле.

Вот типичный пример:

>>>

>>> a = ['foo', 'bar', 'baz']
>>> for i in a:
...     print(i)
...
foo
bar
baz

В этом примере<iterable> - это списокa, а<var> - это переменнаяi. Каждый раз при прохождении циклаi принимает последовательный элемент вa, поэтомуprint() отображает значения'foo','bar' и'baz' соответственно. Циклfor, подобный этому, - это питонический способ обработки элементов в итерации.

Но что именно повторяется? Перед дальнейшим изучением цикловfor будет полезно более глубоко разобраться в том, какие итерации есть в Python.

итерируемыми

В Pythoniterable означает, что объект можно использовать в итерации. Термин используется как:

  • An adjective: Объект можно описать как повторяемый.

  • A noun: Объект можно охарактеризовать как итеративный.

Если объект является итеративным, его можно передать встроенной функции Pythoniter(), которая возвращает нечто, называемоеiterator. Да, терминология становится немного повторяющейся. Повесить там. Все это работает в конце.

Каждый из объектов в следующем примере является итерируемым и возвращает некоторый тип итератора при передаче вiter():

>>>

>>> iter('foobar')                             # String


>>> iter(['foo', 'bar', 'baz'])                # List


>>> iter(('foo', 'bar', 'baz'))                # Tuple


>>> iter({'foo', 'bar', 'baz'})                # Set


>>> iter({'foo': 1, 'bar': 2, 'baz': 3})       # Dict

Эти типы объектов, с другой стороны, не повторяемы:

>>>

>>> iter(42)                                   # Integer
Traceback (most recent call last):
  File "", line 1, in 
    iter(42)
TypeError: 'int' object is not iterable

>>> iter(3.1)                                  # Float
Traceback (most recent call last):
  File "", line 1, in 
    iter(3.1)
TypeError: 'float' object is not iterable

>>> iter(len)                                  # Built-in function
Traceback (most recent call last):
  File "", line 1, in 
    iter(len)
TypeError: 'builtin_function_or_method' object is not iterable

Все типы данных, с которыми вы столкнулись до сих пор, а именно типы коллекций или контейнеров, являются итеративными. К ним относятся типыstring,list,tuple,dict,set иfrozenset.

Но это ни в коем случае не единственные типы, которые вы можете перебирать. Многие объекты, встроенные в Python или определенные в модулях, предназначены для итерации. Например, открытые файлы в Python являются итеративными. Как вы вскоре увидите в руководстве по файловому вводу / выводу, перебор открытого объекта файла считывает данные из файла.

Фактически, почти любой объект в Python может быть сделан итеративным. Даже определенные пользователем объекты могут быть спроектированы таким образом, чтобы их можно было повторять. (Вы узнаете, как это сделать, в следующей статье об объектно-ориентированном программировании.)

итераторы

Хорошо, теперь вы знаете, что значит итерация объекта, и знаете, как использоватьiter() для получения из него итератора. Если у вас есть итератор, что вы можете с ним сделать?

Итератор по сути является источником значений, который выдает последовательные значения из своего связанного итерируемого объекта. Встроенная функцияnext() используется для получения следующего значения из итератора.

Вот пример, использующий тот же список, что и выше:

>>>

>>> a = ['foo', 'bar', 'baz']

>>> itr = iter(a)
>>> itr


>>> next(itr)
'foo'
>>> next(itr)
'bar'
>>> next(itr)
'baz'

В этом примереa - это итерационный список, аitr - это связанный итератор, полученный с помощьюiter(). Каждый вызовnext(itr) получает следующее значение изitr.

Обратите внимание, как итератор внутренне сохраняет свое состояние. Он знает, какие значения уже были получены, поэтому, когда вы вызываетеnext(), он знает, какое значение вернуть дальше.

Что происходит, когда у итератора заканчиваются значения? Давайте сделаем еще один вызовnext() для итератора выше:

>>>

>>> next(itr)
Traceback (most recent call last):
  File "", line 1, in 
    next(itr)
StopIteration

Если все значения от итератора уже были возвращены, последующий вызовnext() вызывает исключениеStopIteration. Любые дальнейшие попытки получить значения от итератора потерпят неудачу.

Вы можете получить значения от итератора только в одном направлении. Вы не можете вернуться назад. Нет функцииprev(). Но вы можете определить два независимых итератора для одного и того же итерируемого объекта:

>>>

>>> a
['foo', 'bar', 'baz']

>>> itr1 = iter(a)
>>> itr2 = iter(a)

>>> next(itr1)
'foo'
>>> next(itr1)
'bar'
>>> next(itr1)
'baz'

>>> next(itr2)
'foo'

Даже когда итераторitr1 уже находится в конце списка,itr2 все еще находится в начале. Каждый итератор поддерживает свое собственное внутреннее состояние, независимое от другого.

Если вы хотите получить сразу все значения из итератора, вы можете использовать встроенную функциюlist(). Среди других возможных примененийlist() принимает итератор в качестве аргумента и возвращает список, состоящий из всех значений, выданных итератором:

>>>

>>> a = ['foo', 'bar', 'baz']
>>> itr = iter(a)
>>> list(itr)
['foo', 'bar', 'baz']

Точно так же встроенные функцииtuple() иset() возвращают кортеж и набор, соответственно, из всех значений, полученных итератором:

>>>

>>> a = ['foo', 'bar', 'baz']

>>> itr = iter(a)
>>> tuple(itr)
('foo', 'bar', 'baz')

>>> itr = iter(a)
>>> set(itr)
{'baz', 'foo', 'bar'}

Не обязательно совмещать это с привычкой. Часть элегантности итераторов в том, что они «ленивы». Это означает, что когда вы создаете итератор, он не генерирует все элементы, которые он может получить только тогда. Он ждет, пока вы их запросите с помощьюnext(). Элементы не создаются, пока они не будут запрошены.

Когда вы используетеlist(),tuple() и т.п., вы заставляете итератор генерировать все свои значения сразу, чтобы их можно было вернуть. Если общее количество объектов, возвращаемых итератором, очень велико, это может занять много времени.

Фактически, в Python можно создать итератор, который возвращает бесконечный ряд объектов. (Вы узнаете, как это сделать, в следующих руководствах по функциям генератора иitertools.) Если вы попытаетесь получить все значения сразу из бесконечного итератора, программа выдастhang.

Внутренности цикла Pythonfor

Теперь вы познакомились со всеми концепциями, необходимыми для полного понимания того, как работает цикл Pythonfor. Прежде чем продолжить, давайте рассмотрим соответствующие условия:

Term Смысл

итерация

Процесс перебора объектов или элементов в коллекции

Iterable

Объект (или прилагательное, используемое для описания объекта), который можно повторять.

Итератор

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

iter()

Встроенная функция, используемая для получения итератора из итерируемого

Теперь снова рассмотрим простой циклfor, представленный в начале этого руководства:

>>>

>>> a = ['foo', 'bar', 'baz']
>>> for i in a:
...     print(i)
...
foo
bar
baz

Этот цикл может быть полностью описан в терминах концепций, о которых вы только что узнали. Чтобы выполнить итерацию, описываемую этим цикломfor, Python делает следующее:

  • Вызываетiter() для получения итератора дляa

  • Вызываетnext() несколько раз, чтобы получить каждый элемент от итератора по очереди

  • Завершает цикл, когдаnext() вызывает исключениеStopIteration

Тело цикла выполняется один раз для каждого возвращаемого элементаnext(), при этом для переменной циклаi устанавливается значение данного элемента для каждой итерации.

Эта последовательность событий представлена ​​на следующей диаграмме:

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

  • Многие встроенные и библиотечные объекты являются итеративными.

  • Существует модуль стандартной библиотеки под названиемitertools, содержащий множество функций, возвращающих итерации.

  • Пользовательские объекты, созданные с объектно-ориентированной возможностью Python, могут быть сделаны итеративными.

  • Python имеет конструкцию, называемую генератором, которая позволяет вам создать свой собственный итератор простым и понятным способом.

Вы узнаете больше обо всем вышеупомянутом в этой серии. Все они могут быть целью циклаfor, и синтаксис одинаков для всех. Он элегантен своей простотой и чрезвычайно универсален.

Перебор словаря

Вы видели ранее, что итератор может быть получен из словаря с помощьюiter(), поэтому вы знаете, что словари должны быть повторяемыми. Что происходит, когда вы перебираете словарь? Посмотрим:

>>>

>>> d = {'foo': 1, 'bar': 2, 'baz': 3}
>>> for k in d:
...     print(k)
...
foo
bar
baz

Как видите, когда циклfor проходит по словарю, переменная цикла присваивается ключам словаря.

Чтобы получить доступ к значениям словаря в цикле, вы можете сделать ссылку на словарь, используя ключ, как обычно:

>>>

>>> for k in d:
...     print(d[k])
...
1
2
3

Вы также можете перебирать значения словаря напрямую, используя.values():

>>>

>>> for v in d.values():
...     print(v)
...
1
2
3

Фактически, вы можете перебирать и ключи, и значения словаря одновременно. Это потому, что переменная циклаfor не ограничена только одной переменной. Это также может быть кортеж, и в этом случае назначения производятся из элементов итерируемого с использованием упаковки и распаковки, так же как и с помощью оператора присваивания:

>>>

>>> i, j = (1, 2)
>>> print(i, j)
1 2

>>> for i, j in [(1, 2), (3, 4), (5, 6)]:
...     print(i, j)
...
1 2
3 4
5 6

Как отмечалось в руководстве по Pythondictionaries, словарный метод.items() фактически возвращает список пар ключ / значение в виде кортежей:

>>>

>>> d = {'foo': 1, 'bar': 2, 'baz': 3}

>>> d.items()
dict_items([('foo', 1), ('bar', 2), ('baz', 3)])

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

>>>

>>> d = {'foo': 1, 'bar': 2, 'baz': 3}
>>> for k, v in d.items():
...     print('k =', k, ', v =', v)
...
k = foo , v = 1
k = bar , v = 2
k = baz , v = 3

Функцияrange()

В первом разделе этого руководства вы видели тип циклаfor, называемыйnumeric range loop, в котором указываются начальные и конечные числовые значения. Хотя эта форма циклаfor не встроена непосредственно в Python, к ней легко добраться.

Например, если вы хотите перебрать значения от0 до4, вы можете просто сделать это:

>>>

>>> for n in (0, 1, 2, 3, 4):
...     print(n)
...
0
1
2
3
4

Это решение не так уж плохо, когда всего несколько цифр. Но если бы диапазон чисел был намного больше, он бы быстро стал утомительным.

К счастью, Python предоставляет лучший вариант - встроенную функциюrange(), которая возвращает итерацию, которая дает последовательность целых чисел.

range(<end>) возвращает итерацию, которая возвращает целые числа, начинающиеся с0, вплоть до<end>, но не включая:

>>>

>>> x = range(5)
>>> x
range(0, 5)
>>> type(x)

Обратите внимание, чтоrange() возвращает объект классаrange, а не список или кортеж значений. Поскольку объектrange является итерируемым, вы можете получить значения, перебирая их с помощью циклаfor:

>>>

>>> for n in x:
...     print(n)
...
0
1
2
3
4

Вы также можете зафиксировать все значения сразу с помощьюlist() илиtuple(). В сеансе REPL это может быть удобным способом быстрого отображения значений:

>>>

>>> list(x)
[0, 1, 2, 3, 4]

>>> tuple(x)
(0, 1, 2, 3, 4)

Однако, когдаrange() используется в коде, который является частью более крупного приложения, обычно считается плохой практикой использовать таким образомlist() илиtuple(). Как и итераторы, объектыrange ленивы - значения в указанном диапазоне не генерируются, пока они не будут запрошены. Использованиеlist() илиtuple() для объектаrange приводит к одновременному возврату всех значений. Это редко необходимо, и если список длинный, он может тратить время и память.

range(<begin>, <end>, <stride>) возвращает итерацию, которая возвращает целые числа, начинающиеся с<begin>, вплоть до<end>, но не включая его. Если указано,<stride> указывает величину пропуска между значениями (аналогично значению шага, используемому для нарезки строк и списков):

>>>

>>> list(range(5, 20, 3))
[5, 8, 11, 14, 17]

Если<stride> опущен, по умолчанию используется1:

>>>

>>> list(range(5, 10, 1))
[5, 6, 7, 8, 9]
>>> list(range(5, 10))
[5, 6, 7, 8, 9]

Все параметры, указанные дляrange(), должны быть целыми числами, но любой из них может быть отрицательным. Естественно, если<begin> больше, чем<end>,<stride> должен быть отрицательным (если вам нужны какие-либо результаты):

>>>

>>> list(range(-5, 5))
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> list(range(5, -5))
[]
>>> list(range(5, -5, -1))
[5, 4, 3, 2, 1, 0, -1, -2, -3, -4]

Technical Note: Строго говоря,range() не совсем встроенная функция. Он реализован как вызываемый класс, который создает тип неизменяемой последовательности. Но для практических целей он ведет себя как встроенная функция.

Дополнительные сведения оrange() см. В статьеPython’s range() Function (Guide) на Real Python.

Изменение поведения циклаfor

В предыдущем руководстве этой вводной серии вы видели, как выполнение циклаwhile может быть прервано с помощьюbreak and continue statements и изменено с помощьюelse clause. Эти возможности также доступны в циклеfor.

Утвержденияbreak иcontinue

break иcontinue работают с цикламиfor так же, как с цикламиwhile. break полностью завершает цикл и переходит к первому оператору, следующему за циклом:

>>>

>>> for i in ['foo', 'bar', 'baz', 'qux']:
...     if 'b' in i:
...         break
...     print(i)
...
foo

continue завершает текущую итерацию и переходит к следующей итерации:

>>>

>>> for i in ['foo', 'bar', 'baz', 'qux']:
...     if 'b' in i:
...         continue
...     print(i)
...
foo
qux

Пунктelse

Циклfor также может иметь предложениеelse. Интерпретация аналогична интерпретации циклаwhile. Предложениеelse будет выполнено, если цикл завершится из-за исчерпания итерации:

>>>

>>> for i in ['foo', 'bar', 'baz', 'qux']:
...     print(i)
... else:
...     print('Done.')  # Will execute
...
foo
bar
baz
qux
Done.

Предложениеelse не будет выполнено, если список нарушен с помощью оператораbreak:

>>>

>>> for i in ['foo', 'bar', 'baz', 'qux']:
...     if i == 'bar':
...         break
...     print(i)
... else:
...     print('Done.')  # Will not execute
...
foo

Заключение

В этом руководстве представлен циклfor, рабочая лошадкаdefinite iteration в Python.

Вы также узнали о внутренней работеiterables иiterators, двух важных типов объектов, которые лежат в основе определенной итерации, но также занимают видное место в большом количестве другого кода Python.

В следующих двух руководствах этой вводной серии вы немного переключитесь и исследуете, как программы Python могут взаимодействовать с пользователем черезinput с клавиатуры иoutput на консоли.