Что происходит без функций

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

Проблема вылезает при правке. Кто-то замечает, что итоговую оценку нужно округлять до одного знака после запятой, и меняет формулу в первом месте. Второе и третье места остаются старыми просто потому, что про них забыли. Отчёт и проверка на стипендию годами показывают неокруглённое число, хотя на экране студент уже видит исправленное значение. Три копии одной логики — это три места, где программа расходится сама с собой.

Из чего состоит функция

Функцию объявляют словом def, дальше идёт её имя, а в круглых скобках — параметры, которые она принимает на вход. Тело функции содержит вычисления, а слово return отдаёт готовый результат наружу, в то место программы, откуда функцию вызвали. Вызов выглядит короткой строкой вроде calculate_average(scores) вместо повторного набора всей формулы заново.

Параметров может быть ноль, один или несколько — зависит от того, сколько входных данных нужно логике. Функция не обязана ничего возвращать явно: тогда её вызывают ради того, что она делает, а не ради значения, которое отдаёт обратно, и Python в этом случае молча подставляет None.

Побочные эффекты и чистые вычисления

Функцию, которая только считает число по входным данным и ничего больше не трогает, называют чистой. Одинаковый вход всегда даёт одинаковый выход, а файловая система, экран и глобальные переменные остаются нетронутыми. calculate_average — как раз такой случай: она берёт список оценок и возвращает одно число.

Функция save_report, наоборот, открывает файл и записывает в него текст — это побочный эффект, изменение чего-то за пределами самой функции. Разделять эти две роли полезно на практике. Чистую функцию с расчётом легко проверить отдельно, а функцию с побочным эффектом проверить сложнее, потому что для этого нужен настоящий файл или его имитация.

Единая точка правки

Вернёмся к истории с округлением оценки. Если формула живёт в одной функции calculate_average, а вывод на экран, отчёт и проверка стипендии просто вызывают её с разными аргументами, правка округления в одном месте автоматически меняет поведение во всех трёх сценариях сразу. Программа больше не может незаметно разойтись сама с собой — источник логики теперь один.

Тестируемость

Функцию calculate_average можно вызвать напрямую с заранее известным списком оценок и сравнить результат с ожидаемым числом, не запуская всю программу и не вводя данные руками через input. Такая проверка занимает пару строк и выполняется за доли секунды, а повторить её можно сколько угодно раз после любой правки кода.

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

Как из набора функций получается модуль

Родственные функции — calculate_average, format_grade, round_rating — переносят в отдельный файл, например grades.py, оставляя там же короткое описание назначения файла в начале. Модуль не обязан содержать только функции: в нём могут жить и константы, и вспомогательные переменные, важные именно для этой темы. Главный ориентир — единство назначения. Всё, что относится к работе с оценками, живёт в одном файле, а не рассыпано по всему проекту.

Как импортировать модуль в другом файле

Строка import grades делает доступными все функции модуля через точку — например, вызов будет выглядеть как grades.calculate_average с аргументом scores в скобках. Форма from grades import calculate_average подключает конкретную функцию напрямую, без имени модуля впереди, а слово as даёт модулю короткий псевдоним при импорте. Модуль можно ещё и проверить в одиночку: в конец файла добавляют условие, что код ниже выполняется только при прямом запуске grades.py, а при обычном импорте в другой файл это условие остаётся в стороне.

  • Один и тот же блок кода дословно повторяется два раза и больше
  • Логику можно назвать одним понятным глаголом вроде посчитать, сохранить или проверить
  • Кусок кода хочется проверить отдельно от всей остальной программы
  • Один и тот же расчёт нужен в разных местах с разными входными данными
  • Код хочется целиком скопировать в другой проект

Частые вопросы

Обязательно ли функция должна что-то возвращать

Нет, функция вправе просто выполнить действие и не отдавать наружу никакого значения — тогда её вызывают ради самого действия, а не ради результата, и Python в этом случае подставляет None.

Чем функция отличается от модуля

Функция — это один именованный кусок кода на одну задачу, а модуль — файл, где собраны несколько связанных функций и иногда переменных под одной темой, который целиком подключают через import.

Сколько параметров может принимать функция

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

Можно ли вызвать одну функцию внутри другой

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