4.1. Теория¶
При составлении алгоритма и написании программы возникают ситуации, когда часть действий необходимо выполнить при каком-либо условии или повторить (несколько раз или также при условии). В высокоуровневых языках программирования для этого предназначены специальные конструкции:
ветвление (условная конструкция);
цикл - конструкция, предназначенная для организации многократного исполнения набора инструкций.
В языке Python ветвление и цикл поддерживаются операторами:
Содержание
4.1.1. Условный оператор¶
Оператор if позволяет выполнять часть программы при наступлении определенного условия.
-
if
¶ if logical_expression_1: # (if строго 1) условие для проверки suite_1 # блок команд для выполнения, если условие истинно elif logical_expression_2: # (elif - 0 или несколько) дополнительные условия suite_2 # проверка идет, если условия выше ложны elif logical_expression_N: suite_N else: # (else - 0 или 1) блок команд для выполнения, else_suite # если все условия выше оказались ложными
Ход выполнения:
каждое из условий
logical_expression_N
по очереди проверяется на истинность; условия - выражения типаbool
, например,x > 2
,a > 5 and b < 7
и т.д.;как только истинное условие найдено, выполняется соответствующий блок
suite_N
, после чего осуществляется выход из всей условной конструкции (прочие варианты не проверяются и не выполняются);если ни одно из условий не истинно («не срабатывает»), выполняется блок
else
(при наличии).
Для небольших условий возможно использование специального сокращенного варианта:
expression_1 if logical_expression else expression_2
Пример использования условного оператора приведен в Листинге 4.1.1.
# Покупка в аптеке с подсчетом стоимости в зависимости от:
# - наличия социальной карты студента;
# - количества товара.
a_count = int(input("Сколько аскорбинок хотите купить? "))
has_social_discount = input("Есть ли у Вас социальная карта? ").upper() in ("1", "Y", "ДА")
price = 15.35 # Цена 1 аскорбинки
total_base = price * a_count # Первоначальная стоимость
total = total_base # Общая стоимость после применения скидок
print("\nЧек")
print('-' * 5)
if 0 < a_count <= 5:
# Продаем по обычной цене
print("Продаем по обычной цене")
elif a_count < 10:
# До 10 аскорбинок даем скидку в 5%
total *= 0.95
print("Вам положена скидка в 5%!")
else:
# Если больше 10 - каждая 10-я аскорбинка - бесплатно!
free_count = a_count // 10
total -= free_count * price
print("Для Вас каждая 10-я аскорбинка будет бесплатной!")
# Если есть соц. карта - делаем скидку в 10% и отбрасываем копейки
if has_social_discount:
total = int(total * 0.9)
# Форматная строка для "красивого" вывода чека
BILL_ITEM_FORMAT = "{message:<15} {value:>8.2f} р."
print("Соц. карта:", "Да" if has_social_discount else "Нет")
print(BILL_ITEM_FORMAT.format(message="Сумма покупки", value=total_base))
print(BILL_ITEM_FORMAT.format(message="Скидка", value=total_base - total))
print(BILL_ITEM_FORMAT.format(message="Итого", value=total))
# -------------
# Пример вывода:
# Сколько аскорбинок хотите купить? 7
# Есть ли у Вас социальная карта? Да
#
# Чек
# -----
# Вам положена скидка в 5%!
# Соц. карта: Да
# Сумма покупки 91.00 р.
# Скидка 16.45 р.
# Итого 91.00 р.
Примечание
При тестировании программ с условной конструкцией необходимо проверять все варианты выполнения программы, запуская ее несколько раз с входными данными, подходящими под разные случаи.
4.1.2. Циклы¶
4.1.2.1. Цикл с условием¶
Цикл с условием представлен в Python оператором while.
-
while
¶ while logical_expression: # Если условие истинно, выполняется 'while_suite' while_suite # После выполнения, происходит возврат к проверке else: # (else - 0 или 1) else_suite # Выполняется, если не было прерывания цикла
Особенности:
блок команд
while_suite
выполняется покаlogical_expression
истинно;блок
else
необязателен, а его содержимое (блокelse_suite
) выполняется только, если не было прерывания цикла.
Цикл с условием используется, когда количество итераций неизвестно, однако известно условие его окончания.
Пример использования цикла while
приведен в Листинге 4.1.2.
# Использование цикла с условием для охлаждения напитка
#
# Считаем, что напиток холодный при температуре <= 10
TEMP_COLD_MAX = 10
temp = 20
while temp > TEMP_COLD_MAX:
print("Кола при температуре {}°C - так себе! ".format(temp) +
"Кладу кусочек льда...")
temp -= 1
print("Отлично, при {}°C можно пить!".format(temp))
# -------------
# Пример вывода:
# Кола при температуре 20°C - так себе! Кладу кусочек льда...
# Кола при температуре 19°C - так себе! Кладу кусочек льда...
# Кола при температуре 18°C - так себе! Кладу кусочек льда...
# Кола при температуре 17°C - так себе! Кладу кусочек льда...
# Кола при температуре 16°C - так себе! Кладу кусочек льда...
# Кола при температуре 15°C - так себе! Кладу кусочек льда...
# Кола при температуре 14°C - так себе! Кладу кусочек льда...
# Кола при температуре 13°C - так себе! Кладу кусочек льда...
# Кола при температуре 12°C - так себе! Кладу кусочек льда...
# Кола при температуре 11°C - так себе! Кладу кусочек льда...
# Отлично, при 10°C можно пить!
Пример использования ключевого слова else
приводится в разделе Прерывание и продолжение циклов.
Примечание
Бесконечный цикл
При составлении программы с циклом while
необходимо проверять, чтобы в конечном итоге проверяемое условие становилось ложным и цикл завершался; в противном случае он будет бесконечным.
4.1.2.2. Совместный цикл (цикл по коллекциям)¶
Совместный цикл представлен в Python циклом for.
-
for
¶ for expression in iterable: # Для каждого элемента 'expression' из 'iterable' for_suite # выполняется 'for_suite' else: # (else - 0 или 1) else_suite # Выполняется, если не было прерывания цикла
Особенности:
в качестве
iterable
может использоваться любой итерируемый объект (список, словарь и др.);блок команд
for_suite
выполняется для каждого элементаexpression
изiterable
; при этом внутри блокаfor_suite
expression
содержит ссылку на текущий просматриваемый элемент;блок
else
необязателен; при наличии выполняется блокelse_suite
, если не было прерывания цикла.
Пример использования for
приведен в Листинге 4.1.3.
# 1. Простое перемещение по коллекции (списку)
#
# Выводим на экран наименование языка программирования из списка и его длину
langs = ['С++', 'Java', 'Python']
for lang in langs:
print(lang, len(lang))
print()
# 2. В Python в явном виде отсутствует привычный для Паскаля или Си
# цикл со счетчиком. Однако данный тип цикла может быть реализован как
# for ... in с перемещением по числовому диапазону range(...)
#
# Выводим на экран числа от 1 до 10
for i in range(10):
print(i + 1, end=" ")
# -------------
# Пример вывода:
# С++ 3
# Java 4
# Python 6
#
# 1 2 3 4 5 6 7 8 9 10
Пример использования ключевого слова else
приводится в разделе Прерывание и продолжение циклов.
4.1.2.2.1. Итераторы¶
Универсальность цикла for
в виде возможности перемещения по различным коллекциям основана на использовании итераторов - специальных интерфейсных объектов, предоставляющих навигацию по другим (итерируемым) объектам.
Общая схема взаимодействия коллекции и итератора следующая:
коллекция и итератор взаимно ссылаются друг на друга через специальный метод
__iter__()
;итератор предоставляет дополнительный метод
__next__()
для получения следующего элемента привязанной коллекции (Рисунок 4.1.1).
Итераторы, реализованные по-разному для различных классов (например, для кортежа и словаря) позволяют циклу for
перемещаться по коллекциям, не заботясь о внутренней структуре объектов, а используя методы __iter__()
и __next__()
. Вызов __next__()
завершается возбуждением исключения StopIteration
, означающим, что элементов в коллекции больше нет (Листинг 4.1.4).
# 1. Явное использование итератора для списка
>>> my_list = [1, 2, 3]
>>> it = my_list.__iter__()
>>> it
<list_iterator object at 0x000001E8E20BAB00>
>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> b.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
# 2. Явное использование итератора для словаря
>>> my_dict = dict(name="Саша", age=20, weight=65.6)
>>> it = my_dict.__iter__()
>>> it
<dict_keyiterator object at 0x000001E8E20B04F8>
>>> it.__next__()
'age'
>>> it.__next__()
'name'
>>>
>>> it.__next__()
'weight'
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Итерируемые объекты также поддерживаются рядом полезных функций.
-
all
(iterable)¶ Возвращает
True
, если все элементыiterable
в логическом контексте оцениваются какTrue
.
-
any
(iterable)¶ Возвращает
True
, если хотя бы 1 элементiterable
в логическом контексте оцениваются какTrue
.
-
enumerate
(iterable, start=0)¶ Возвращает итератор, где каждый элемент является парой «номер» - «значение». Номер отсчитывается от
start
. Обычно используется в циклахfor
, чтобы получить последовательность кортежей(номер, элемент)
.
-
sorted
(iterable, key=None, reverse=False)¶ Возвращает отсортированный объект в виде списка для итерируемого объекта
iterable
.- Параметры
key – функция сортировки (по умолчанию не учитывается, сортировка осуществляется поэлементно);
reverse – если равен
True
, сортировка осуществляется в обратном порядке.
-
reversed
(iterable)¶ Возвращает итератор, возвращающий элементы в обратном для исходного
iterable
порядке.
Пример использования нескольких функций приведен в Листинге 4.1.5.
# Использование специальных функций для итерируемых объектов
>>> fruits = ["апельсины", "ябоки", "мандарины", ""]
>>> any(fruits)
True
>>> all(fruits) # False (пустая строка == False)
False
>>> list(reversed(fruits))
['', 'мандарины', 'ябоки', 'апельсины']
>>> list(enumerate(fruits, start=1))
[(1, 'апельсины'), (2, 'ябоки'), (3, 'мандарины'), (4, '')]
4.1.2.2.2. Некоторые техники перемещения по коллекциям¶
Python предоставляет широкие возможности по организации циклов. В Листинге 4.1.5 приведены некоторые часто используемые случаи с различными типами коллекций.
# 1. Последовательности
# Используя enumerate(), можно "дать" порядковый номер элементу коллекции в цикле
for i, item in enumerate(['камень', 'ножницы', 'бумага']):
print(i, item)
# Вывод:
# ------
# 0 камень
# 1 ножницы
# 2 бумага
# Используя sorted(), можно вывести элементы коллекции в порядке возрастания
for item in sorted(['камень', 'ножницы', 'бумага']):
print(item)
# Вывод:
# ------
# бумага
# камень
# ножницы
# Цикл со счетчиком в обратную сторону можно организовать, используя
# reversed() или range() с параметрами
# Следующие 2 цикла выводят одинаковые значения
for i in reversed(range(10)):
print(i) # Числа от 9 до 0 на отдельной строке
for i in range(9, 0, -1):
print(i) # Числа от 9 до 0 на отдельной строке
# 2. Словари
sportsman = {"фио": "Федоров Сергей Викторович",
"вид_спорта": "хоккей",
"дата_рождения": "13.12.1968"}
# По умолчанию цикл for перемещается по ключам
# enumerate() и sorted() аналогично работают только с ключами
for attr in sorted(sportsman):
print(attr) # Получить значение по ключу - sportsman[attr]
# Вывод:
# ------
# вид_спорта
# дата_рождения
# фио
# Используя items() можно сразу получать пару ключ-значение
for attr, value in sportsman.items():
print(attr, ":", value)
# Вывод:
# ------
# фио : Федоров Сергей Викторович
# вид_спорта : хоккей
# дата_рождения : 13.12.1968
4.1.2.3. Прерывание и продолжение циклов¶
-
break, continue
Любая циклическая конструкция может быть немедленно прервана или продолжена (осуществлен переход к проверке условия или следующей итерации в циклах while
и for
соответственно).
Прерывание цикла может произойти при:
наличии команды
break
: приводит к выходу из цикла (только из того, внутри которого она написана);наличии команды
return
: выход из функции (и из цикла, соответственно, Тема №5);возникновении исключения (Тема №7).
Не зависимо от способа прерывания цикла, механизм действует одинаково - выполнение цикла прерывается, дополнительная часть цикла else
не выполняется и осуществляется выход за пределы цикла.
Для продолжения цикла используется команда continue
, передающая управление в начало цикла для выполнения следующей проверки или итерации.
Примеры прерывания и продолжения цикла приведены в Листингах 4.1.7 и 4.1.8.
break
в Python (для цикла while
механизм аналогичен) | скачать
¶# Прерывание цикла может оказаться полезным, если задача решена и продолжение цикла не нужно
#
# Ищем первое четное число в списке
# При нахождении имеет смысл выйти из цикла и не просматривать его дальше впустую
nums = [1, 6, 8, 2, 9, 3, 5]
for x in nums:
if x % 2 == 0:
print("Число найдено: {}".format(x))
break
else:
print("Четное число не найдено!")
# -------------
# Пример вывода:
# Число найдено: 6
continue
в Python (для цикла while
механизм аналогичен) | скачать
¶# Прерывание цикла может оказаться полезным, если задача решена и продолжение цикла не нужно
#
# Запрашиваем у пользователя 5 целых чисел и считаем сумму четных
# Данную задачу можно также решить и выполняя сложение при условии без continue
sum_x = 0
for i in range(5):
x = int(input("Введите число №{}: ".format(i + 1)))
if x % 2 != 0:
continue
sum_x += x
print("Сумма =", sum_x)
# -------------
# Пример вывода:
# Введите число №1: 1
# Введите число №2: 2
# Введите число №3: 3
# Введите число №4: 4
# Введите число №5: 5
# Сумма = 6
4.1.3. Комбинация циклов и условий¶
В соответствии с методологией структурного программирование любая программа строится из трех базовых управляющих структур: последовательность, ветвление и цикл.
Циклические конструкции можно произвольно комбинировать для достижения нужного эффекта - тем самым создавая вложенные друг в друга циклы; 2 цикла называются внешним и внутренним (или вложенным) соответственно, когда второй находится внутри первого. Количество уровней вложенности, как правило, не ограничивается.
В Листингах 4.1.9 и 4.1.10 приведены примеры вложенности циклов и комбинации циклов и условий.
# Дан список чисел, найти НОД каждого из них с введенным числом
nums = [12, 86, 44, 24, 73, 19]
print(nums)
x = int(input("Введите второе число для поиска НОД: "))
for num in nums:
# Поиск НОД по алгоритму Евклида https://ru.wikipedia.org/wiki/Алгоритм_Евклида
temp1, temp2 = num, x
while temp1 != temp2:
if temp1 < temp2:
temp2 -= temp1
else:
temp1 -= temp2
print("Числа {} и {}, НОД = {}".format(num, x, temp1))
# -------------
# Пример вывода:
# [12, 86, 44, 24, 73, 19]
# Введите второе число для поиска НОД: 12
# Числа 12 и 12, НОД = 12
# Числа 86 и 12, НОД = 2
# Числа 44 и 12, НОД = 4
# Числа 24 и 12, НОД = 12
# Числа 73 и 12, НОД = 1
# Числа 19 и 12, НОД = 1
# Поиск простых чисел от 1 до 10
#
# Внешний цикл отвечает за перебор чисел от 1 до 10
# Внутренний цикл подсчитывает количество делителей, перебирая
# все числа от 2 до текущего числа - 1
#
# Данная задача выглядит проще, если решить ее с использованием 2 циклов for,
# данный вариант приведен для демонстрации вложенности разных видов циклов
for i in range(1, 11):
divisors = 0
j = 2
# Внутренний цикл выполняется пока не переберутся все числа [2; i] или
# не найдется хотя бы 1 делитель
while j < i and divisors == 0:
if i % j == 0:
divisors += 1
j += 1
# Если делители для [i] не найдены - это простое число
if divisors == 0:
print(i, end=" ")
# -------------
# Пример вывода:
# 1 2 3 5 7
При написании вложенных циклов необходимо помнить:
внешний цикл выполняется реже или «медленнее», чем внутренний - при каждом отдельном значении внешнего цикла внутренний выполняется полностью;
операторы
break
иcontinue
действуют для того цикла, в котором написаны.
4.1.4. Коллекционные включения¶
Python поддерживает специальный компактный синтаксис создания коллекций, называемый коллекционным включением (comprehensions).
Общий вид (на примере списков):
# Просто коллекционное включение:
# "Составить список из 'expression' для каждого
# элемента 'item' в итерируемом объекте 'iterable'"
[expression for item in iterable]
# Коллекционное включение с условием:
# "Составить список из 'expression' для каждого
# элемента 'item' в итерируемом объекте 'iterable'
# есдт соблюдается условие 'condition'"
[expression for item in iterable if condition]
В Листинге 4.1.11 приведен пример компактного создания отдельных коллекций.
# Коллекционные включения позволяют эффективнее создавать коллекции
# 1. Формирование списка чисел от 1 до 100, оканчивающихся на 1
# Стандартный способ
nums = tuple(range(100))
lst = []
for i in nums:
if i % 10 == 1:
lst.append(i)
# Списковое включение
lst2 = [i for i in nums if i % 10 == 1]
print(lst == lst2) # True
# 2. В Python коллекционные включения также доступны и для других типов
# Словарь
# Определяем слова и их длину
words = "Без труда не вытащишь и рыбку из пруда"
stats = {w.lower(): len(w) for w in words.split()}
print(stats) # {'вытащишь': 8, 'рыбку': 5, 'не': 2, 'пруда': 5,
# 'и': 1, 'из': 2, 'без': 3, 'труда': 5}
В коллекционных включениях также возможно использовать вложенные циклы for
. В целом, использование коллекционных включений рекомендуется в случаях, где они упрощают код, а не усложняют его чтение.
- 1
Sebesta, W.S Concepts of Programming languages. 10E; ISBN 978-0133943023.
- 2
Python - официальный сайт. URL: https://www.python.org/.
- 3
Python - FAQ. URL: https://docs.python.org/3/faq/programming.html.
- 4
Саммерфилд М. Программирование на Python 3. Подробное руководство. — М.: Символ-Плюс, 2009. — 608 с.: ISBN: 978-5-93286-161-5.
- 5
Лучано Рамальо. Python. К вершинам мастерства. — М.: ДМК Пресс , 2016. — 768 с.: ISBN: 978-5-97060-384-0, 978-1-491-94600-8.
- 6
Итератор и итерируемый объект. URL: http://nvie.com/img/iterable-vs-iterator.png.