Что происходит без функций
Представим скрипт, который в трёх разных местах считает среднюю оценку студента: один раз для вывода на экран, второй раз для отчёта в файл, третий раз для проверки права на стипендию. Формула расчёта — сумма оценок делённая на их количество — скопирована трижды с небольшими отличиями в именах переменных. Пока формула верна, три копии работают одинаково, и разницы никто не замечает.
Проблема вылезает при правке. Кто-то замечает, что итоговую оценку нужно округлять до одного знака после запятой, и меняет формулу в первом месте. Второе и третье места остаются старыми просто потому, что про них забыли. Отчёт и проверка на стипендию годами показывают неокруглённое число, хотя на экране студент уже видит исправленное значение. Три копии одной логики — это три места, где программа расходится сама с собой.
Из чего состоит функция
Функцию объявляют словом 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.
Сколько параметров может принимать функция
Столько, сколько требует логика, от нуля и почти без верхнего предела, но если параметров становится больше пяти-шести, это обычно повод разбить функцию на несколько более простых.
Можно ли вызвать одну функцию внутри другой
Да, и это обычная практика: крупная функция часто опирается на несколько мелких, каждая из которых отвечает за свой маленький шаг общей задачи.