Аргументы функций в Python: позиционные, именованные, *args и **kwargs с примерами

Аргументы функций в Python — одна из базовых тем, которая напрямую влияет на читаемость кода и качество API. В этой статье мы подробно разберём позиционные и именованные аргументы, значения по умолчанию, *args и **kwargs, распаковку, а также более продвинутые вещи: позиционно‑только и ключево‑только параметры. Материал ориентирован на запрос: «аргументы функций в Python: *args и **kwargs, позиционные и именованные» и подойдет как новичкам, так и продолжающим разработчикам.
Кратко о терминах
- Позиционные аргументы — передаются по порядку: f(1, 2).
- Именованные (ключевые) аргументы — указываются по имени: f(x=1, y=2).
- Значения по умолчанию — используются, если аргумент не передан: def f(x=10).
- *args — собирает дополнительные позиционные аргументы в кортеж.
- **kwargs — собирает дополнительные именованные аргументы в словарь.
- Позиционно‑только (/) — аргументы, которые нельзя передать по имени.
- Ключево‑только (* ) — аргументы, которые можно передавать только по имени.
Порядок параметров в сигнатуре
Рекомендуемый порядок (PEP 8):
def func(pos_only, /, standard, *args, kw_only, **kwargs):
pass
Здесь:
- До символа / — позиционно‑только параметры (Python 3.8+).
- Между / и * — обычные (могут быть и позиционно, и по имени).
- После * — ключево‑только параметры (их можно передавать только по имени).
Позиционные и именованные аргументы: база
def greet(name, greeting="Привет", *, upper=False):
msg = f"{greeting}, {name}!"
return msg.upper() if upper else msg
print(greet("Анна")) # Привет, Анна!
print(greet("Анна", "Здравствуйте")) # Здравствуйте, Анна!
print(greet("Анна", upper=True)) # ПРИВЕТ, АННА!
Аргумент upper — ключево‑только. Его нельзя передать позиционно, что делает API явным и самодокументируемым.
Значения по умолчанию и частая ошибка с изменяемыми типами
Не используйте изменяемые объекты (list, dict, set) как значения по умолчанию. Они создаются один раз при определении функции:
def add_item(item, bucket=[]): # ПЛОХО
bucket.append(item)
return bucket
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — неожиданное «накапливание»
Правильно — использовать None и инициализировать внутри:
def add_item(item, bucket=None): # ХОРОШО
if bucket is None:
bucket = []
bucket.append(item)
return bucket
*args и **kwargs: гибкость без хаоса
*args собирает лишние позиционные аргументы, **kwargs — лишние именованные. Это полезно для обёрток и «проброса» параметров глубже по стеку.
def log_call(fn):
def wrapper(*args, **kwargs):
print(f"CALL {fn.__name__} args={args} kwargs={kwargs}")
return fn(*args, **kwargs)
return wrapper
@log_call
def power(base, exp=2):
return base ** exp
print(power(3)) # CALL power args=(3,) kwargs={'exp': 2}
print(power(2, exp=5)) # CALL power args=(2,) kwargs={'exp': 5}
Совет: не злоупотребляйте *args/**kwargs. Явные параметры делают API безопаснее. Используйте их там, где нужна расширяемость, например в промежуточных слоях и декораторах.
Распаковка при вызове: * и **
Операторы * и ** применяются не только в сигнатуре, но и при вызове функции для распаковки последовательностей и словарей:
def rectangle(area, /, *, color="blue", border=1):
return {"area": area, "color": color, "border": border}
data = (10 * 5,) # кортеж из одного элемента
options = {"color": "red", "border": 3}
print(rectangle(*data, **options)) # {'area': 50, 'color': 'red', 'border': 3}
Позиционно‑только и ключево‑только параметры
Позиционно‑только параметры через «/» фиксируют интерфейс и упрощают рефакторинг: вы можете менять имена без риска сломать внешний код, зависящий от именованных аргументов.
def normalize(value, /, *, method="minmax"):
# value — только позиционно, method — только по имени
...
Ключево‑только параметры через «*» повышают явность и читаемость, особенно для булевых флагов и параметров настройки.
Практические мини‑кейсы
1) Универсальная функция форматирования
def format_price(value, /, *, currency="₽", sep=" ", precision=2):
s = f"{value:,.{precision}f}".replace(",", "_").replace(".", ",").replace("_", sep)
return f"{s}{sep}{currency}".strip()
print(format_price(1234567.8))
print(format_price(1234567.8, currency="USD", precision=0))
2) Безопасная сборка SQL‑условий
def build_where(**filters):
# Пример: build_where(status='active', user_id=42)
parts, params = [], []
for field, value in filters.items():
parts.append(f"{field} = %s")
params.append(value)
clause = " AND ".join(parts) if parts else "1=1"
return clause, tuple(params)
where, params = build_where(status="active", user_id=42)
print(where) # status = %s AND user_id = %s
print(params) # ('active', 42)
3) CLI‑хелпер с пробросом опций
def run_job(name, /, *, dry_run=False, verbose=False):
if verbose:
print(f"Running {name}; dry_run={dry_run}")
if not dry_run:
print("Job executed")
cli_flags = {"dry_run": True, "verbose": True}
run_job("cleanup", **cli_flags)
Аннотации типов для *args и **kwargs
С типами всё просто на базовом уровне:
from typing import Any
def mean(*values: float) -> float:
if not values:
raise ValueError("empty")
return sum(values) / len(values)
# **kwargs всегда со строковыми ключами
from typing import Mapping
def configure(**options: Any) -> None:
# options: dict[str, Any]
print(options)
Для более строгой типизации ключевых параметров используют TypedDict и Unpack (Python 3.11+), но для «основ» достаточно подхода выше.
Частые ошибки и ловушки
- Изменяемые значения по умолчанию (list/dict/set) — используйте None и инициализацию внутри функции.
- Смена порядка аргументов без сохранения обратной совместимости — добавляйте новые настройки как ключево‑только параметры.
- Злоупотребление *args/**kwargs — усложняет автодополнение и проверку типов; предпочитайте явные параметры, если интерфейс стабилен.
- Конфликт имен при распаковке **dict — следите, чтобы ключи не дублировали уже переданные именованные аргументы.
Мини‑чек‑лист при проектировании функции
- Можно ли сделать часть параметров ключево‑только для читаемости?
- Есть ли разумные значения по умолчанию?
- Нужны ли позиционно‑только параметры для стабильности API?
- Действительно ли требуется *args/**kwargs или лучше явные аргументы?
- Добавьте аннотации типов и краткую docstring.
Заключение
Грамотная работа с аргументами функций в Python делает код понятнее, API — стабильнее, а разработку — быстрее. Освойте позиционные и именованные аргументы, значения по умолчанию, *args и **kwargs, распаковку и паттерны «/» и «*». Эти знания постоянно применяются в реальных проектах — от веб‑бэкенда до анализа данных.
Хотите быстрее и глубже прокачать основы с практикой и разбором задач? Посмотреть программу и записаться на курс «Python с Нуля до Гуру» — отличный следующий шаг.
-
-
Михаил Русаков
Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.