Зачем нужны функции
Представьте, что вы каждый день готовите один и тот же салат. Можно каждый раз заново придумывать рецепт по памяти, а можно один раз записать его в тетрадь и потом просто открывать нужную страницу. Функция в программировании работает точно так же: это кусок кода, которому вы дали имя и который можно вызывать снова и снова, не переписывая его заново.
До сих пор, если один и тот же расчёт был нужен в разных местах программы, приходилось копировать строки кода. Это неудобно: если в расчёте найдётся ошибка, придётся искать и исправлять её во всех копиях сразу. Программисты называют это нарушением принципа DRY — сокращение от английского Don't Repeat Yourself, «не повторяйся». Если один и тот же код встречается в программе два раза и больше — это сигнал, что здесь нужна функция.
Без функции — одна и та же логика повторяется трижды:
width1, height1 = 5, 3
area1 = width1 * height1
print(f"Площадь: {area1}")
width2, height2 = 10, 4
area2 = width2 * height2
print(f"Площадь: {area2}")
width3, height3 = 7, 2
area3 = width3 * height3
print(f"Площадь: {area3}")
С функцией — логика написана один раз:
def print_area(width, height):
area = width * height
print(f"Площадь: {area}")
print_area(5, 3)
print_area(10, 4)
print_area(7, 2)
Как объявить функцию: слово def
Функция, которую вы «записываете в тетрадь» — это объявление с ключевым словом def (сокращение от английского define, «определить»). Вот самая простая функция, какая только бывает:
def greet():
print("Привет!")
Разберём эту запись по кусочкам:
| def | Ключевое слово: говорит Python «дальше идёт определение новой функции» |
| greet | Имя функции. Правила именования те же, что у переменных: буквы, цифры, подчёркивание, не начинать с цифры |
| () | Круглые скобки нужны всегда, даже пустые. Внутри них позже появятся параметры |
| : | Двоеточие закрывает заголовок функции — так же, как в if и for |
| print("Привет!") | Тело функции, с отступом в 4 пробела. Всё, что имеет отступ, принадлежит функции |
Важно: строка def greet(): сама по себе ничего не печатает. Python на этом шаге только запоминает: «есть функция greet, вот что она делает». Тело выполнится позже — когда функцию вызовут.
Определение и вызов — разные вещи
Начинающие часто путают эти два шага, поэтому зафиксируем разницу чётко. Определение (def имя(): с телом) — это запись рецепта. Вызов (имя()) — это команда «выполни рецепт прямо сейчас». Функцию можно определить один раз, а вызвать сколько угодно раз в любом месте программы после определения.
def greet(): # определение — Python запомнил функцию
print("Привет!")
greet() # вызов — печатает "Привет!"
greet() # вызов — печатает "Привет!" ещё раз
greet() # и ещё раз
Вызов — это всегда имя со скобками: greet(). Если написать greet без скобок, Python не запустит функцию, а просто покажет ссылку на неё, что-то вроде <function greet at 0x...>. Скобки — это и есть команда «выполнить».
Оператор return: отдаём результат наружу
Представьте автомат с газировкой: вы нажали кнопку, и он не просто «пошумел» внутри, а выдал вам стакан — результат, который можно унести с собой. В функциях за это отвечает return: он отдаёт значение туда, откуда функцию вызвали, чтобы с этим значением можно было работать дальше — сохранить в переменную, сложить с другим числом, передать в другую функцию.
Это принципиально отличается от print(), который просто показывает текст на экране и на этом всё заканчивается — результат нигде не сохраняется.
def square(n):
return n * n
result = square(5) # result = 25
print(result) # 25
print(square(3) + square(4)) # 9 + 16 = 25
Теперь сравним с версией, где вместо return стоит print:
def square_bad(n):
print(n * n) # только печатает, ничего не отдаёт наружу
result = square_bad(5) # на экране появится: 25
print(result) # None — результата, с которым можно работать, нет
Правило: если функция что-то вычисляет и это значение понадобится дальше — используйте return. Если функция просто что-то делает (печатает, сохраняет) и её результат никому не нужен — return можно не писать.
Функция без return возвращает None
Если в теле функции нет ни одного return, Python всё равно что-то возвращает — специальное значение None, которое означает «ничего». Это официальный термин: None — единственный объект своего типа NoneType, и он не равен ни нулю, ни пустой строке, ни False.
def say_hello():
print("Привет!")
# return здесь нет
result = say_hello() # на экране: Привет!
print(result) # None
print(type(result)) # <class 'NoneType'>
Отсюда частая ошибка новичков: написать x = my_func(), забыв поставить return внутри my_func, а потом удивляться, почему x оказался None. Если результат неожиданно None — первым делом проверьте, есть ли в функции return.
Несколько return: ранний выход
В одной функции может быть несколько операторов return. Как только выполнение доходит до первого же return — функция немедленно завершается, а всё, что написано ниже, просто не выполняется. Такой приём называется ранним выходом (по-английски early return).
def classify_age(age):
if age < 0:
return "Некорректный возраст"
if age < 18:
return "Ребёнок"
if age < 60:
return "Взрослый"
return "Пожилой"
print(classify_age(-5)) # Некорректный возраст
print(classify_age(10)) # Ребёнок
print(classify_age(35)) # Взрослый
print(classify_age(70)) # Пожилой
Обратите внимание: здесь нет ни одного elif. Ранний return сам обрывает выполнение, поэтому до следующей проверки код просто не доходит — в этом и смысл приёма: он убирает лишнюю вложенность и делает условия плоскими и читаемыми.
Возврат нескольких значений сразу
Функция может вернуть сразу два и больше значения. Никакой отдельной магии для этого нет: запятая между значениями после return собирает их в кортеж, который вы уже умеете распаковывать.
def min_max(numbers):
return min(numbers), max(numbers) # это кортеж из двух значений
result = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print(result) # (1, 9)
print(type(result)) # <class 'tuple'>
minimum, maximum = min_max([3, 1, 4, 1, 5, 9, 2, 6]) # сразу распаковали
print(minimum) # 1
print(maximum) # 9
return a, b и return (a, b) — одно и то же: кортеж создаёт именно запятая, а не скобки. Такой приём удобен, когда нужно вернуть сразу и результат, и, например, признак успеха операции.
Локальные переменные и область видимости
Представьте, что каждая функция — это отдельная комната со своими вещами. То, что лежит внутри комнаты, не видно снаружи, и наоборот — вещи из коридора сами не оказываются в комнате. Переменная, созданная внутри функции, существует только внутри неё: снаружи её просто нет. Это называется официальным термином область видимости (scope).
def calculate():
secret = 42
result = secret * 2
return result
print(calculate()) # 84
print(secret) # NameError: name 'secret' is not defined
Переменные, созданные внутри функции, называют локальными: каждый вызов функции создаёт их заново, а после завершения функции они пропадают. Даже если снаружи есть переменная с таким же именем, они не мешают друг другу:
x = 100 # переменная уровня модуля
def show():
x = 999 # другая переменная, живёт только внутри show
print(x) # 999
show()
print(x) # 100 — внешняя x не изменилась
Совет: такая изоляция — это удобство, а не помеха. Не пытайтесь её обойти без необходимости: правильный способ передать данные в функцию — через параметры, а получить результат обратно — через return.
Ключевое слово global
Функция без проблем может читать переменную снаружи себя. А вот изменить такую переменную изнутри функции просто присваиванием не получится — Python решит, что вы создаёте новую локальную переменную с тем же именем. Чтобы явно сказать «я хочу изменить именно внешнюю переменную», используют слово global:
counter = 0 # переменная уровня модуля
def increment():
global counter # без этой строки Python создал бы локальную counter
counter += 1
increment()
increment()
increment()
print(counter) # 3
Без строки global counter код упал бы с ошибкой: Python решил бы, что counter внутри функции локальная, и попытался бы прочитать её раньше, чем она создана.
Когда стоит пользоваться global? Почти никогда. Если код тянется к global на каждом шагу — это повод пересмотреть структуру: передавайте данные через параметры и забирайте результат через return. Так проще понять, откуда пришло значение и кто его меняет.
Правило LEGB: как Python ищет имена
Когда в коде встречается имя переменной, Python должен понять, о какой именно переменной идёт речь, если таких имён вложено несколько. Он ищет по уровням строго по порядку, и это правило называется LEGB — по первым буквам английских названий уровней.
| L | Local | Внутри той функции, где мы сейчас находимся — ищем здесь первым делом |
| E | Enclosing | Внутри функции, в которую текущая функция вложена (для функции внутри функции) |
| G | Global | Переменные, объявленные на верхнем уровне файла, вне всех функций |
| B | Built-in | Встроенные имена самого Python: print, len, range, True, None |
x = "уровень модуля"
def outer():
x = "объемлющий уровень"
def inner():
x = "локальный уровень"
print(x) # найдёт "локальный уровень" — он ближе всего
inner()
print(x) # найдёт "объемлющий уровень"
outer()
print(x) # найдёт "уровень модуля"
Уровень Built-in — это то, что Python знает по умолчанию: print, len, int и так далее. Именно поэтому не стоит называть свои переменные list или print: вы перекроете встроенное имя своей переменной, и вызвать оригинальную функцию станет нельзя.
Частые ошибки
Забыть скобки при вызове. Без скобок функция не выполняется — вы просто получаете ссылку на неё, а не результат:
def square(n):
return n * n
result = square # забыли скобки
print(result) # <function square at 0x...>, а не число
Спутать print и return. Функция, которая только печатает результат, не отдаёт его наружу — работать с таким «результатом» дальше нельзя:
def double(n):
print(n * 2) # только печатает
total = double(5) + double(3) # TypeError: печать возвращает None
Неправильный отступ у тела функции. Тело обязано иметь отступ, иначе Python не поймёт, что относится к функции:
def greet():
print("Привет!") # нет отступа
# IndentationError: expected an indented block
Что важно запомнить
- DRY: функция — это код с именем, который можно вызывать сколько угодно раз, не копируя его
- def имя(): определяет функцию, вызов — это всегда
имя()со скобками - return отдаёт значение обратно туда, откуда вызвали функцию; без него функция возвращает
None - Несколько return — функция завершается на первом же достигнутом
return, это называют ранним выходом - return a, b возвращает кортеж; его можно сразу распаковать:
x, y = func() - Локальные переменные существуют только внутри своей функции и пропадают после её завершения
- global позволяет изменить внешнюю переменную изнутри функции, но злоупотреблять им не стоит
- LEGB — порядок поиска имён: Local, затем Enclosing, затем Global, затем Built-in
Проверьте себя
6 вопросов
Площадь прямоугольника
Напишите функцию rectangle_area(width, height), которая принимает ширину и высоту прямоугольника и возвращает его площадь.
Например, rectangle_area(5, 3) должна вернуть 15.