Короткий ответ
*args собирает произвольное количество позиционных аргументов в tuple, а kwargs собирает произвольные именованные аргументы в dict — это позволяет писать функции с гибким числом параметров и универсальные обёртки, прозрачно передающие аргументы дальше.
Как это работает подробнее
При определении функции *args должен идти после обычных позиционных параметров, kwargs — последним. При вызове все "лишние" позиционные аргументы попадают в args как кортеж, все лишние именованные — в kwargs как словарь. При вызове другой функции func(*args, kwargs) происходит обратная операция — распаковка.
Пример кода
example.py
def call_and_log(func, *args, **kwargs):
print(f"Вызов {func.__name__} с args={args}, kwargs={kwargs}")
return func(*args, **kwargs)
def create_user(name, age, role="user"):
return {"name": name, "age": age, "role": role}
result = call_and_log(create_user, "Alice", age=30, role="admin")
print(result) # {'name': 'Alice', 'age': 30, 'role': 'admin'}Что использовать на практике
- Практика. Типичное применение — декораторы и обёртки (
logging_wrapper(*args, kwargs)), которые не знают заранее сигнатуру оборачиваемой функции, но должны передать в неё все аргументы как есть. Также используется для функций с опциональными параметрами конфигурации, где явный список был бы слишком длинным.
Подводные камни
- Риски. Злоупотребляют
kwargsтам, где нужны явные, задокументированные именованные параметры — это ухудшает читаемость и подсказки IDE/статическую типизацию. Также путают порядок: позиционные параметры, затем*args, затем параметры только по имени (keyword-only), затемkwargs.