В Python функция — это не просто набор команд: это интерфейс, через который в неё передают данные (аргументы) и из неё получают результат (возвращаемое значение). Понимание того, как устроена эта передача, критично для предсказуемой работы кода.
Базовый принцип: вход и выход
У функции есть два канала обмена данными:
- Вход: аргументы, которые вы передаёте при вызове.
- Выход: значение, которое функция возвращает через
return.
Простейший пример:
def сложить(a, b): return a + b результат = сложить(2, 3) print(результат) # 5
Здесь a и b — аргументы, 5 — возвращаемое значение.
Важно: если в функции нет return, она возвращает None:
def просто_печатает(x): print(x) r = просто_печатает(10) print(r) # None
Это частая ошибка: ожидают число или строку, а получают None, потому что забыли вернуть значение.
Позиционные и именованные аргументы
Аргументы можно передавать двумя способами: по позиции и по имени.
def заказать_кофе(вид, количество, молоко=True): тип_молока = "с молоком" if молоко else "без молока" return f"{количество} шт. {вид}, {тип_молока}"
Вызов по позиции (порядок важен):
заказать_кофе("американо", 2, False) # "2 шт. американо, без молока"
Вызов с именованными аргументами (порядок не важен, код читаемее):
заказать_кофе(количество=3, вид="латте", молоко=True) # "3 шт. латте, с молоком"
Именованные аргументы особенно полезны, когда параметров много или у них есть значения по умолчанию: сразу видно, что именно передаётся.
Значения по умолчанию
Значения по умолчанию задаются в заголовке функции и используются, если аргумент не передали:
def формат_даты(день, месяц, год, разделитель="-"): return f"{день}{разделитель}{месяц}{разделитель}{год}"
Примеры:
формат_даты(1, 5, 2025) # "1-5-2025" формат_даты(1, 5, 2025, разделитель="/") # "1/5/2025"
Важное предупреждение: не используйте изменяемые объекты (список, словарь) как значения по умолчанию. Пример проблемы:
# Плохо: список создаётся один раз при определении функции def добавить_в_список(элемент, список=[]): список.append(элемент) return список
Если вызвать эту функцию несколько раз без передачи списка, он будет накапливать элементы между вызовами.
Правильный шаблон:
def добавить_в_список(элемент, список=None): if список is None: список = [] список.append(элемент) return список
Переменное число аргументов: *args и **kwargs
Когда количество аргументов заранее неизвестно, используют *args (позиционные) и **kwargs (именованные).
*args — кортеж позиционных аргументов
def сумма(*числа): total = 0 for n in числа: total += n return total
Вызовы:
сумма(1, 2, 3) # 6 сумма(10, 20, 30, 40) # 100
Внутри функции числа — это кортеж.
**kwargs — словарь именованных аргументов
def показать_параметры(**параметры): for ключ, значение in параметры.items(): print(f"{ключ} = {значение}")
Вызов:
показать_параметры(цвет="красный", размер=12, активный=True)
Вывод:
цвет = красный размер = 12 активный = True
На практике *args и **kwargs часто применяют в обёртках, декораторах и функциях, которые должны «пробрасывать» любые аргументы дальше.
Передача по ссылке и изменяемость объектов
В Python аргументы передаются «по ссылке на объект», но это не значит, что переменные внутри и снаружи функции — одно и то же. Важнее понять, что происходит с самим объектом.
Для неизменяемых объектов (числа, строки, кортежи) эффект выглядит как «передача по значению»:
def увеличить(x): x = x + 1 a = 5 увеличить(a) print(a) # 5 — переменная a не изменилась
Для изменяемых объектов (списки, словари) изменения внутри функции видны снаружи:
def добавить(список, элемент): список.append(элемент) data = [1, 2] добавить(data, 3) print(data) # [1, 2, 3]
Поэтому, если функция не должна менять входные данные, лучше работать с копией:
def удвоить_список(список): новый = список.copy() for i in range(len(новый)): новый[i] *= 2 return новый
Возврат нескольких значений
Функция может вернуть сразу несколько значений — на самом деле это один кортеж:
def статистика(числа): сумма_значений = sum(числа) среднее = сумма_значений / len(числа) if числа else 0 минимум = min(числа) if числа else None максимум = max(числа) if числа else None return сумма_значений, среднее, минимум, максимум
Распаковка при вызове:
s, avg, mn, mx = статистика([1, 2, 3, 4, 5]) print(s, avg, mn, mx) # 15 3.0 1 5
Можно вернуть и словарь, если имена полей важнее порядка:
def статистика_dict(числа): ... return { "сумма": сумма_значений, "среднее": среднее, "минимум": минимум, "максимум": максимум, }
Словарь удобнее, когда порядок значений не очевиден или их много.
Практический пример: валидация и нормализация цены
Допустим, нужно принимать цену в разных форматах, проверять её и возвращать нормализованное значение и статус.
def нормализовать_цену(значение, валюта="руб.", минимальная=0): # попытка привести к float try: цена = float(значение) except (TypeError, ValueError): return None, "некорректный формат" if цена < минимальная: return None, f"цена ниже минимальной ({минимальная})" return round(цена, 2), "OK"
Использование:
цена, статус = нормализовать_цену("199.99") if статус == "OK": print(f"Цена: {цена} {валюта}") else: print("Ошибка:", статус) цена2, статус2 = нормализовать_цена("abc") print("Ошибка:", статус2)
Такой интерфейс чётко разделяет «результат» и «статус ошибки», и его легко использовать в разных местах.
Частые ошибки и как их избежать
- Забыли
returnи пытаются использовать результат.
Всегда проверяйте, что функция действительно возвращает нужное значение. - Изменяют входные аргументы, когда этого не ожидают.
Если функция не должна менять данные — работайте с копиями или создавайте новые объекты. - Используют изменяемые объекты как значения по умолчанию.
Применяйте шаблонarg=Noneи инициализацию внутри тела функции. - Путают позиционные и именованные аргументы при сложных сигнатурах.
Когда параметров много, именованные аргументы делают код понятнее и уменьшают риск перепутать значения.
Пример: функция-агрегатор с гибкой передачей параметров
Задача: собрать статистику по продажам, принимая произвольное число цен и необязательные настройки.
def продажи_статистика(*цены, валюта="руб.", округление=2): if not цены: return {"сумма": 0, "среднее": 0, "минимум": None, "максимум": None} сумма = sum(цены) среднее = сумма / len(цены) минимум = min(цены) максимум = max(цены) return { "сумма": round(сумма, округление), "среднее": round(среднее, округление), "минимум": round(минимум, округление), "максимум": round(максимум, округление), "валюта": валюта, }
Вызовы:
продажи_статистика(100, 200, 300) продажи_статистика(99.5, 149.9, 299.0, валюта="USD", округление=1)
Эта функция показывает, как комбинировать *args, параметры по умолчанию и возврат структурированного результата.
- Аргументы — это способ передать данные внутрь функции. Используйте позиционные для простоты, именованные — для читаемости и гибкости.
- Возвращаемое значение — это способ получить результат наружу. Если результат сложный, удобно возвращать кортеж или словарь.
- Будьте внимательны с изменяемыми объектами: они могут «протекать» между внешней областью и функцией.
- Значения по умолчанию делайте безопасными: избегайте изменяемых типов, используйте шаблон с
None. - Если логика становится запутанной, разбейте её на несколько функций с понятными входами и выходами — так код будет проще поддерживать и тестировать.
