Глава 13 · Автоматизация с помощью функций

Debug Lab: типичные ошибки функций

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

Десять типичных ошибок, связанных с функциями, — каждая с примером и объяснением. Первые две уже разбирались отдельно (§13.1, §13.13) — здесь для полноты списка, компактно; остальные восемь — подробно.

1 · Случайный вызов до определения
bug.py
greet()

def greet():
    print('Привет')

На момент вызова greet() ещё не определена — Python выполняет код сверху вниз, и NameError возникает раньше, чем строка def успевает выполниться.

2 · Незаметный def внутри def
bug.py
def outer():
    ...

    def helper():
        ...

Отступ определяет вложенность. Если helper случайно оказался с отступом внутри outer, он не будет виден снаружи как самостоятельная функция — только через outer.

3 · Функция всегда возвращает None
bug.py
def calculate(x):
    print(x * 2)

result = calculate(5) + 1

calculate печатает, но не возвращает — result станет None, и None + 1 вызовет TypeError.

4 · Не все пути функции возвращают значение
bug.py
def sign(number):
    if number > 0:
        return 'positive'
    # а если number <= 0?

Для отрицательных и нулевых чисел функция доходит до конца без return — и молча возвращает None.

5 · return внутри цикла на первой итерации
bug.py
def contains_even(numbers):
    for n in numbers:
        if n % 2 == 0:
            return True
        else:
            return False

return в ветке else срабатывает уже на первом элементе — проверяются не все числа. Правильно: return True внутри if, а return False — после всего цикла, с тем же отступом, что и for.

6 · return перепутан с break
bug.py
def find_first_even(numbers):
    for n in numbers:
        if n % 2 == 0:
            break   # а не return n!

break просто останавливает цикл — n нужно ещё явно вернуть отдельной строкой после цикла. return сразу выходит из ВСЕЙ функции, break — только из ближайшего цикла.

7 · Неожиданная мутация переданного списка
bug.py
def sorted_names(names):
    names.sort()
    return names

names.sort() мутирует список вызывающего кода на месте. Если это не входит в план, безопаснее return sorted(names) — новый список, оригинал не тронут.

8 · Забыли скопировать перед изменением
bug.py
def normalize_items(items):
    items[0] = items[0].strip()
    return items

Если исходные данные нужно сохранить нетронутыми, начните с items = items.copy() — но помните про поверхностность копии (глава 11, §11.10) для вложенных структур.

9 · Глобальное состояние усложняет тестирование
bug.py
score = 0

def add_point():
    global score
    score += 1

Работает, но такую функцию сложнее тестировать изолированно — её результат зависит от скрытой глобальной переменной. def add_point(score): return score + 1 тестируется одной строкой, без всякой настройки.

10 · Параметр затеняет встроенное имя
bug.py
def total(list):
    return sum(list)

list — имя встроенного типа. Затенение его параметром работает, но сбивает с толку читателя и ломает доступ к настоящему list() внутри этой функции. Лучше: def total(numbers):

Метод отладки функций

  • Печатает функция или возвращает? Проверьте на бумаге, прежде чем читать код.
  • Проверены ли ВСЕ пути выполнения функции, включая неявный «конец без return»?
  • return внутри цикла — точно ли он должен сработать именно на этой итерации?
  • Мутирует ли функция переданный аргумент намеренно? Если нет — скопируйте.
  • Не перепутано ли имя параметра со встроенным именем Python?
Практика: находим и исправляем ошибки в функциях
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки
Открыть практику →