Объявление функции
Функция — это именованный блок кода, который можно
вызывать многократно с разными аргументами. Объявляется ключевым словом
def:
def greet(name):
print(f"Привет, {name}!")
greet("Анна") # Привет, Анна!
greet("Борис") # Привет, Борис!
Главный смысл функции — DRY (Don't Repeat Yourself): написали один раз, вызываем сколько нужно.
Возврат значения: return
Функция может не только действовать (печать), но и возвращать
результат через return:
def add(a, b):
return a + b
result = add(3, 4)
print(result) # 7
print(add(10, 20)) # 30
Как только встретился return, функция завершается. Если
return нет или он без значения — функция возвращает
None:
def do_nothing():
pass
print(do_nothing()) # None
Возврат нескольких значений
Технически возвращается кортеж, но Python позволяет его сразу распаковать:
def min_max(numbers):
return min(numbers), max(numbers)
lo, hi = min_max([3, 1, 4, 1, 5, 9])
print(lo, hi) # 1 9
Параметры и аргументы
«Параметр» — это переменная в объявлении функции. «Аргумент» — значение, которое передают при вызове. Аргументы можно передавать по позиции или по имени:
def describe(name, age, city):
print(f"{name}, {age} лет, г. {city}")
describe("Анна", 28, "Москва") # позиционные
describe(name="Анна", age=28, city="Москва")# именованные
describe("Анна", city="Москва", age=28) # смешанно
Значения по умолчанию
def greet(name, greeting="Привет"):
print(f"{greeting}, {name}!")
greet("Анна") # Привет, Анна!
greet("Анна", "Здравствуй") # Здравствуй, Анна!
Важно: параметры со значениями по умолчанию должны идти после обязательных.
Ловушка изменяемого значения по умолчанию
Это классический грабли Python — избегайте изменяемых объектов в качестве дефолта:
# ПЛОХО: список создаётся один раз и разделяется между вызовами
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] — сюрприз!
# ХОРОШО: используем None как сигнальное значение
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
*args и **kwargs
*args собирает лишние позиционные аргументы в кортеж,
**kwargs — именованные в словарь:
def log(message, *args, **kwargs):
print(f"[{message}]", *args)
for key, value in kwargs.items():
print(f" {key} = {value}")
log("INFO", "запущено", "в 10:00", user="Анна", host="localhost")
# [INFO] запущено в 10:00
# user = Анна
# host = localhost
args и kwargs — просто договорённость об именах;
сути дела это не меняет (* и ** важны, а не имена).
Распаковка при вызове
Звёздочки работают и в обратную сторону — распаковывают коллекцию в аргументы:
def add(a, b, c):
return a + b + c
nums = [1, 2, 3]
print(add(*nums)) # 6 — список распакован в a, b, c
opts = {"a": 1, "b": 2, "c": 3}
print(add(**opts)) # 6 — словарь распакован в именованные
Области видимости
Переменные внутри функции — локальные, они не видны снаружи:
def f():
x = 10 # локальная переменная
print(x)
f() # 10
# print(x) # NameError — x не существует снаружи
Функция видит внешние переменные (чтение), но не может их изменить
без объявления global:
counter = 0
def increment():
global counter # разрешаем менять глобальную
counter += 1
increment()
print(counter) # 1
Однако global — обычно признак плохой архитектуры. Лучше
передавать значение через параметр и возвращать результат. Глобальные
переменные усложняют отладку и тестирование.
Анонимные функции: lambda
lambda — короткая функция в одну строку, без имени. Удобна,
когда функция нужна «один раз на месте»:
# Обычная функция
def square(x):
return x ** 2
# Эквивалентная lambda
square = lambda x: x ** 2
print(square(5)) # 25
Чаще всего lambda используют как аргумент для sorted(),
map(), filter():
users = [
{"name": "Анна", "age": 28},
{"name": "Борис", "age": 22},
{"name": "Вера", "age": 35},
]
# Сортировка словарей по ключу age
sorted_users = sorted(users, key=lambda u: u["age"])
print([u["name"] for u in sorted_users]) # ['Борис', 'Анна', 'Вера']
Если lambda не помещается в одну короткую строку — перепишите её в обычный
def.
Docstrings — документация функции
Строка сразу после def становится документацией функции. Её
видят редакторы и инструменты автогенерации документации:
def divide(a, b):
'''Возвращает частное a / b. Возбуждает ZeroDivisionError при b == 0.'''
return a / b
print(divide.__doc__) # Возвращает частное a / b...
Аннотации типов
Аннотации — это «подсказки» о типах параметров и возвращаемого значения. Python их не проверяет во время выполнения, но их используют редакторы и статические анализаторы (mypy):
def add(a: int, b: int) -> int:
return a + b
def greet(name: str) -> str:
return f"Привет, {name}!"
# Составные типы
def average(numbers: list[int]) -> float:
return sum(numbers) / len(numbers)
def get_user(user_id: int) -> dict[str, str] | None:
...
Символ | (Python 3.10+) означает «или»: dict | None
— «словарь или None».
Кейс из реального проекта: helpers для магазина
Несколько небольших функций-помощников для обработки цен и скидок. Каждая с аннотацией и docstring:
def format_price(amount: float) -> str:
'''Форматирует цену: 199.5 -> '199.50 ₽'.'''
return f"{amount:.2f} ₽"
def apply_discount(price: float, percent: float) -> float:
'''Возвращает цену со скидкой. percent — от 0 до 100.'''
if not 0 <= percent <= 100:
raise ValueError("Скидка должна быть от 0 до 100")
return price * (1 - percent / 100)
def total_price(items: list[tuple[str, float]]) -> float:
'''Сумма цен из списка пар (название, цена).'''
return sum(price for _, price in items)
cart = [("Ноутбук", 89990.0), ("Мышь", 1200.0)]
print(format_price(apply_discount(total_price(cart), 10)))
# 82071.00 ₽
Заметьте: каждая функция делает одно дело и легко тестируется отдельно. Это и есть хорошая декомпозиция.
Типичные ошибки
1. Изменяемый дефолт
# Плохо
def f(items=[]):
items.append(1)
return items
# Хорошо
def f(items=None):
if items is None:
items = []
...
2. Функция что-то считает, но забыли return
def double(x):
x * 2 # результат потерян, функция вернёт None
def double(x):
return x * 2 # правильно
3. Слишком много global
total = 0
def add(x):
global total # лучше возвращать и присваивать снаружи
total += x
Практика
- Напишите функцию
is_even(n), возвращающуюbool. - Сделайте функцию
format_name(first, last, uppercase=False)с параметром по умолчанию. - Реализуйте
sum_all(*args), возвращающую сумму всех аргументов. - Отсортируйте список строк по длине через
key=lenи черезlambda. - Добавьте аннотации типов и docstring к функции из задания 1.
Итог
Функции — основной способ структурировать код. Мы изучили объявление,
return, параметры и значения по умолчанию, *args и
**kwargs, области видимости, lambda, docstrings и аннотации
типов. В следующей главе — работа с файлами и обработка исключений, без
которых не обходится ни одно реальное приложение.