Debug Lab: типичные ошибки функций
Забытый return, return внутри цикла на первой итерации, случайно мутированный аргумент — десять ошибок, которые встречаются в реальном коде снова и снова.
Десять типичных ошибок, связанных с функциями, — каждая с примером и объяснением. Первые две уже разбирались отдельно (§13.1, §13.13) — здесь для полноты списка, компактно; остальные восемь — подробно.
greet()
def greet():
print('Привет')
На момент вызова greet() ещё не определена — Python выполняет код сверху вниз, и NameError возникает раньше, чем строка def успевает выполниться.
def outer():
...
def helper():
...
Отступ определяет вложенность. Если helper случайно оказался с отступом внутри outer, он не будет виден снаружи как самостоятельная функция — только через outer.
def calculate(x):
print(x * 2)
result = calculate(5) + 1
calculate печатает, но не возвращает — result станет None, и None + 1 вызовет TypeError.
def sign(number):
if number > 0:
return 'positive'
# а если number <= 0?
Для отрицательных и нулевых чисел функция доходит до конца без return — и молча возвращает None.
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.
def find_first_even(numbers):
for n in numbers:
if n % 2 == 0:
break # а не return n!
break просто останавливает цикл — n нужно ещё явно вернуть отдельной строкой после цикла. return сразу выходит из ВСЕЙ функции, break — только из ближайшего цикла.
def sorted_names(names):
names.sort()
return names
names.sort() мутирует список вызывающего кода на месте. Если это не входит в план, безопаснее return sorted(names) — новый список, оригинал не тронут.
def normalize_items(items):
items[0] = items[0].strip()
return items
Если исходные данные нужно сохранить нетронутыми, начните с items = items.copy() — но помните про поверхностность копии (глава 11, §11.10) для вложенных структур.
score = 0
def add_point():
global score
score += 1
Работает, но такую функцию сложнее тестировать изолированно — её результат зависит от скрытой глобальной переменной. def add_point(score): return score + 1 тестируется одной строкой, без всякой настройки.
def total(list):
return sum(list)
list — имя встроенного типа. Затенение его параметром работает, но сбивает с толку читателя и ломает доступ к настоящему list() внутри этой функции. Лучше: def total(numbers):
Метод отладки функций
- Печатает функция или возвращает? Проверьте на бумаге, прежде чем читать код.
- Проверены ли ВСЕ пути выполнения функции, включая неявный «конец без return»?
- return внутри цикла — точно ли он должен сработать именно на этой итерации?
- Мутирует ли функция переданный аргумент намеренно? Если нет — скопируйте.
- Не перепутано ли имя параметра со встроенным именем Python?