Классические подвохи: итерация, поздняя привязка, деление

Подборка «вопросов с подвохом», которые любят давать, чтобы проверить внимательность.

Эти ошибки выглядят безобидно, компилируются и часто дают неправильный, но не падающий результат — самый коварный вид багов.

Подвох 1: изменение списка во время итерации

Чёткий ответ. Удалять элементы из списка, по которому идёт for, нельзя: индексы сдвигаются, и цикл пропускает элементы. Решение — итерировать по копии (list[:]) или строить новый список.

nums = [1, 2, 2, 3]
for n in nums:
    if n == 2:
        nums.remove(n)   # меняем список во время итерации
print("неверно:", nums)  # одна двойка осталась!

nums = [1, 2, 2, 3]
result = [n for n in nums if n != 2]   # правильный способ
print("верно:  ", result)

Вывод:

неверно: [1, 2, 3]
верно:   [1, 3]

После удаления первой двойки индексы сдвинулись, и цикл «перепрыгнул» вторую — она уцелела. Comprehension строит новый список и проблемы не имеет.

Подвох 2: поздняя привязка в замыканиях

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

funcs = [lambda: i for i in range(3)]
print("ловушка:", [f() for f in funcs])   # ожидали 0,1,2

funcs = [lambda i=i: i for i in range(3)]  # фиксируем через дефолт
print("фикс:   ", [f() for f in funcs])

Вывод:

ловушка: [2, 2, 2]
фикс:    [0, 1, 2]

Все три лямбды смотрят на одну переменную i, которая после цикла равна 2. Приём i=i сохраняет текущее значение в аргументе по умолчанию (он вычисляется сразу).

Подвох 3: целочисленное деление и остаток

/ всегда даёт float, // — целочисленное деление с округлением вниз (к меньшему), а не к нулю. Для отрицательных чисел это сюрприз.

print(7 / 2)      # обычное деление -> float
print(7 // 2)     # floor division
print(-7 // 2)    # округление ВНИЗ, не к нулю -> -4
print(-7 % 2)     # остаток того же знака, что делитель

Вывод:

3.5
3
-4
1

Итог

  • Не меняйте список во время for по нему — итерируйте по копии или стройте новый.
  • Лямбды в цикле захватывают переменную, а не значение; фиксируйте через x=x.
  • // округляет вниз (для отрицательных -7 // 2 == -4); / всегда float.
Проверьте себя
1. Почему опасно удалять элементы из списка во время for по нему?
AСписок становится неизменяемым
BИндексы сдвигаются, и цикл пропускает элементы
CВозникает деление на ноль
DPython это запрещает синтаксически
2. Что выведет [f() for f in [lambda: i for i in range(3)]]?
A[0, 1, 2]
B[2, 2, 2]
C[0, 0, 0]
DОшибку
3. Чему равно -7 // 2 в Python?
A-3
B-4
C3
D-3.5
Поддержать проект