


Генератор списка в Python, или list comprehension, создаёт новый список по правилу [выражение for элемент in источник if условие]. Выражение задаёт результат, цикл перебирает источник, а необязательное условие отбирает элементы. Такой приём удобен для одного понятного преобразования, но не обязан заменять любой цикл.
В примере Mentoring.digital возьмём учебные заказы: получим номера оплаченных заказов и подпись для каждого. Данные уже имеют известную структуру; проверку внешнего JSON нужно выполнять до этого шага.
orders = [
{"id": 1, "paid": True, "total": 1200},
{"id": 2, "paid": False, "total": 800},
{"id": 3, "paid": True, "total": 500},
]
paid_ids = [order["id"] for order in orders if order["paid"]]
labels = ["paid" if order["paid"] else "waiting" for order in orders]
print(paid_ids) # [1, 3]
print(labels) # ['paid', 'waiting', 'paid']
В paid_ids условие стоит после for и исключает неоплаченный заказ из результата. Поэтому элементов два. В labels условное выражение находится перед for: оно выбирает одну из двух строк для каждого заказа. Поэтому элементов три.
Ошибка [x for x in items if condition else other] возникает из-за смешения этих конструкций. Если нужно изменить значение каждого элемента, поставьте if ... else ... в выражение слева. Если нужно убрать неподходящие элементы, оставьте фильтр справа. Эти возможности можно сочетать, но сначала проверьте, легко ли прочитать правило вслух.
paid_ids = []
for order in orders:
if order["paid"]:
paid_ids.append(order["id"])
Это тот же отбор с тем же порядком результата. List comprehension не сортирует и не удаляет повторы сам по себе. Два оплаченных заказа с одинаковым id дадут два одинаковых значения. Если источник пустой, получится пустой список.
Не используйте [print(x) for x in items] только ради вывода. Такое выражение создаёт ненужный список результатов вызова. Обычный цикл лучше выражает действие с побочным эффектом. Он также удобнее, когда внутри нужны обработка исключений, несколько промежуточных переменных или отдельное сообщение об ошибке.
squares = [n * n for n in range(4)]
lazy_squares = (n * n for n in range(4))
print(list(lazy_squares)) # [0, 1, 4, 9]
print(list(lazy_squares)) # []
Квадратные скобки строят список сразу. Круглые скобки в этом примере создают выражение-генератор: значения вычисляются по мере запроса, а после полного обхода этот объект исчерпан. Для повторного обхода нужен новый генератор или сохранённый список.
Если требуется только сумма, можно передать генератор непосредственно в sum(n * n for n in range(4)). Это позволяет не создавать отдельный список квадратов. Из этого не следует универсальное обещание ускорения: выбор зависит от размера данных, повторных обходов и работы внутри выражения.
rows = [[1, 2], [], [3]]
flat = [value for row in rows for value in row]
assert flat == [1, 2, 3]
Порядок for здесь такой же, как в обычных вложенных циклах: сначала строка, затем её элементы. Сравните с парами [(x, y) for x in [1, 2] for y in [3, 4]], где получается четыре комбинации, а не попарное соединение двух списков.
Для самостоятельной проверки добавьте пустой источник, один подходящий элемент, повторы и элемент, не проходящий фильтр. Если правило стало трудно объяснить без расшифровки, вынесите преобразование в именованную функцию или верните обычный цикл.
На курсе Python с наставником такие задачи помогают связать коллекции с обработкой реальных данных. Для разбора уже написанного кода покажите ментору по Python исходный пример, ожидаемый список и случай, в котором результат отличается.