Перейти к основному содержимому

Управляющие конструкции - if, for, while

Разработчику Архитектору

Перед чтением: Операторы — общие понятия оператора, операнда, приоритетов и типов операций без привязки к языку.

Сначала: Циклы в коде — общая идея повторений, виды циклов и типичные ошибки без привязки к синтаксису языка.


Python — if, for, while

Ветвление — if / elif / else с отступами вместо {}. Цикл for в Python почти всегда означает «для каждого элемента в …» (for x in items), а не счётчик с тремя частями в заголовке; счётчик — через range(). Списковое включение — компактная форма того же цикла, когда результат сразу собирается в список. while — когда число итераций заранее неизвестно.

Общая схема циклов и типичные ошибки — в циклах в коде (блок "Сначала" выше). Оценка сложности вложенных циклов (O(n²)) с разбором кода — Lab / Big-O — 1128. В задачах на Python те же конструкции встречаются в переборах, FizzBuzz и двойных циклах — Lab / 1122.


Операторы

Play ITЗагрузка интерактивного демо…


Условные операторы

Условные операторы предназначены для ветвления выполнения программы на основе логических выражений.

В Python используется ключевое слово if для начала проверки условия, elif (сокращение от else if) — для дополнительных условий, и else — для блока, выполняемого в случае, если ни одно из предыдущих условий не было истинным.

Синтаксис условной конструкции:

if условие1:
# блок кода 1
elif условие2:
# блок кода 2
else:
# блок кода 3

Каждый блок кода должен быть выделен отступом (PEP 8 рекомендует использовать 4 пробела). Интерпретатор оценивает условия по порядку; выполнение передаётся первому блоку, чьё условие вернуло True. Остальные ветви игнорируются.

Пример:

x = 10
if x > 15:
print("x больше 15")
elif x > 5:
print("x больше 5, но не больше 15")
else:
print("x меньше или равно 5")

Разбор примера:

  • x = 10 создаёт переменную и сохраняет в ней целое число.
  • if x > 15 проверяется первым. Условие ложно, блок пропускается.
  • elif x > 5 проверяется вторым. Условие истинно, выполняется print(...).
  • else не выполняется, потому что подходящая ветка уже найдена.
  • В консоли будет строка x больше 5, но не больше 15.

Важно отметить, что ветви elif и else являются опциональными. Конструкция может состоять только из одного if.


Присваивание в условии (:=)

С Python 3.8 в условии if или заголовке while можно присвоить значение переменной и сразу проверить его — оператор := (walrus, "моржовый"). Определение и синтаксис — в Работа с типами — операторы.

В if — сохранить длину списка и сразу сравнить:

data = list(range(12))

if (n := len(data)) > 10:
print(f"{n} items")

Разбор:

  • len(data) вычисляется один раз, результат попадает в n.
  • Условие (n := len(data)) > 10 истинно, печатается 12 items.
  • Без walrus пришлось бы писать две строки: n = len(data) и if n > 10:.

В while — читать ввод и проверять его в одном выражении:

while (line := input("Enter: ")) != "quit":
print("You typed:", line)

Разбор:

  • Каждая итерация вызывает input(...), строка сохраняется в line.
  • Цикл продолжается, пока пользователь не введёт quit.
  • Переменная line доступна в теле цикла на каждой итерации.

Скобки вокруг (n := len(data)) и (line := input(...)) нужны из-за приоритета операторов.


Сопоставление с образцом (match / case, Python 3.10+)

Конструкция match — альтернатива длинным цепочкам if/elif, когда нужно разобрать структуру значения (число, строка, кортеж, объект):

def http_status_line(code: int) -> str:
match code:
case 200:
return "OK"
case 404:
return "Not Found"
case 500 | 502 | 503:
return "Server error"
case n if 400 <= n < 500:
return f"Client error {n}"
case _:
return "Unknown"

case _ — ветка по умолчанию (как else). Для простых двух вариантов по-прежнему достаточно if/else; match удобен при многих ветках и распаковке структур. Ключевые слова перечислены в справочнике ключевых слов.

if <условие>:
<действие>

if <условие>:
<действие_1>
else:
<действие_2>

if <условие_1>:
<действие_1>
elif <условие_2>:
<действие_2>
else:
<действие_3>

Логические операторы

Логические операторы используются для комбинирования или инвертирования булевых выражений. В Python они представлены словами and, or, not, в отличие от многих других языков программирования, где применяются символьные аналоги (&&, ||, !). Это делает код более читаемым, но требует внимания при сравнении с другими C-подобными языками.

  • and — возвращает True, если оба операнда истинны.
  • or — возвращает True, если хотя бы один операнд истинен.
  • not — возвращает логическое отрицание операнда.
<выражение_1> and <выражение_2>
<выражение_1> or <выражение_2>
not <выражение>

Операторы работают по принципу короткого замыкания (short-circuit evaluation):

  • x and y: если x ложно, y не вычисляется.
  • x or y: если x истинно, y не вычисляется.

Пример:

a = True
b = False
print(a and b) # False
print(a or b) # True
print(not a) # False

Разбор примера:

  • and возвращает True только если оба операнда истинны. True and False даёт False.
  • or возвращает True, если истинен хотя бы один операнд. True or False даёт True.
  • not инвертирует булево значение. not True даёт False.
  • print(...) выводит результат каждой операции в отдельной строке.

Такой подход позволяет эффективно управлять порядком вычислений и избегать ошибок, например:

if obj is not None and obj.has_method():
obj.method()

Здесь has_method() не будет вызван, если objNone, поскольку первая часть условия ложна, и вторая не вычисляется.

Важно не путать логические операторы с операторами сравнения:

  • == равно;
  • != не равно;
  • > больше;
  • < меньше;
  • >= больше или равно;
  • <= меньше или равно.

Сравнительные операторы

Есть классические операторы сравнения:

Код ITЗагрузка примера кода…

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

== — проверяет равенство значений двух объектов. Вызывает метод __eq__() соответствующего класса.

is — проверяет идентичность объектов, то есть указывают ли две переменные на один и тот же объект в памяти (сравнивает их идентификаторы через id()).

<значение_1> == <значение_2>
<значение_1> != <значение_2>
<значение_1> > <значение_2>
<значение_1> < <значение_2>
<значение_1> >= <значение_2>
<значение_1> <= <значение_2>
<объект_1> is <объект_2>
<объект> is None

Пример:

a = [1, 2, 3]
b = [1, 2, 3]

print(a == b) # True — значения одинаковы
print(a is b) # False — это разные объекты в памяти

Разбор примера:

  • a и b содержат одинаковые элементы, поэтому a == b возвращает True.
  • a is b сравнивает не значения, а идентичность объектов.
  • В памяти созданы два разных списка, поэтому a is b возвращает False.
  • Практическое правило: для сравнения данных используйте ==, а is оставляйте для None и синглтонов.

Однако:

c = a
print(a is c) # True — c ссылается на тот же объект, что и a

Разбор примера:

  • c = a не копирует список, а создаёт второе имя для того же объекта.
  • Поэтому a is c возвращает True.
  • Если изменить a, изменения будут видны и через c.

Исключение составляют малые целые числа и строки, которые интернируются (кэшируются) интерпретатором. Например:

x = 256
y = 256
print(x is y) # True (в большинстве реализаций)

x = 257
y = 257
print(x is y) # Может быть False — зависит от контекста выполнения

По этой причине не рекомендуется использовать is для сравнения значений, особенно числовых или строковых. Оператор is следует применять в основном для сравнения с None, True, False и другими синглтонами:

if obj is None:
print("Объект не определён")

Это считается лучшей практикой, поскольку None — уникальный объект.


Интерактивное демо — пошаговый цикл на примере JavaScript (for, while). В Python синтаксис другой (forin, while), но порядок шагов тот же. Обобщённо: циклы в коде.

Play ITЗагрузка интерактивного демо…


Циклы

В Python два основных оператора повторения — for … in (обход итерируемого) и while (пока условие истинно). Оператора do…while в языке нет; "сделать хотя бы раз" имитируют через while True с break или отдельной проверкой после первого прохода.

КонструкцияПорядокКогда удобна
for … inследующий элемент → телосписок, строка, range(), ключи словаря, генератор
списковое включениевыражение → элемент → …новый список из итерируемого за одну строку
whileпроверка условия → телочисло итераций заранее неизвестно

Счётный цикл "N раз" в Python обычно пишут как for i in range(N) или for _ in range(N), а не через while со счётчиком. Обобщённая теория — циклы в коде.


for

Цикл for в Python является итерационным, а не счётным, как в некоторых других языках. Он предназначен для обхода элементов итерируемого объекта — коллекций, строк, диапазонов и других объектов, поддерживающих протокол итерации.

Синтаксис:

for переменная in итерируемый_объект:
# тело цикла

Часто в качестве итерируемого объекта используется функция range(), которая генерирует арифметическую последовательность чисел. Функция range(stop) — создаёт объект диапазона, содержащий целые числа от 0 до stop - 1.

range(start, stop[, step]) позволяет задать начальное значение, конечное значение (не включая его) и шаг.

Примеры:

Код ITЗагрузка примера кода…

Разбор примера:

  • for i in range(10) выполняет тело 10 раз, а i принимает значения от 0 до 9.
  • print(i, end=" ") печатает числа в одну строку, разделяя их пробелом.
  • range(0, 50, 5) задаёт начало, конец (не включая 50) и шаг 5.
  • range(10, 0, -1) использует отрицательный шаг и даёт обратный отсчёт.
  • for letter in 'word' итерируется по строке посимвольно — w, o, r, d.

Обратите внимание — range() возвращает объект типа range, который является ленивым (lazy) — числа генерируются по мере необходимости, что экономит память.

Я бы выделил три основных вида цикла for():

  1. for(A) - повторять определённое количество раз;
  2. for(A, B) - выполнять количество раз в диапазоне от A до B;
  3. for(A, B, C) - выполнять количество раз в диапазоне от A до B, с размером шага C.

Цикл for можно использовать с любыми итерируемыми типами:

  • списками;
  • кортежами;
  • множествами;
  • словарями (ключи);
  • генераторами;
  • т.д.
for <переменная> in <итерируемый_объект>:
<действие>

for <переменная> in range(<конец>):
<действие>

for <переменная> in range(<начало>, <конец>):
<действие>

for <переменная> in range(<начало>, <конец>, <шаг>):
<действие>

К примеру, повторить 100 раз какую-то фразу:

for i in range(100):
print(f'{i}. Повтор!')

Комбинированные паттерны

# Поиск с прерыванием
for <элемент> in <коллекция>:
if <условие_нахождения>:
<обработка>
break

# Пропуск элементов
for <элемент> in <коллекция>:
if <условие_пропуска>:
continue
<обработка>

# Безопасная проверка объекта
if <объект> is not None and <объект>.<метод>():
<действие>

# Итерация по словарю
for <ключ> in <словарь>:
<действие_с_ключом>

for <ключ>, <значение> in <словарь>.items():
<действие_с_парой>

Списковые включения — «for в одну строку»

Списковое включение (list comprehension) — компактная запись цикла for, который сразу строит новый список. В разговорной речи его иногда называют «генератором списка», но не путайте с функцией-генератором (yield) и выражением-генератором в круглых скобках — об этом ниже и в статье Итераторы, генераторы и контекстные менеджеры.

Вместо «создать пустой список → пройти циклом → append» вы описываете результат одним выражением. Это один из самых характерных приёмов Python — от простых квадратов чисел до генерации игровых карт и матриц.

Полный синтаксис

[<выражение> for <элемент> in <итерируемый_объект> if <условие>]

Читаем слева направо: сначала что попадёт в список, затем откуда берутся элементы, при необходимости — фильтр в конце.

[x ** 2 for x in range(5)]
# ^^^^^ ^^^^^^^^^^^^^^^
# что откуда берём
# делаем элементы

Часть if <условие> необязательна. Без неё перебираются все элементы итератора.

Базовые формы

# 1. Простое включение — без преобразования
[x for x in range(10)]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 2. С преобразованием
[x ** 2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 3. С фильтрацией
[x for x in range(10) if x % 2 == 0]
# [0, 2, 4, 6, 8]

# 4. Фильтрация и преобразование вместе
[x ** 2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]

Эквивалент четвёртой формы через обычный цикл:

result = []
for x in range(10):
if x % 2 == 0:
result.append(x ** 2)

Тот же шаблон работает со строками, списками и любыми итерируемыми объектами:

upper = [letter.upper() for letter in "hello"]
lengths = [len(word) for word in ["Python", "Java", "Go"]]

Условное выражение внутри включения

Если в список должны попадать разные значения в зависимости от условия, в левую часть (в <выражение>) вставляют тернарный оператор:

<значение_если_True> if <условие> else <значение_если_False>
result = [x if x % 2 == 0 else -x for x in range(10)]
# [0, -1, 2, -3, 4, -5, 6, -7, 8, -9]
# чётные оставляем, нечётные делаем отрицательными

Разбор:

  • for x in range(10) — источник элементов.
  • x if x % 2 == 0 else -xвыражение результата для каждого x.
  • Фильтрующий if в конце здесь не нужен — в список попадают все десять чисел, просто в разном виде.

Типичный приём в roguelike и тайловых играх — карта с границей-стеной:

ROWS, COLS = 10, 15

map_with_border = [
['#' if x == 0 or x == COLS - 1 or y == 0 or y == ROWS - 1 else '.'
for x in range(COLS)]
for y in range(ROWS)
]
# края — стены ('#'), внутри — пол ('.')

Внешний цикл for y in range(ROWS) строит строки карты, внутренний — столбцы в каждой строке. Похожие приёмы — в практикуме по roguelike.

Вложенные включения

Вложенное включение — это включение, результат которого снова используется как элемент другого включения. Внешний цикл for идёт последним в записи (снизу вверх при чтении).

Матрица одинаковых строк:

matrix = [[j for j in range(3)] for i in range(3)]
# [[0, 1, 2], [0, 1, 2], [0, 1, 2]]

Матрица с уникальными значениями:

matrix = [[i * 3 + j for j in range(3)] for i in range(3)]
# [[0, 1, 2], [3, 4, 5], [6, 7, 8]]

Транспонирование (строки становятся столбцами):

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Шахматная доска — чередование символов по сумме координат:

chess_board = [
['♖' if (x + y) % 2 == 0 else '♘' for x in range(8)]
for y in range(8)
]

Разворот (flatten) вложенного списка в один — один уровень вложенности, два цикла в одной строке:

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Несколько циклов for в одном включении

Несколько for в одном включении эквивалентны вложенным циклам. Порядок сохраняется — как если бы вы писали for снаружи внутрь:

# Обычный вложенный цикл
pairs = []
for x in range(3):
for y in range(3):
pairs.append((x, y))

# То же через включение
pairs = [(x, y) for x in range(3) for y in range(3)]
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

Фильтр if в конце относится ко всей комбинации циклов:

# только пары, где x != y
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
# [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]

# пары с суммой координат 3
pairs = [(x, y) for x in range(4) for y in range(4) if x + y == 3]
# [(0, 3), (1, 2), (2, 1), (3, 0)]

Несколько if подряд работают как цепочка and:

result = [x for x in range(20) if x % 2 == 0 if x % 3 == 0]
# [0, 6, 12, 18] — делятся и на 2, и на 3

Включения для set и dict

Тот же синтаксис «цикл + выражение» работает для других коллекций:

КонструкцияСкобкиРезультат
списковое включение[...]list
включение множества{expr ...} (без :)set
словарное включение{ключ: значение ...}dict

Множество — уникальные значения:

squares = {x ** 2 for x in range(10)}
letters = {char for char in "hello world" if char != ' '}
# {'h', 'e', 'l', 'o', 'w', 'r', 'd'}

Словарь — пары ключ–значение:

squares_dict = {x: x ** 2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in original.items()}
# {1: 'a', 2: 'b', 3: 'c'}

filtered = {k: v for k, v in original.items() if v > 1}
# {'b': 2, 'c': 3}

Подробнее про включения в контексте коллекций — в главе Типы данных.

enumerate, zip и другие итераторы

Включение не обязано идти только по range() — источником может быть любой итерируемый объект:

# индекс + значение
indexed = [(i, x) for i, x in enumerate(['a', 'b', 'c'])]
# [(0, 'a'), (1, 'b'), (2, 'c')]

# параллельный обход двух списков
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 92, 78]
students = [(name, score) for name, score in zip(names, scores)]
# [('Alice', 85), ('Bob', 92), ('Charlie', 78)]

Функции enumerate и zip — в статье Функции.

Примеры из игровой логики

Клетки комнаты — все координаты прямоугольника одним выражением:

def create_room(x, y, w, h):
room_cells = [(cx, cy) for cx in range(x, x + w) for cy in range(y, y + h)]
for cx, cy in room_cells:
game_map[cy][cx] = '.' # пол комнаты

Видимые клетки в радиусе (круг на дискретной сетке):

visible_cells = [
(x, y)
for x in range(max(0, px - radius), min(COLS, px + radius + 1))
for y in range(max(0, py - radius), min(ROWS, py + radius + 1))
if (x - px) ** 2 + (y - py) ** 2 <= radius ** 2
]

Пакетное создание объектов — несколько монстров за один проход:

import random

monsters = [
Monster(
random.randint(room[0] + 1, room[0] + room[2] - 2),
random.randint(room[1] + 1, room[1] + room[3] - 2),
random.choice(['Goblin', 'Orc', 'Bat']),
random.randint(3, 8),
random.randint(1, 4),
)
for _ in range(random.randint(1, 3))
]

Фильтрация «живых» объектов после обновления:

particles = [p for p in particles if p.update()]
# update() двигает частицу и возвращает False, когда life <= 0

Система частиц — создание и отбор за два включения:

import random

class Particle:
def __init__(self, x, y, dx, dy):
self.x, self.y, self.dx, self.dy = x, y, dx, dy
self.life = 100

def update(self):
self.x += self.dx
self.y += self.dy
self.life -= 1
return self.life > 0

particles = [
Particle(
random.randint(0, 800),
random.randint(0, 600),
random.uniform(-1, 1),
random.uniform(-1, 1),
)
for _ in range(100)
]

particles = [p for p in particles if p.update()]

Выражение-генератор

Выражение-генератор выглядит как списковое включение, но в круглых скобках и не создаёт список сразу — возвращает ленивый итератор:

list_comp = [x ** 2 for x in range(1_000_000)] # list — всё в памяти
gen_exp = (x ** 2 for x in range(1_000_000)) # generator — по одному элементу

total = sum(x ** 2 for x in range(10)) # скобки часто опускают

Разница принципиальна:

  • [...] — все элементы материализуются сразу.
  • (...) — элементы считаются по запросу (next, цикл for, sum, max).

В roguelike, если список координат нужен только для одного прохода, выгоднее не создавать список:

visible_cells = (
(x, y)
for x in range(max(0, px - radius), min(COLS, px + radius + 1))
for y in range(max(0, py - radius), min(ROWS, py + radius + 1))
if (x - px) ** 2 + (y - py) ** 2 <= radius ** 2
)

for x, y in visible_cells:
game_map[y][x].visible = True

Когда нужен именно список (индексация, повторный обход, len) — квадратные скобки. Когда достаточно одного прохода — круглые скобки или выражение без обёртки.

Когда включение, когда обычный for

Удобно, когда:

  • простое преобразование: squares = [x ** 2 for x in numbers];
  • фильтрация: evens = [x for x in numbers if x % 2 == 0];
  • создание матрицы: matrix = [[0 for _ in range(cols)] for _ in range(rows)];
  • код читается как «создать список из…».

Лучше обычный for, когда:

# 1. Сложная логика с несколькими ветками
# Плохо:
result = [x if x > 0 else y if y > 0 else z for x, y, z in zip(a, b, c)]
# Хорошо:
result = []
for x, y, z in zip(a, b, c):
if x > 0:
result.append(x)
elif y > 0:
result.append(y)
else:
result.append(z)

# 2. Побочные эффекты (print, запись в файл, API)
# Плохо:
[print(x) for x in range(5)]
# Хорошо:
for x in range(5):
print(x)

# 3. Очень большой объём и один проход — генераторное выражение
# list: [x for x in range(1_000_000)]
# gen: (x for x in range(1_000_000))

Списковое включение — выражение, возвращающее список; цикл forоператор с блоком инструкций.

Производительность

Списковые включения обычно быстрее эквивалентного цикла с append: интерпретатор выполняет построение списка на более низком уровне, без повторных вызовов метода.

import timeit

def test_loop():
result = []
for i in range(1000):
result.append(i ** 2)
return result

def test_comprehension():
return [i ** 2 for i in range(1000)]

# На типичной машине comprehension быстрее примерно на 30–50 %
timeit.timeit(test_loop, number=10_000)
timeit.timeit(test_comprehension, number=10_000)

Разница заметна на больших объёмах и в горячих участках кода. На читаемости экономить не стоит — если включение превращается в «однострочник на полэкрана», вернитесь к обычному for.

Полезные приёмы

Матрица без ловушки общей ссылки[[0] * cols] * rows даёт один и тот же внутренний список во всех строках. Правильно:

# НЕЛЬЗЯ: [[0, 0]] * 3 — все строки ссылаются на один список
matrix = [[0 for _ in range(cols)] for _ in range(rows)]

Подсчёт вхождений через включение (для небольших данных; на больших — collections.Counter):

words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counts = {word: words.count(word) for word in set(words)}
# {'apple': 3, 'banana': 2, 'orange': 1}

Безопасное удаление из списка — не менять коллекцию во время обхода, а собрать новую:

items = [1, 2, 3, 4, 5]
items = [x for x in items if x % 2 == 1]
# [1, 3, 5]

Тот же приём — в разделе Частые ловушки ниже.

Частые ошибки

Забытый if — условие фильтрации пишется только с ключевым словом if:

# Ошибка:
# [x for x in range(10) x % 2 == 0]

# Правильно:
[x for x in range(10) if x % 2 == 0]

Порядок циклов — переменная в <выражение> должна быть объявлена раньше (левее) того цикла, где используется:

# Ошибка — x ещё не определён в момент чтения выражения:
# [x for y in range(3) for x in range(2)]

# Правильно:
[x for x in range(2) for y in range(3)]

Путаница «изменить» и «создать новый» — включение не меняет исходный список, а возвращает новый. Результат нужно присвоить переменной:

numbers = [1, 2, 3, 4, 5]
doubled = [x * 2 for x in numbers] # numbers не изменился
numbers = doubled # явная замена — ок

Вложенное включение для побочных эффектов[game_map[y].__setitem__(x, '.') for ...] и подобное лучше заменить обычным for: включение должно возвращать данные, а не выполнять действия ради действий.


while

Цикл while выполняет блок кода до тех пор, пока указанное условие остаётся истинным. Он используется, когда количество итераций заранее неизвестно.

Синтаксис:

while условие:
# тело цикла

Пример:

count = 0
while count < 5:
print(count)
count += 1

Разбор примера:

  • count = 0 задаёт начальное значение счётчика.
  • Перед каждой итерацией проверяется условие count < 5.
  • Внутри цикла текущее значение выводится в консоль.
  • count += 1 изменяет состояние, поэтому цикл в итоге завершается.
  • Результат вывода: числа от 0 до 4.

Типичный приём с walrus — чтение ввода прямо в условии цикла (см. присваивание в условии):

while (line := input("Enter: ")) != "quit":
print("You typed:", line)
while <условие>:
<действие>

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

while True:
print("Это бесконечный цикл")

Такие конструкции допустимы, если внутри цикла предусмотрен механизм прерывания через break.

Для детального контроля над выполнением циклов и условных блоков в Python предусмотрены специальные управляющие инструкции.

break — немедленно прекращает выполнение текущего цикла и передаёт управление следующему за ним блоку кода. Часто используется при поиске элемента или обработке ошибок.

for i in range(10):
if i == 5:
break
print(i) # Вывод: 0, 1, 2, 3, 4

Разбор примера:

  • Цикл идёт по числам от 0 до 9.
  • Когда i становится 5, срабатывает break.
  • break мгновенно завершает весь цикл, а не только текущую итерацию.
  • Поэтому печатаются только 0, 1, 2, 3, 4. continue — прерывает текущую итерацию и переходит к следующей. Управление возвращается к началу цикла, где проверяется условие.
for i in range(5):
if i == 2:
continue
print(i) # Вывод: 0, 1, 3, 4

Разбор примера:

  • continue пропускает оставшийся код текущей итерации.
  • При i == 2 вызов print(i) не выполняется.
  • Цикл продолжается со следующего значения.
  • Поэтому в выводе отсутствует только 2.

pass — пустая операция. Используется как заглушка, когда синтаксически требуется наличие инструкции, но логика ещё не реализована.

if condition:
pass # Заглушка для будущей реализации
else:
print("Условие ложно")

pass также применяется в определениях классов, функций и блоков, где отсутствие кода привело бы к синтаксической ошибке.


Связанные материалы


Практическая логика написания условий и циклов

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

  1. Условия пишутся от "самого узкого и критичного" к "общему".
  2. Длинные проверки выносятся в отдельные функции-предикаты.
  3. Цикл отвечает за обход, а бизнес-логика выносится в функции.

Пример аккуратного предиката:

def can_publish(post, user):
return user.is_staff and post.is_ready and not post.is_archived

if can_publish(post, user):
publish(post)

Так код проще тестировать и поддерживать.


Частые ловушки новичков

  • Ошибочный is вместо == при сравнении строк и чисел.
  • Изменение коллекции во время обхода той же коллекции в for — безопаснее собрать новый список через списковое включение.
  • Бесконечный while из-за условия, которое не меняется.
  • Глубокие вложенные if, где лучше ранний выход через return.
  • Включение ради побочного эффекта — [print(x) for x in items] вместо обычного for (см. когда не использовать включение).
  • [[0] * cols] * rows вместо [[0 for _ in range(cols)] for _ in range(rows)] — общая ссылка на внутренние списки.

Безопасный шаблон изменения списка:

items = [1, 2, 3, 4, 5]
items = [x for x in items if x % 2 == 1]

Это надёжнее, чем удалять элементы из items внутри цикла.