Функция в Python создаётся словом def: после него пишут имя, в скобках параметры, ставят двоеточие и с отступом пишут тело. Результат функция отдаёт через return, а запускается вызовом по имени со скобками: greet("Аня").
def greet(name):
return f"Привет, {name}!"
message = greet("Аня")
print(message)
# Привет, Аня!
Функции нужны, когда один и тот же кусок кода повторяется: его пишут один раз и вызывают сколько угодно. Если вы только написали первую программу по разбору Hello World и четырёх простых примеров, это следующий шаг. Программы, где функции разбирают на заданиях с проверкой, собраны в каталоге курсов по Python. Вывод программы в примерах дан комментарием под кодом.
Как создать функцию через def

Синтаксис описан в учебнике на docs.python.org: за def идут имя и список параметров в скобках, а тело начинается со следующей строки и обязано быть с отступом.
- Напишите
defи имя функции:def area. - В скобках перечислите параметры через запятую:
(width, height). Параметров может не быть, тогда скобки пустые. - Поставьте двоеточие в конце строки.
- С новой строки и с отступом в четыре пробела напишите тело: что функция делает с данными.
- Если функция должна что-то отдать наружу, допишите
returnи выражение.
Код удобнее писать не в интерактивной консоли, а в файле: как создать его и запустить одной кнопкой, показано в инструкции про файл Python в VS Code.
def area(width, height):
return width * height
print(area(5, 4))
# 20
Имя пишите строчными буквами через подчёркивание: calculate_total, а не CalculateTotal. Так советует PEP 8, руководство по стилю кода Python.
Само определение функцию не запускает. Строка с def только создаёт объект функции и связывает его с именем. Тело выполнится, когда функцию вызовут, это прямо записано в справочнике языка.
Как вызвать функцию
Чтобы вызвать функцию, напишите её имя и круглые скобки, в скобках аргументы. Без скобок вы получите не результат, а саму функцию как объект:
print(area(5, 4))
# 20
print(area)
# <function area at 0x1060a7270>
Адрес в конце у вас будет другим, это место в памяти. Если в выводе появилось <function …>, значит, где-то забыли скобки.
Вызывать функцию можно из любого места программы, но только после строки с def. Python читает файл сверху вниз, и на вызове выше определения имени ещё нет:
print(hello())
def hello():
return "hi"
# NameError: name 'hello' is not defined. Did you mean: 'help'?
Решение: ставьте определения функций в начало файла, а основной код ниже. Внутри другой функции порядок мягче: тело выполняется в момент вызова, и к этому моменту все def модуля уже прочитаны.
Что делает return и чем он отличается от print
return завершает функцию и отдаёт значение тому, кто её вызвал. print только печатает текст на экран, а функция при этом возвращает None. Разница видна, когда результат нужно сохранить:
def add_print(a, b):
print(a + b)
def add_return(a, b):
return a + b
x = add_print(2, 3)
y = add_return(2, 3)
print(x, y)
# 5
# None 5
Первая функция напечатала пятёрку, но в переменную x попал None. С таким значением дальше ничего не посчитать. Если результат нужен в программе, пишите return, а print оставьте для вывода пользователю.
Функция без return и «процедуры»
Отдельного слова для процедур, как в Паскале, в Python нет. Функция без return всё равно возвращает значение, просто это None: так сказано в том же разделе учебника. Такие функции пишут ради действия: записать файл, отправить сообщение, напечатать отчёт.
Заготовку функции, которую допишете потом, оформляют через pass:
def todo():
pass
print(todo())
# None
Несколько значений и ранний выход
Через запятую return отдаёт несколько значений сразу. На деле это один кортеж, который удобно разложить по переменным:
def min_max(numbers):
return min(numbers), max(numbers)
low, high = min_max([7, 2, 9, 4])
print(low, high)
# 2 9
print(min_max([7, 2, 9, 4]))
# (2, 9)
В одной функции может стоять несколько return. Сработает первый, до которого дойдёт выполнение, остальной код ниже пропускается. Так проверки не вкладываются друг в друга лесенкой:
def check_age(age):
if age < 0:
return "Возраст не может быть отрицательным"
if age < 18:
return "Доступ закрыт"
return "Добро пожаловать"
print(check_age(-3))
print(check_age(15))
print(check_age(30))
# Возраст не может быть отрицательным
# Доступ закрыт
# Добро пожаловать
Короткую проверку из двух веток можно уложить и в одну строку, об этом в разборе тернарного оператора в Python.
Параметры и аргументы
Эти два слова часто смешивают. Параметр — имя в скобках при определении (width). Аргументом называют значение, которое вы передаёте при вызове (5).
| Вид | Как выглядит | Когда нужен |
|---|---|---|
| Позиционный | area(5, 4) |
Значения встают в параметры по порядку |
| Именованный | area(height=4, width=5) |
Порядок не важен, вызов читается без подсказок |
| Со значением по умолчанию | def f(price, discount=10) |
Аргумент можно не передавать |
*args |
def total(*numbers) |
Заранее неизвестно, сколько позиционных аргументов придёт |
**kwargs |
def profile(**data) |
Заранее неизвестно, сколько именованных аргументов придёт |
Значения по умолчанию и именованные аргументы
def price_with_discount(price, discount=10):
return price - price * discount / 100
print(price_with_discount(2000))
print(price_with_discount(2000, 25))
print(price_with_discount(discount=5, price=1000))
# 1800.0
# 1500.0
# 950.0
Параметры со значением по умолчанию пишут после обязательных. Наоборот нельзя: Python не поймёт, к какому параметру отнести первый аргумент, и откажется запускать файл.
*args и **kwargs
Звёздочка собирает лишние позиционные аргументы в кортеж, две звёздочки собирают именованные в словарь:
def total(*numbers):
print(numbers)
return sum(numbers)
print(total(1, 2, 3))
# (1, 2, 3)
# 6
def profile(**data):
print(data)
profile(name="Аня", city="Казань")
# {'name': 'Аня', 'city': 'Казань'}
Имена args и kwargs условные, работают именно звёздочки. Другие способы сложить числа из списка, от цикла до NumPy, есть в статье как суммировать список в Python.
Только позиционные и только именованные
Косая черта / в списке параметров запрещает передавать стоящие до неё параметры по имени, звёздочка * требует передавать стоящие после неё только по имени. Первое появилось в Python 3.8 по PEP 570, второе описано в PEP 3102.
def power(base, exp=2, /, *, verbose=False):
result = base ** exp
if verbose:
print(f"{base} в степени {exp} = {result}")
return result
print(power(3))
power(2, 10, verbose=True)
print(power(2, 3, True))
# 9
# 2 в степени 10 = 1024
# TypeError: power() takes from 1 to 2 positional arguments but 3 were given
Две звёздочки в base ** exp к параметрам отношения не имеют, это возведение в степень, о нём в статье про операторы в Python.
Зачем это нужно: флаг вроде verbose в вызове power(2, 3, True) непонятен без заглядывания в определение. Звёздочка заставляет писать verbose=True, и код читается сам.
Где живут переменные: локальная и глобальная область
Переменная, созданная внутри функции, видна только там. Обратное правило хитрее. В FAQ по программированию оно сформулировано так: если переменную внутри функции только читают, она считается глобальной. Если ей где-то в теле присваивают значение, она локальная.
Отсюда самая частая ошибка новичка со счётчиком:
counter = 0
def increase():
counter += 1
increase()
# UnboundLocalError: cannot access local variable 'counter' where it is not associated with a value
Из-за += Python решил, что counter локальная, а значения у неё внутри функции ещё нет.
Решение: передать значение аргументом и вернуть новое через return.
counter = 0
def increase(value):
return value + 1
counter = increase(counter)
print(counter)
# 1
Есть и слово global counter в начале тела, оно разрешает менять глобальную переменную. Работает, но такие функции трудно проверять: результат зависит от того, что лежит снаружи. Для вложенных функций есть nonlocal, он меняет переменную объемлющей функции:
def make_counter():
count = 0
def step():
nonlocal count
count += 1
return count
return step
tick = make_counter()
tick()
print(tick())
# 2
Lambda и передача кода как значения
Функция в Python тоже объект. Её можно положить в переменную, передать в другую функцию, вернуть из функции, как make_counter выше:
def shout(text):
return text.upper() + "!"
action = shout
print(action("готово"))
print(list(map(shout, ["да", "нет"])))
# ГОТОВО!
# ['ДА!', 'НЕТ!']
Для коротких функций в одно выражение есть lambda. По учебнику это синтаксический сахар для обычного определения: внутри одно выражение, return не пишут.
square = lambda x: x * x
print(square(6))
# 36
words = ["яблоко", "кот", "банан"]
print(sorted(words, key=len))
print(sorted(words, key=lambda w: w[-1]))
# ['кот', 'банан', 'яблоко']
# ['банан', 'яблоко', 'кот']
Лямбду чаще всего встречают в параметре key у сортировки: она говорит, по какому признаку сравнивать элементы. Подробнее в статье про метод sort() и функцию sorted().
Присваивать лямбду переменной, как square выше, PEP 8 не одобряет: он требует в таком случае всегда писать def.
Чистые функции и побочные эффекты

Чистая функция зависит только от своих аргументов и ничего не меняет снаружи. На одни и те же данные она всегда отвечает одинаково. Побочным эффектом называют любое изменение за пределами функции: правка глобального списка, запись в файл, печать на экран.
cart = []
def add_impure(item):
cart.append(item)
def add_pure(items, item):
return items + [item]
add_impure("чай")
print(cart)
# ['чай']
old = ["чай"]
new = add_pure(old, "сахар")
print(old, new)
# ['чай'] ['чай', 'сахар']
Первая функция тихо поменяла список cart, и чтобы понять её поведение, надо помнить, что в нём лежало до вызова. Вторая вернула новый список, а старый остался прежним.
Без побочных эффектов программа бесполезна: что-то же нужно сохранить или показать. Обычно их собирают в нескольких функциях на краю программы, а расчёты оставляют чистыми. Чистую функцию проще проверить: подали аргументы, сравнили ответ, и никакой подготовки окружения.
Пример: разбить программу на функции
Задача: из строки оценок посчитать средний балл и выдать вердикт. Каждый шаг вынесен в свою функцию, и каждая делает одно дело:
def parse_scores(line):
return [int(x) for x in line.split()]
def average(numbers):
return sum(numbers) / len(numbers)
def grade(avg):
if avg >= 4.5:
return "отлично"
if avg >= 3.5:
return "хорошо"
return "надо подтянуть"
def report(line):
scores = parse_scores(line)
avg = average(scores)
return f"Средний балл {avg:.2f}: {grade(avg)}"
print(report("5 4 5 5 3"))
print(report("3 4 3"))
# Средний балл 4.40: хорошо
# Средний балл 3.33: надо подтянуть
Поменялись пороги оценок, и вы правите только grade. Оценки стали приходить из файла, а не строкой? Меняется только parse_scores. Остальной код об этом не знает. Потренироваться в таком разбиении можно на блоке про функции в подборке 30 задач по Python для начинающих.
Одна подсказка к названиям: называйте функцию глаголом или тем, что она вернёт. average, parse_scores, is_valid читаются без комментариев, а func1 и do_stuff заставляют лезть в тело.
Docstring, подсказки типов и рекурсия
Строка документации
Первая строка в теле, взятая в тройные кавычки, становится документацией функции. Её показывают help() и подсказки редактора. PEP 257 советует писать её как команду («Вернуть площадь…») и заканчивать точкой.
Подсказки типов
После параметра через двоеточие можно указать ожидаемый тип, после скобок через -> указывают тип результата. Python эти подсказки при запуске не проверяет: авторы PEP 484 прямо пишут, что язык останется динамически типизированным. Ловят несоответствия редактор и анализаторы вроде mypy.
def area(width: float, height: float) -> float:
"""Вернуть площадь прямоугольника."""
return width * height
print(area.__doc__)
print(area("ab", 3))
# Вернуть площадь прямоугольника.
# ababab
Строку умножили на три, и никакой ошибки: подсказка float ничего не запретила.
Рекурсия
Функция может вызывать сама себя. Нужно условие остановки, иначе вызовы не кончатся:
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5))
# 120
import sys
print(sys.getrecursionlimit())
# 1000
Глубина ограничена, текущий предел показывает sys.getrecursionlimit(). Для обхода больших данных надёжнее обычный цикл, например while или for.
Что уже встроено в Python
Прежде чем писать свою функцию, проверьте, нет ли готовой. В таблице встроенных функций на docs.python.org 73 имени, они доступны без импорта. Часть из них формально типы (int, list, str), но вызываются так же.
- длина, сумма, наименьшее и наибольшее:
len(),sum(),min(),max(); - работа с последовательностями:
sorted(),reversed(),enumerate(),zip(); - ввод, вывод и файлы:
input(),print(),open(); - применить функцию к каждому элементу или проверить условие:
map(),filter(),any(),all().
Знаки вроде //, % и ** функциями не являются, это операторы. А функции, которые принадлежат объекту и вызываются через точку (words.append()), называются методами, они появятся вместе с классами в разборе ООП простыми словами.
Частые ошибки и как их исправить

Большинство ошибок с функциями узнаётся по тексту сообщения. Найдите своё в левой колонке.
| Что видите | Почему | Что сделать |
|---|---|---|
<function … at 0x…> вместо результата |
Функцию упомянули без скобок | Дописать скобки: area() |
None там, где ждали число |
В функции print вместо return |
Вернуть значение через return |
IndentationError: expected an indented block after function definition |
Тело функции без отступа | Сдвинуть тело на четыре пробела |
TypeError: area() missing 1 required positional argument: 'height' |
Передали меньше аргументов, чем параметров | Передать все или задать значение по умолчанию |
NameError: name '…' is not defined |
Вызов стоит выше def или в имени опечатка |
Перенести определение выше, сверить имя |
UnboundLocalError |
Глобальную переменную меняют внутри функции | Передать аргументом и вернуть через return |
Изменяемое значение по умолчанию
Здесь Python не выдаёт никакого сообщения, поэтому ошибку легко пропустить. Список по умолчанию создаётся один раз при определении функции и дальше копит данные между вызовами:
def items_to_list(item, basket=[]):
basket.append(item)
return basket
print(items_to_list("хлеб"))
print(items_to_list("молоко"))
# ['хлеб']
# ['хлеб', 'молоко']
Ждали два списка по одному товару, а получили общий. Поведение описано в FAQ на docs.python.org: значение по умолчанию вычисляется один раз.
Решение: по умолчанию ставить None и создавать список внутри функции.
def items_to_list_fixed(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
print(items_to_list_fixed("хлеб"))
print(items_to_list_fixed("молоко"))
# ['хлеб']
# ['молоко']
То же касается словарей и множеств. Чем отличаются append(), insert() и extend(), показано в статье как добавить элемент в список Python.
Как закрепить
- Возьмите кусок своего кода, который повторяется два раза, и вынесите его в функцию с параметрами.
- Перепишите решения своих прошлых задач так, чтобы каждая часть была функцией с
return, аprintостался один, в самом конце. - Запускайте каждый пример из статьи сами и меняйте аргументы: так быстрее видно, что делает каждый параметр.
Где освоить Python системно
На функциях построено всё остальное в Python: модули, классы, работа с данными и веб-фреймворки. По отдельным статьям их можно понять, а вот привычку разбивать программу на части проще выработать на курсе с код-ревью и проектами.
Если думаете о Python как о профессии, начните с обзора кто такой Python-разработчик и чем он занимается. Ниже пять программ из каталога.
| Курс | Школа | Стоимость со скидкой | В рассрочку | Длительность | Обзор курса от Checkroi |
|---|---|---|---|---|---|
| Профессия «Python-разработчик» Перейти на сайт курса | 157 335 ₽ | 5987 ₽/мес. | 10 месяцев | Обзор курса | |
| Fullstack-разработчик на Python Перейти на сайт курса | 137 011 ₽ | 7125 ₽/мес. | 21 месяц | Обзор курса | |
| ДО Профессия Python-разработчик Перейти на сайт курса | 149 001 ₽ | 3167 ₽/мес. | 10 месяцев | Обзор курса | |
| Автоматизированное тестирование на Python Перейти на сайт курса | 118 666 ₽ | 4108 ₽/мес. | 9 месяцев | Обзор курса | |
| Программирование на Python ПРО Перейти на сайт курса | 119 600 ₽ | - | 17 месяцев | Обзор курса |
Больше программ — в полном каталоге курсов по Python




