Кот-наставник— Привет! Я кот-наставник, провожу людей через Python на платформе SkillPy. И вот что скажу: функции — это момент, когда код перестаёт быть списком инструкций и становится программой.
— Ты уже вызывал функции:
print(),input(),len(). Но писать свои — совсем другое дело. Здесь начинается осознанное программирование.— Не пугайся: концепция простая, а возможностей сразу много. Разберём всё по порядку — от первого
defдоlambda.
Функции в Python — это именованные блоки кода, которые можно вызывать сколько угодно раз. Без них программа растёт в одну длинную простыню: сто строк, двести, пятьсот — и всё в одном потоке. С функциями программа разбивается на маленькие детали, каждая из которых делает одну вещь и делает её хорошо.
В этом гайде разберём функции в Python с нуля: объявление через def, параметры и аргументы, возврат значений через return, область видимости, аргументы по умолчанию, *args и **kwargs, а также lambda-функции и их границы. Все примеры запускаются в Python 3.12 без библиотек. Если пока плавают int, str и bool, загляни в гайд Типы данных в Python: полный гайд для начинающих.
Функции в Python: коротко
Функция в Python — это именованный блок кода, который объявляют через def, вызывают по имени со скобками и который через return отдаёт результат обратно в программу. Она нужна, чтобы написать логику один раз и переиспользовать её сколько угодно. Функция принимает данные через параметры, а результат отдаёт через return; если return нет — функция возвращает None.
def greet(name): # объявление функции через def
return f"Привет, {name}!" # return отдаёт результат
message = greet("Аня") # вызов по имени со скобками
print(message)Привет, Аня!
Зачем нужны функции в Python?
Допустим, ты пишешь программу для кофейни. Нужно посчитать цену заказа со скидкой в трёх разных местах: при оформлении, при показе чека и при отправке в базу. Без функций ты пишешь одну и ту же формулу три раза.
# Заказ 1
price1 = 300
discount1 = 10
total1 = price1 - price1 * discount1 / 100
print(f"Итого: {total1} руб.")
# Заказ 2 — та же логика, другой заказ
price2 = 450
discount2 = 15
total2 = price2 - price2 * discount2 / 100
print(f"Итого: {total2} руб.")
# Заказ 3 — снова копируем...
price3 = 520
discount3 = 5
total3 = price3 - price3 * discount3 / 100
print(f"Итого: {total3} руб.")Итого: 270.0 руб. Итого: 382.5 руб. Итого: 494.0 руб.
Работает, но проблем три:
- ❌ Код дублируется. Формула одна — написана трижды. Если нужно изменить логику скидки, править придётся в трёх местах.
- ❌ Легко ошибиться. Скопировал, забыл поменять переменную — и один заказ считается по чужой скидке.
- ❌ Не масштабируется. Заказов будет сто — что, сто раз копировать?
Функция решает все три проблемы: логика пишется один раз, вызывается сколько нужно, и при правке меняется только одно место.
Тот же расчёт, но через функцию:
def calculate_price(price, discount):
total = price - price * discount / 100
return total
print(f"Итого: {calculate_price(300, 10)} руб.")
print(f"Итого: {calculate_price(450, 15)} руб.")
print(f"Итого: {calculate_price(520, 5)} руб.")Итого: 270.0 руб. Итого: 382.5 руб. Итого: 494.0 руб.
Тот же результат, но формула живёт в одном месте. Нужно поменять логику — правишь функцию, и все вызовы автоматически используют новую версию. Это и есть принцип DRY: Don't Repeat Yourself — не повторяй себя.
Объявление функции: def и вызов
Функция — это именованный блок кода, который выполняет определённую задачу. Функция может принимать данные (параметры), обрабатывать их и возвращать результат.
Объявление функции в Python начинается с ключевого слова def. После него — имя функции, скобки с параметрами (если они есть) и двоеточие. Тело функции — все строки с отступом внутри.
Простая функция без параметров:
def greet():
print("Привет из функции!")
greet()
greet()Привет из функции! Привет из функции!
Что произошло:
1. def greet() — объявили функцию с именем greet и без параметров.
2. Тело функции — одна строка print("Привет из функции!").
3. greet() — вызвали функцию. Python выполнил тело и вернулся к следующей строке.
4. Вызвали ещё раз — функция выполнилась заново.
Параметры и аргументы функции
Параметры — это переменные в объявлении функции. Аргументы — это значения, которые передаются при вызове. Параметры — это «ячейки» в определении, аргументы — то, что в них кладут.
Функция с одним параметром:
def greet_user(name):
print(f"Привет, {name}!")
greet_user("Анна")
greet_user("Игорь")Привет, Анна! Привет, Игорь!
Здесь name — параметр, а "Анна" и "Игорь" — аргументы. Функция использует name внутри тела, и при каждом вызове name получает новое значение.
Функция с несколькими параметрами:
def print_receipt(item, price, quantity):
total = price * quantity
print(f"{item} × {quantity} = {total} руб.")
print_receipt("Кофе", 150, 2)
print_receipt("Круассан", 90, 3)Кофе × 2 = 300 руб. Круассан × 3 = 270 руб.
Параметры передаются по позиции: первый аргумент идёт в первый параметр, второй — во второй. Поэтому порядок важен. Если перепутать price и quantity, результат будет другим.
Возвращаемое значение: return
Функция может не только выводить результат на экран, но и возвращать его в программу. Для этого используется ключевое слово return. Возвращённое значение можно сохранить в переменную или использовать дальше.
def add(a, b):
return a + b
result = add(5, 3)
print(result)
print(add(10, 20))8 30
return немедленно завершает функцию и отдаёт значение наружу. Всё, что написано после return в той же ветке, не выполнится.
Использование return для раннего выхода:
def check_age(age):
if age < 18:
return "Доступ запрещён"
return "Доступ разрешён"
print(check_age(15))
print(check_age(25))Доступ запрещён Доступ разрешён
Если age < 18, функция доходит до return "Доступ запрещён" и выходит. Второй return не выполняется. Если условие ложно — выполняется второй return. Сам оператор ветвления разбираем в гайде if, elif и else в Python: условия с примерами.
Функция без return возвращает None
Если в функции нет return или return написан без значения, функция возвращает None — специальное значение, означающее «ничего».
def say_hello(name):
print(f"Привет, {name}!")
result = say_hello("Мир")
print(result)
print(type(result))Привет, Мир! None <class 'NoneType'>
Функция say_hello выводит текст на экран, но не возвращает результат. Поэтому result содержит None. Это частая путаница: print() внутри функции — это вывод на экран, а не возврат значения. Если нужно получить результат из функции, используй return.
Кот-наставник— Запомни простое правило:
return— это отдать. Если функция считает что-то полезное, она должнаreturn, а не
Область видимости: локальные и глобальные переменные
Переменные, созданные внутри функции, живут только внутри неё. Снаружи их не видно. Это называется локальной областью видимости.
def calculate():
result = 42
calculate()
print(result)NameError: name 'result' is not definedПеременная result создана внутри calculate(). Когда функция завершилась, result исчезла. Снаружи её не существует — отсюда NameError.
Переменные снаружи функции видны внутри, но изменять их напрямую нельзя:
counter = 0
def increment():
counter = counter + 1
increment()UnboundLocalError: cannot access local variable 'counter' where it is not associated with a valuePython видит присваивание counter = внутри функции и решает, что counter — локальная переменная. Но при этом пытается использовать её значение до того, как присвоила. Отсюда ошибка.
Правильный подход — не менять глобальные переменные из функции, а принимать данные через параметры и возвращать результат через return:
counter = 0
def increment(value):
return value + 1
counter = increment(counter)
print(counter)
counter = increment(counter)
print(counter)1 2
Аргументы по умолчанию
Параметру можно задать значение по умолчанию. Если при вызове аргумент не передан, используется оно.
def greet(name, greeting="Привет"):
print(f"{greeting}, {name}!")
greet("Анна")
greet("Игорь", "Здравствуй")Привет, Анна! Здравствуй, Игорь!
Первый вызов не передаёт greeting — используется значение по умолчанию "Привет". Второй вызов передаёт "Здравствуй" — оно заменяет значение по умолчанию.
Именованные аргументы
При вызове функции аргументы можно передавать по имени, а не только по позиции. Это делает код читаемее, особенно когда параметров много.
def create_user(name, role, active=True):
print(f"Пользователь: {name}, роль: {role}, активен: {active}")
create_user("Анна", "admin")
create_user("Игорь", "editor", active=False)
create_user(role="moderator", name="Олег")Пользователь: Анна, роль: admin, активен: True Пользователь: Игорь, роль: editor, активен: False Пользователь: Олег, роль: moderator, активен: True
Третий вызов передаёт аргументы по имени, а не по позиции — порядок не важен. Это удобно в функциях с пятью-шестью параметрами, где легко перепутать позицию.
*args и **kwargs: переменное число аргументов
Иногда заранее неизвестно, сколько аргументов передадут функции. Для этого в Python есть *args — сбор всех позиционных аргументов в кортеж, и **kwargs — сбор всех именованных аргументов в словарь.
*args — любое количество позиционных аргументов:
def sum_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2, 3))
print(sum_all(10, 20, 30, 40, 50))
print(sum_all())6 150 0
Внутри функции numbers — это кортеж со всеми переданными аргументами: его удобно перебрать циклом for, как разобрано в гайде Циклы в Python: for, while, break и continue. Можно передать ноль, один, сто — функция отработает одинаково.
**kwargs — именованные аргументы в словарь:
def print_info(**info):
for key, value in info.items():
print(f"{key}: {value}")
print_info(name="Анна", age=25, city="Москва")name: Анна age: 25 city: Москва
Внутри функции info — это словарь. Ключи — имена аргументов, значения — то, что передали. kwargs расшифровывается как keyword arguments.
*args и kwargs — в чём разница?
| Критерий | *args | **kwargs |
|---|---|---|
| Звёздочки | Одна * | Две ** |
| Что собирает | Позиционные аргументы | Именованные аргументы |
| Тип внутри функции | Кортеж (tuple) | Словарь (dict) |
| Пример вызова | f(1, 2, 3) | f(name="Анна", age=25) |
Lambda-функции: анонимные функции в Python
lambda — это способ создать функцию в одну строку без def. Такая функция не имеет имени (поэтому «анонимная») и ограничена одним выражением.
Lambda-функция — это анонимная функция, записанная в одно выражение. Синтаксис:
lambda параметры: выражение. Результат выражения — это возвращаемое значение.
Сравнение обычной функции и lambda:
# Обычная функция
def square(x):
return x ** 2
# То же самое через lambda
square_lambda = lambda x: x ** 2
print(square(5))
print(square_lambda(5))25 25
Результат одинаковый. Разница в том, что def создаёт именованную функцию с телом из нескольких строк, а lambda — одно выражение без имени.
def и lambda — в чём разница?
| Критерий | def | lambda |
|---|---|---|
| Имя | Есть | Нет (анонимная) |
| Тело | Любое число строк | Одно выражение |
| return | Пишется явно | Значение выражения возвращается само |
| Логика с if, циклами | Да | Нет, только выражение |
| Когда использовать | Переиспользуемая логика | Одноразовая функция как аргумент |
Где lambda полезна?
Lambda раскрывается там, где функция нужна один раз и её не имеет смысла выносить в отдельный def. Чаще всего — как аргумент для встроенных функций sorted(), map(), filter().
Сортировка списка словарей по ключу:
users = [
{"name": "Анна", "age": 25},
{"name": "Игорь", "age": 19},
{"name": "Олег", "age": 32},
]
sorted_users = sorted(users, key=lambda user: user["age"])
for u in sorted_users:
print(u["name"], u["age"])Игорь 19 Анна 25 Олег 32
Здесь lambda user: user["age"] — это функция, которая извлекает возраст из словаря. sorted() вызывает её для каждого элемента и сортирует по результату. Выносить это в отдельный def — лишний шум, когда логика умещается в одно выражение.
Когда lambda не нужна?
Lambda — не замена обычным функциям. Не используй её, когда...
- ❌ Логика не умещается в одно выражение. Если нужен
ifс несколькими ветками или цикл — пишиdef. - ❌ Функция используется больше одного раза. Именованная функция читается лучше и переиспользуется.
- ❌ Код становится нечитаемым. Вложенные lambda — это ад для того, кто будет читать код после тебя.
Кот-наставник— Правило простое: если lambda не помещается в одну короткую строку — это уже не lambda, а
def. Читаемость важнее краткости. «Я написал всё в одну строчку» — это не круто.
Частые ошибки при работе с функциями:
1. Забыли return:
def multiply(a, b):
a * b
result = multiply(3, 4)
print(result)None
Выражение a * b вычислено, но результат никому не отдан — нет return. Функция вернула None. Правильно: return a * b.
2. Изменяемый аргумент по умолчанию:
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("яблоко"))
print(add_item("банан"))['яблоко'] ['яблоко', 'банан']
Сюрприз: второй вызов добавил «банан» к списку, в котором уже было «яблоко». Значение по умолчанию [] создаётся один раз при объявлении функции, а не при каждом вызове. Все вызовы без items используют один и тот же список.
Правильный способ — использовать None и создавать список внутри:
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item("яблоко"))
print(add_item("банан"))['яблоко'] ['банан']
3. Перепутали порядок аргументов
def power(base, exp):
return base ** exp
print(power(2, 3))
print(power(3, 2))8 9
power(2, 3) — это 2 в степени 3 = 8. power(3, 2) — это 3 в степени 2 = 9. Порядок аргументов определяет, какое значение попадёт в какой параметр. Если перепутать — результат будет другим, и ошибка неочевидна.
Рефакторинг: разбиваем программу на функции
Допустим, есть программа — всё в одном потоке, без функций:
name = input("Имя: ")
age = int(input("Возраст: "))
if age < 18:
print(f"{name}, доступ запрещён")
else:
print(f"{name}, доступ разрешён")
name2 = input("Имя: ")
age2 = int(input("Возраст: "))
if age2 < 18:
print(f"{name2}, доступ запрещён")
else:
print(f"{name2}, доступ разрешён")Владимир, доступ запрещён Матвей, доступ разрешён
Один и тот же блок повторяется дважды. Разобьём на функции:
def check_access(name, age):
if age < 18:
return f"{name}, доступ запрещён"
return f"{name}, доступ разрешён"
def ask_user():
name = input("Имя: ")
age = int(input("Возраст: "))
return name, age
name, age = ask_user()
print(check_access(name, age))
name, age = ask_user()
print(check_access(name, age))Каждая функция делает одну вещь: ask_user собирает данные, check_access принимает решение. Если нужно изменить логику проверки — правим check_access. Если нужно изменить формат ввода — правим ask_user. Функции не мешают друг другу.
Кстати, return name, age — это возврат двух значений. Технически Python возвращает кортеж (name, age), а name, age = ask_user() его распаковывает. Подробнее об этом — в статье Списки в Python: создание, методы и срезы, где разбираются кортежи и распаковка.
Короткие ответы на вопросы про функции Python
Чем параметр отличается от аргумента?
Параметр — это переменная в объявлении функции, «ячейка» для данных. Аргумент — конкретное значение, которое передают при вызове. В def greet(name) слово name — параметр, а в вызове greet("Анна") строка "Анна" — аргумент.
Что возвращает функция без return?
Функция без return (или с пустым return) возвращает None — специальное значение «ничего». Вывод через print() внутри функции не считается возвратом: он показывает текст на экране, но наружу отдаётся None. Чтобы получить результат для дальнейших вычислений, нужен return.
Что такое lambda-функция простыми словами?
lambda — это функция в одну строку без имени и без def, ограниченная одним выражением. Синтаксис: lambda параметры: выражение. Результат выражения возвращается автоматически. Её используют для коротких одноразовых функций, например как аргумент sorted(), map() или filter().
Зачем нужны *args и **kwargs?
Они позволяют функции принимать переменное число аргументов. *args собирает все лишние позиционные аргументы в кортеж, а **kwargs — все именованные аргументы в словарь. Это удобно, когда заранее неизвестно, сколько значений передадут функции.
Как вернуть из функции несколько значений?
Достаточно перечислить значения после return через запятую: return name, age. Python упакует их в кортеж, а на стороне вызова его можно распаковать сразу в несколько переменных: name, age = ask_user().
Главное про функции:
- Функция — именованный блок кода
Объявляется через
def, вызывается по имени со скобками.def greet(name): print(f"Привет, {name}!") greet("Анна") returnвозвращает результатБез
returnфункция возвращаетNone.returnзавершает функцию немедленно.def add(a, b): return a + b result = add(2, 3) # 5- Параметры — вход,
return— выходФункция принимает данные через параметры и отдаёт через
return. Не лазий в глобальные переменные.counter = 0 def increment(value): return value + 1 counter = increment(counter) - Аргументы по умолчанию и именованные
Упрощают вызов и делают код читаемее.
def greet(name, greeting="Привет"): print(f"{greeting}, {name}!") greet("Анна") greet("Игорь", greeting="Здравствуй") - lambda — для коротких одноразовых функций
Одно выражение, без
def. Не замена обычным функциям.sorted_users = sorted(users, key=lambda u: u["age"])
Кот-наставник— Функции — это первый шаг к осознанному программированию. Не к «выучить синтаксис», а к «мыслить как разработчик»: разбивать задачу на части, давать им имена, собирать из частей целое.
— Напиши три функции из примеров выше, вызови каждую с разными аргументами. Потом сломай: убери
return, перепутай порядок аргументов, поставь изменяемый аргумент по умолчанию. Увидеть ошибку своими глазами полезнее, чем прочитать про неё дважды.
Попробуй на практике!
Напиши свою первую функцию — и проследи, как она работает! На SkillPy функции пишешь сам: от простого def до *args и lambda. AI-наставник читает твоё решение, объясняет, почему return важнее print, и показывает, где изменяемый аргумент по умолчанию стреляет в ногу.
