Автор: Шабанов Павел Александрович
Email: pa.shabanov@gmail.com
URLs:
Дата последнего обновления: 20.10.2018
Одним из базовых понятий программирования (в процедурном стиле и не только) является цикл.
Цикл - это инструмент, позволяющий повторять какой-либо кусок кода много-много раз. Разумно использовать цикл столько раз, сколько это нужно для реализации алгоритма.
Каждый повтор называется ещё итерацией.
Цикл (loop) - это логический блок, конструкция-контейнер, в пределах которого осуществляется повторение всего кода, содержащегося внутри него. Повторение просходит столько раз, сколько это определено условием (цикл while
), либо определённое заранее число раз (цикл for
).
Всё содержание цикла ("тело цикла") отделяется от строки, в которой объявлен цикл, четырьмя пробелами (правило 4 пробелов для вложенных структур).
Строка, в которой объявлен цикл, обязательно заканчивается двоеточием!
В python реализовано два вида циклов:
В первом случае известно количество повторений, шагов цикла или итераций (либо задаётся прямо, либо определяется длиной последовательности, которая используется для перебора). Во втором случае цикл будет повторять код бесконечно, если не будет выполнено условие выхода или не будет прервано пользователем вручную (это приведёт к остановке выполнения всей программы).
Цикл for имеет следующую конструкцию:
for <объект цикла> in <последовательность>:
....тело цикла
Уже знакомый оператор вхождения in здесь работает в связке с for и позволяет перебрать все элементы последовательности. В конце условия цикла обязательно ставится двоеточие. Тело цикла должно быть вложенным, т.е. размещаться на 4 пробела правее от начала описания цикла (от позиции буквы "f" в for).
Обратите внимание, что даже в интерактивном режиме (консоль) необходимо делать 4 пробела при написании кода внутри цикла! IDLE и другие, работающие с python среды разработки (IDE), будут вам помогать делать это автоматически, но за правильностью отступов вложенных логических структур в коде нужно следить!
Важно помнить, что тело цикла должно быть, но может быть несколько циклов со своими телами!
Пустая строка не годится, нужен какой-либо код. Чтобы описать "нулевой" или пустой цикл, который будет корректно восприниматься интерпретатором python, нужно воспользоваться особой командой pass.
Это пустая функция, пустая операция или выражение, которое позволяет управляющим структурам (циклы и ветвление) иметь тело без дополнительного кода. Это удобно в случае разработки кода, когда точно есть потребность в конструкции, но конкретное наполнение пока не определено.
# Тривиальный цикл
for i in [1, 2]:
pass
a = list(range(10)) # список
b = tuple(a) # кортеж; функция tuple() возвращает кортеж от последовательности
# Перебор элементов списка b
#for j in b:
# j - переменная, в которую передаёся элемент последовательности списка b
# print(j) # запятая подавляет перевод строки
# Перебор элементов кортежа a
for i in a:
print(i, end='|')
0|1|2|3|4|5|6|7|8|9|
Перебирать в цикле можно только те типы данных, которые поддерживают итерационный механизм или итерации! Например, можно перебирать элементы списков, элементы строк, ключи в словарях, открытые объект-файлы.
Нельзя перебирать в циклах числа, булевы переменные!
try:
for x in False:
pass
except:
print('Так не надо, Ошибка')
try:
for y in 234:
pass
except:
print('Так не надо, Ошибка')
Так не надо, Ошибка Так не надо, Ошибка
Но можно перебирать строки
, множества
, словари
, numpy-массивы
, pandas.DataFrames
и т.д.
# перебор элементов множества
A = {1, 2, 4, 5, 6}
for elem in A:
print(elem)
1 2 4 5 6
ss = 'My wonderful string!'
for s in ss:
print(s, end='*')
M*y* *w*o*n*d*e*r*f*u*l* *s*t*r*i*n*g*!*
В случае словарей, у которых элемент последовательности состоит не только из значения, а из связки "ключ:значение", цикл for будет перебирать ключи. Чтобы узнать значения, соответствующие этим ключам, необходимо обратиться к словарю по ключу, используя квадратные скобки [].
Так как словарь является неупорядоченной последовательностью, т.е. в нём нет строго правила следования элементов друг за другом, то и порядок возвращаемых ключей/значений не соответствует порядку их ввода при составлении словаря.
c = {'one':1, 'two':2, 'three': 'long'}
# Перебор ключей, значений словаря c
print('I')
for i in c:
# i - ключи!
print(i, c[i])
# Другой вариант
print('II')
for key in c.keys():
print(key, c[key])
I one 1 two 2 three long II one 1 two 2 three long
Одной из самых полезных функций при работе с циклами является встроенная функция enumerate(). Она применяется к последовательности и позволяет перебрать не только сами элементы последовательности, но и их индексы. Для этого указывается не одна переменная цикла (i в примере выше), а две, указанные через запятую: index, value.
enumerate() - функция, которая возвращает на каждом шаге цикла индекс и значение элемента последовательности, которая перебирается в цикле
c = ['windows', 'linux', 'mac', 'sun', 'unix']
for i in c:
print(i, end=' ')
for i, elem in enumerate(c):
print(i, elem)
windows linux mac sun unix 0 windows 1 linux 2 mac 3 sun 4 unix
c = ['windows', 'linux', 'mac', 'sun', 'unix']
for val in enumerate(c):
print(val[0], val[1])
print('Val', type(val))
0 windows 1 linux 2 mac 3 sun 4 unix <class 'tuple'>
В неупорядоченных последовательностях, таких как множества, а также в словарях, функция enumerate выводит какие-то индексы, согласно выводу элементов из этих коллекций (так тоже можно назвать последовательности).
A = {1, 7, 4, 5, 5, 6, -1, False}
for i, elem in enumerate(A):
print(i, elem)
print('<---------------->')
dic = {'three': 'long', 'one':1, 'two':2, }
for i, key in enumerate(dic):
print(i, key)
0 False 1 1 2 4 3 5 4 6 5 7 6 -1 <----------------> 0 three 1 one 2 two
Ещё одной полезной встроенной функцией при работе с циклами является функция zip().
zip() - объединяет в кортежи элементы из последовательностей, переданных в качестве аргументов. Если последовательности имеют разные длины, то объединение произойдёт по минимуму из данных последовательностйей.
Это один из самых простых способов соединить несколько списков-векторов в матрично подобную стуктуру.
import random
N = 15
a = list(range(1950, 1950+N, 2)) # длина 8
b = list(range(N)) # длина 15
c = [random.randint(-9, 37) for i in range(N)] # длина 15
e = zip(a, b, c) # длина 8
print(type(e))
sar = list(e)
print(type(sar), len(sar), sar[1][:])
for year, val, val2 in sar:
print(year, val, val2)
<class 'zip'> <class 'list'> 8 (1952, 1, 8) 1950 0 -5 1952 1 8 1954 2 14 1956 3 22 1958 4 23 1960 5 26 1962 6 -1 1964 7 11
Как видно, цикл for позволяет работать не только с одной переменной (вспомним, что она есть ссылка на объект), а с несколькими переменными на каждом шаге (итерации) цикла. Поясним это на примере вложенных списков.
a = ['Tom', 'Jerry', 'DOG']
b = list(range(3))
c = (True, False, True)
ilist = [a, b, c] # вложенный список: список, состящий из списков
for *i, j in [a, b, c]: # здесь i - число, строка или булева переменная
print(i, '-', j)
# print(k)
for i, j, k in zip(a, b, c):
print(i, j, k)
print(i, len(i))
#print('*'*20)
#for i in ilist: # здесь i - список
# print(type(i), i)
['Tom', 'Jerry'] - DOG [0, 1] - 2 [True, False] - True Tom 0 True Jerry 1 False DOG 2 True DOG 3
Бесконечный цикл while в python имеет следующую конструкцию:
**while(<выражение>):**
**....тело цикла**
На каждом шаге цикла выражение, стоящее в скобках, проверяется на равенство истине (True). Если оно истинно, то совершается ещё один шаг, ещё одна итерация цикла. Бесконечным такой цикл делает выражение, которое всегда соответствует истине True на любом шаге такого цикла.
Обычно в качестве выражения в такой цикл помещают логическое условие: например, a > 10 (a > b; i < 10; d == 10). Но это может быть и просто переменная (ведь любой тип данных можно привести к логическому типу и сравнить на равенство Истине True).
Соответственно, чтобы цикл не превратился в бесконечный, выражение цикла не должно быть константой, а некоторой переменной, управляемой внутри цикла. Так в случае выражения-условия a > 10 в теле цикла (участок кода, который охватывает цикл) значение переменной "a" должно изменяться и при определённых обстоятельствах стать меньше 10. Тогда такой код будет корректен, цикл не будет выполняться бесконечно, а интерпретатор покинет цикл в момент прекращения выполнения условия.
Поэтому цикл while также называют циклом с условием.
a = 10
while a > 5:
print(a)
a = a - 1
# a -= 1
10 9 8 7 6
Формально, цикл может состоять из операторов for или while и тела цикла (пустого в самом простом виде). Обе конструкции могут быть дополнены необязательными функциями break и continue
Чтобы выйти из любого цикла в определённый момент (даже если выражение цикла while истинно) существует оператор break. Как только он выполняется, программа покидает текущий цикл и переходит в вышестоящую управляющую структуру (для одиночного цикла, это скорее всего возврат в основной поток исполнения программы; для вложенного цикла - возврат на итерацию вышерасположенного цикла и т.д.).
В противоположность функции break, функция continue которая позволяет не заканчивая всё тело цикла, перейти к следующей итерации цикла. Весь код, расположенный "ниже" строки с вызовом continue по порядку, будет игнорирован.
Хотя такие конструкции и затрудняют разбор кода, они полезны, так как помогают прекратить выполнение ненужных участков тела цикла или итераций (а это время исполнения кода + экономия памяти).
Break и continue чаще всего встречаются в блоках условных операторов (if-elif-else).
# Пример проверки случ. значения r больше данного порогового значения TR
import random # импорт модуля для работы со случайными числами
TR = 0.7
t = 0
while True:
t += 2
r = random.random() # генерация псевдослучайного числа
if r >= TR:
print('I. Succsess after', t, 'iterations')
break
I. Succsess after 2 iterations
# Пример замены нечётных элементов случайного списка целых чисел
# на invalid значения, а также запись делящихся нацело на 7
# значений в список spam
N = 100
invalid = -999
box = [random.randint(0, 200) for i in range(N)]
spam = []
print(box[:20])
for i, val in enumerate(box):
if val % 2 == 0:
box[i] = invalid
continue
# print(lst[i])
if val % 7 == 0:
spam.append(i)
box[i] = val*val
print('Number of invalid values: {}'.format(box.count(invalid)))
print('Spam list - 7th zero: ', spam)
print('Powered: ', box[:20])
[101, 112, 7, 153, 9, 144, 196, 71, 169, 120, 117, 126, 135, 112, 189, 120, 20, 33, 187, 155] Number of invalid values: 46 Spam list - 7th zero: [2, 14, 56, 63, 65] Powered: [10201, -999, 49, 23409, 81, -999, -999, 5041, 28561, -999, 13689, -999, 18225, -999, 35721, -999, -999, 1089, 34969, 24025]
import calendar
years = range(1955, 2015)
dates = []
for y in years: # верхний цикл 1
for m in range(1, 13): # средний цикл 2
mon = calendar.month_abbr[m]
weeksNum, days = calendar.monthrange(y, m)
for d in range(1, days+1): # самый вложенный цикл 3
if (y % 5 == 0) and (m % 6 == 0) and (d == 18):
print(d, mon, y)
s = '{}-{}-{}'.format(d, mon, y)
dates.append(s)
print(dates[-5:])
18 Jun 1955 18 Dec 1955 18 Jun 1960 18 Dec 1960 18 Jun 1965 18 Dec 1965 18 Jun 1970 18 Dec 1970 18 Jun 1975 18 Dec 1975 18 Jun 1980 18 Dec 1980 18 Jun 1985 18 Dec 1985 18 Jun 1990 18 Dec 1990 18 Jun 1995 18 Dec 1995 18 Jun 2000 18 Dec 2000 18 Jun 2005 18 Dec 2005 18 Jun 2010 18 Dec 2010 ['27-Dec-2014', '28-Dec-2014', '29-Dec-2014', '30-Dec-2014', '31-Dec-2014']
Вложенные циклы - слабое место python. Будучи интерпретируемым языком, выполнение множества вложенных повторяющихся участков кода оказывается тяжёлой задачей. В случае не сложных или слабо нагруженных задач, это может быть незаметно. Однако нужно понимать, что множество вложеннных циклов - это потенциально слабейшее место для времении исполнения скрипта.