Операторы потока управления
Contents
Операторы потока управления#
В этой главе мы познакомимся с условным ветвлением и циклами в python
.
Об отступах#
Как и во многих других языках программирования исходный код python
исполняется сверху вниз, но операторы потока управления (такие, как условное ветвление и циклы) позволяют проектировать менее линейные программы. Для того чтобы научиться применять такие операторы в python
, необходимо держать в голове, что отступы (indent
) в python
имеют значение.
Во многих языках программирования отступы нередко используются для обозначения программных блоков, например, тела функции, цикла или условного оператора. Ниже приводится два варианта одной и той же функции для вычисления суммы положительных элементов массива на языке C/C++
, но во втором из них расставлены отступы, а в первом — нет.
Без отступов
double sum_positive(double* a, int n) {
double s = 0;
for (int i = 0; i < n; ++i) {
if (a[i] > 0)
s += a[i];
}
double s;
}
С отступами
double sum_positive(double* a, int n) {
double s = 0;
for (int i = 0; i < n; ++i) {
if (a[i] > 0)
s += a[i];
}
double s;
}
Отступы не влияют на ход исполнения программы в C/C++
и с точки зрения компилятора эти две функции абсолютно эквивалентны, но хороший программист расставляет их в любом случае. Это позволяет проще ориентироваться в программе и значительно повышает её читабельность. Например, к какому if
относится else
в следующем примере?
if(x > 0)
if(y > 0)
std::cout << "if";
else
std::cout << "else";
При первом взгляде, кажется, что блок else
должен выполниться, если x <= 0
, т.е. else
относится к внешнему оператору if
. На самом деле это не так! Если расставлять отступы корректно, то такой путаницы будет гораздо меньше.
if(x > 0)
if(y > 0)
std::cout << "if";
else
std::cout << "else";
Если в C/C++
отступы оставляются на усмотрение программиста, то в python
они являются обязательными. Создатель python
Гвидо Ван Россум при проектировании языка решил вынудить программистов расставлять отступы правильно. Не следующий этим требованиям код, не запустится вообще или произведет не те действия, которые от него ожидал программист.
Составные операторы в python
#
Операторы потока управления в python
являются составными. Каждый такой оператор состоит из заголовка и тела (блок инструкций). Во многих других языках программирования для обозначения тела таких операторов используются скобки. Например, в C/C++
за это отвечают фигурные скобки. В python
для этих целей используется отступ: инструкции в теле составного оператора вносятся с одинаковым дополнительным отступом вправо относительно заголовка этого оператора.
инструкция снаружи составного оператора
заголовок составного оператора:
первая инструкция внутри составного оператора
вторая инструкция внутри составного оператора
...
последняя инструкция внутри составного оператора
инструкция снаружи составного оператора
Note
В конце заголовка составного оператора обязательно ставится символ двоеточия “:
”.
Интерпретатор python
понимает, что относится к телу составного оператора, на основе отступов.
Пока он встречает постоянный отступ вправо после заголовка составного оператора, он считает, что эти инструкции относятся к нему.
Как только отступ возвращается на прежний уровень, интерпретатор понимает, что он покинул тело составного оператора.
Note
В python
нет требований к тому, какого конкретно размера должен быть этот отступ. Важно лишь, чтобы он был постоянен в пределах каждого составного оператора. Чаще всего в качестве отступа используют 4 пробела.
Условное ветвление. if
#
Начнем с оператора условного ветвления. Ниже представлен код C/C++
и python
, который решает задачу fizz buzz.
C/C++
if (i % 15 == 0)
std::cout << "FizzBuzz, ";
else if (i % 3 == 0)
std::cout << "Fizz, ";
else if (i % 5 == 0)
std::cout << "Buzz, ";
else
std::cout << i << ", ";
python
if i % 15 == 0:
print('FizzBuzz, ')
elif i % 3 == 0:
print('Fizz, ')
elif i % 5 == 0:
print('Buzz, ')
else:
print(f'{i}, ')
Заголовок оператора условного состоит из ключевого слова
if
и условия. Синтаксическая разница сC/C++
заключается в том, чтоусловие необязательно помещать (и обычно не помещают) в круглые скобки;
после условия обязательно ставить двоеточие.
После заголовка следует тело, т.е. блок инструкций, который выполняется если условие истинно. Синтаксическая разница с
C/C++
заключается в том, чтов
c++
для обозначения этого блока используются фигурные скобки, отступ этого блока расставляется программистом по его усмотрению;в
python
для обозначения этого блока обязательно используется отступ.
После тела опционально можно использовать ключевые слова
else
и/илиelif
, последнее из которых является сокращением отelse if
. После каждого из таких опциональных ветвленийelse
/elif
должен следовать свой блок инструкций, который также обозначается отступом вправо.
Итого, оператор условного ветвление имеет приблизительно следующий вид:
if условие:
первая инструкция блока if
...
последняя инструкция блока if
elif условие:
первая инструкция блока elif
...
последняя инструкция блока elif
else:
первая инструкция блока else
...
последняя инструкция блока else
Note
Блоков elif
может быть произвольное количество, в том числе и ни одного.
Если вычисление выражения после ключевого слова if
или elif
даёт True
, то соответствующий блок инструкций выполняется, если False
, то этот блок инструкций игнорируется и выполняется блок инструкций else
, если таковой указан. Если вычисление этого выражение даёт небулевое значение, то python
пытается его к таковому привести. Так, например, нулевые числа любого типа приводятся к False
, а все остальные к True
, что позволяет проверять на нулевое значение следующим образом.
from random import randint
random_value = randint(0, 1) # 0 или 1
print(f"Результат генерации: {random_value}")
if random_value == 1:
print("Орёл")
else:
print("Решка")
# или так
if random_value:
print("Орёл")
else:
print("Решка")
Результат генерации: 0
Решка
Решка
При этом в python
второй вариант считается предпочтительнее.
Пустые коллекции приводятся к False
, а все непустые к True
.
l = []
if l:
print("Список не пуст")
else:
print("Список пуст")
Список пуст
Составные условия можно записывать с выражениями, включающими логические операции and
и or
.
В выражении
x and y
сначала вычисляется значениеx
и только если оно истинно (True
), то вычисляетсяy
;В выражении
x or y
сначала вычисляется значениеx
и только если оно ложно (False
), то вычисляетсяy
;
Это позволяет записать выражения вида:
if x != 0 and y / x > 0:
print("do something")
или
if x == 0 or y / x > 0:
print("do something")
Note
Скобки не обязательны, т.к. приоритет and
и or
ниже, чем у >
, !=
и ==
.
Цикл while
#
Пример цикла while
в C/C++
.
int i = 1;
while (i < 1000){
std::cout << i;
i *= 2;
}
В python
тот же самый эффект достигается следующим кодом.
i = 1
while i < 1000:
print(i)
i *= 2
Цикл while
тоже является составным оператором из заголовка вида while условие:
и тела цикла. На каждой итерации цикла условие проверяется заново, и если оно истинно, то выполняются инструкции в теле оператора, которое обозначается постоянным отступом вправо.
while условие:
первая инструкция тела цикла
...
последняя инструкция тела цикла
Note
В python
нет аналогам операторам инкремента ++
и декремента --
, зато все остальные операторы +=
, *=
, /=
дают схожий эффект.
Ключевые слова break
и continue
работают точно также, как и в C/C++
:
break
немедленно покидает цикл;continue
прекращает исполнение тела цикла на данной итерации и сразу заходит на следующую.
i = 0
while True:
i += 1
if i == 4:
print("skip", end=", ")
continue
elif i == 8:
print("end")
break
print(i, end=", ")
1, 2, 3, skip, 5, 6, 7, end
Цикл for
#
Цикл for
в python
совсем не похож на обычный цикл for
в C/C++
, но имеет много сходства с добавленным в C++11
range-based for.
Убедиться в этом можно сравнив следующее.
C/C++
код доступный по ссылке выше:
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v) // compiler uses type inference to determine the right type
std::cout << i << ' ';
и аналогичный код на python:
v = [0, 1, 2, 3, 4, 5]
for i in v:
print(i, end=" ")
0 1 2 3 4 5
Цикл for
больше похож на цикл foreach
, т.к. для его работы необходим итерируемый объект, по элементам которого пробегается цикл. В примере выше в качестве такого объекта выступает список v
.
Цикл for
тоже является составным оператором, который имеет приблизительно следующий вид.
for переменная in итерируемый_объект:
первая инструкция тела цикла
...
последняя инструкция тела цикла
Цикл for
пробегается по элементам итерируемого объекта, указанного после ключевого слова in
в заголовке цикла и перед двоеточием. При этом на каждой итерации текущий элемент доступен под именем, указанным в заголовке цикла после ключевого слова for
и перед ключевым словом in
.
Диапазон. range
#
Рассмотрим другой пример итерируемого объекта в python
, который чаще всего используется для итерации по нему в цикле for
.
Очень распространен случай, когда необходимо выполнить какие-то действия несколько раз. Для этих и некоторых других целей в python
есть тип range
(диапазоны).
Один аргумент#
range(n)
создаёт последовательность целых чисел от 0 включая до n
не включая.
for i in range(5):
print(i, end=" ")
0 1 2 3 4
Это позволяет, например, заменить все циклы из C/C++
вида
for(int i=0; i<n; i++){
...
}
на циклы в python
вида
for i in range(n):
...
Два аргумента#
range(start, stop)
- диапазон целых чисел от start
включая до stop
не включая.
for i in range(-4, 5):
print(i, end=" ")
-4 -3 -2 -1 0 1 2 3 4
Это позволяет, например, заменить все циклы из C/C++
вида
for(int i=start; i<stop; i++){
...
}
на циклы в python
вида
for i in range(start, stop):
...
Три аргумента.#
range(start, stop, step)
- то же, что и range(start, stop)
, но с шагом в step
.
for i in range(0, 10, 2):
print(i, end=" ")
0 2 4 6 8
Это позволяет, например, заменить все циклы из C/C++
вида
for(int start=0; i<stop; i+=step){
...
}
на циклы в python
вида
for i in range(start, stop, step):
...
Note
Особенность range
заключается в том, что числа выдаются ленивым образом, т.е. последовательность чисел не вычисляется сразу целиком, а элементы этой последовательности вычисляются только тогда, когда они необходимы.
Коротко про функции#
Объявление функции тоже составной оператор, который имеет приблизительно следующий вид.
def имя_функции(первый_параметр, ..., последний_параметр):
первая инструкция в теле функции
...
последняя инструкция в теле функции
Рассмотрим пример простой функции, считающий сумму квадратов её двух аргументов.
def sum_of_squares(x, y):
square_x = x ** 2
square_y = y ** 2
return square_x + square_y
Здесь
sum_of_squares
— имя функции,x
иy
— имена её параметров,square_x
иsquare_y
— локальные переменные функции, которые снаружи функции не видны.
При вызове функции результат вычисления выражения справа от return
возвращается наружу. Если ключевого слова return
не встречается, то возвращается значение None
.
print(sum_of_squares(3, 4))
25
Практика#
Максимальное значение списка#
В качестве упражнения напишите функцию my_max
, которая вычисляет максимум своего аргумента.
def my_max(array):
return None
print(my_max([1, 10, 50, 200]))
print(my_max([200, 50, 10, 1]))
print(my_max([]))
Вычисление максимума в `python`
Примените её к списку символов
print(my_max(['a', 'b', 'A', 'B']))
Сравните результат её работы с встроенной функцией
max()
print(max([1, 10, 50, 200])) print(max([200, 50, 10, 1])) print(max([]))
Note
Функция max()
возвращает максимальный элемент списка (любого итерируемого объекта) или наибольшее значение из двух и более аргументов.
Функция min()
работает аналогично.