*args и **kwargs в Python — полный разбор для аналитика
Коротко
*args и **kwargs — механизмы Python для работы с переменным числом аргументов. *args собирает позиционные аргументы в кортеж, **kwargs — именованные в словарь. На собеседованиях спрашивают регулярно: «Что такое *args/**kwargs?», «В чём разница?», «Напишите функцию, которая принимает произвольные параметры». Давайте разберёмся.
Что такое *args
Оператор * перед именем параметра говорит Python: «собери все оставшиеся позиционные аргументы в кортеж». Имя args — конвенция, можно назвать как угодно, но все привыкли к args.
def print_all(*args):
for item in args:
print(item)
print_all('SQL', 'Python', 'A/B-тесты')
# SQL
# Python
# A/B-тестыВнутри функции args — обычный кортеж (tuple). Можно итерироваться, обращаться по индексу, проверять длину.
Практический пример: агрегация метрик
def average(*values):
"""Среднее произвольного числа значений."""
if not values:
return 0
return sum(values) / len(values)
# Конверсия по дням
average(3.2, 4.1, 2.8, 3.5, 4.0) # 3.52*args с обычными параметрами
*args можно комбинировать с обычными аргументами. Обычные идут первыми:
def log_metric(metric_name, *values):
"""Логирует метрику и её значения."""
avg = sum(values) / len(values) if values else 0
print(f"{metric_name}: среднее = {avg:.2f}, точек = {len(values)}")
log_metric('DAU', 1200, 1350, 1100, 1400)
# DAU: среднее = 1262.50, точек = 4Что такое **kwargs
Оператор ** перед именем параметра собирает все оставшиеся именованные аргументы в словарь. Имя kwargs — конвенция (keyword arguments).
def create_user(**kwargs):
for key, value in kwargs.items():
print(f"{key} = {value}")
create_user(name='Анна', role='analyst', level='middle')
# name = Анна
# role = analyst
# level = middleВнутри функции kwargs — обычный dict. Ключи — строки (имена аргументов), значения — переданные значения.
Практический пример: гибкие фильтры
def build_filter(**kwargs):
"""Строит SQL WHERE из произвольных фильтров."""
conditions = []
for col, val in kwargs.items():
if isinstance(val, str):
conditions.append(f"{col} = '{val}'")
elif isinstance(val, (list, tuple)):
values = ', '.join(f"'{v}'" for v in val)
conditions.append(f"{col} IN ({values})")
else:
conditions.append(f"{col} = {val}")
return ' AND '.join(conditions)
build_filter(city='Moscow', age=25, status=['active', 'trial'])
# "city = 'Moscow' AND age = 25 AND status IN ('active', 'trial')"Порядок аргументов
Python требует строгий порядок параметров в определении функции:
def func(regular, *args, keyword_only, **kwargs):
pass- Обычные позиционные (
regular) *args- Keyword-only (после
*args, передаются только по имени) **kwargs
def process_data(source, *columns, fillna=0, **options):
print(f"Источник: {source}")
print(f"Колонки: {columns}")
print(f"fillna: {fillna}")
print(f"Опции: {options}")
process_data('sales.csv', 'revenue', 'users',
fillna=-1, encoding='utf-8', sep=';')
# Источник: sales.csv
# Колонки: ('revenue', 'users')
# fillna: -1
# Опции: {'encoding': 'utf-8', 'sep': ';'}Распаковка (unpacking)
Оператор * и ** работает не только в определении функции, но и при вызове — для распаковки коллекций.
Распаковка списка через *
def calc_growth(old, new):
return (new - old) / old * 100
metrics = [1000, 1250]
calc_growth(*metrics) # 25.0
# Эквивалентно: calc_growth(1000, 1250)Распаковка словаря через **
def create_report(title, period, metric):
return f"{title}: {metric} за {period}"
params = {'title': 'DAU Report', 'period': 'март 2026', 'metric': 1350}
create_report(**params) # 'DAU Report: 1350 за март 2026'
# Эквивалентно: create_report(title='DAU Report', period='март 2026', metric=1350)Распаковка в присваивании
first, *rest = [10, 20, 30, 40, 50]
# first = 10, rest = [20, 30, 40, 50]
head, *middle, tail = [1, 2, 3, 4, 5]
# head = 1, middle = [2, 3, 4], tail = 5Этот приём удобен при обработке данных — например, отделить заголовок CSV от строк.
Когда использовать в аналитическом коде
Обёртки над функциями. Декораторы, логирование, кэширование — всё это принимает *args, **kwargs, чтобы работать с любой функцией.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__}: {time.time() - start:.2f}s")
return result
return wrapper
@timer
def heavy_query(query, timeout=30):
# ... выполняем запрос
passГибкие API. Функции, которые принимают произвольные параметры и передают их дальше (в pandas, matplotlib, requests).
Агрегация данных. Функции, которые работают с произвольным числом значений: среднее, медиана, взвешенная сумма.
Частые ошибки
1. Мутабельный дефолт + kwargs. Не путайте с проблемой мутабельных значений по умолчанию — это разные вещи. **kwargs каждый раз создаёт новый dict.
2. Забыли звёздочку при вызове.
values = [1, 2, 3]
# Неправильно — передаст список как один аргумент:
average(values) # TypeError: unsupported operand type
# Правильно — распакует:
average(*values) # 2.03. Двойное определение аргумента.
def func(a, **kwargs):
pass
func(1, a=2) # TypeError: got multiple values for argument 'a'4. Порядок нарушен.
# Неправильно:
def func(**kwargs, *args): # SyntaxError
passВопросы с собеседований
— **Что такое *args и kwargs?
— *args собирает позиционные аргументы в кортеж, **kwargs — именованные в словарь. Позволяют функции принимать произвольное число аргументов.
— В каком порядке должны идти параметры? — Обычные → *args → keyword-only → **kwargs. Нарушение порядка — SyntaxError.
— *Что будет, если передать в args именованный аргумент? — Именованные аргументы не попадают в *args — они идут в **kwargs (если есть) или в конкретный параметр по имени.
— **Можно ли использовать *args и kwargs одновременно?
— Да. def func(*args, **kwargs) — принимает вообще всё. Типичный паттерн для декораторов и обёрток.
— Чем * отличается от ** при вызове функции?
— * распаковывает итерируемый объект (список, кортеж) в позиционные аргументы. ** распаковывает словарь в именованные аргументы. При определении функции — наоборот, упаковывают.
FAQ
Можно ли назвать параметры не args и kwargs?
Да, имена произвольные. *values и **options работают точно так же. Но args и kwargs — устоявшаяся конвенция, и отклоняться от неё без причины не стоит: код читают другие люди.
Как узнать, какие аргументы были переданы в kwargs?
kwargs — обычный словарь. Используйте kwargs.keys(), kwargs.get('param', default), проверку 'param' in kwargs. Для валидации можно поднять ошибку, если передан неизвестный ключ.
Когда лучше использовать *args/**kwargs, а когда явные аргументы?
Явные аргументы — всегда предпочтительнее: IDE подсказывает, ошибки ловятся раньше, код понятнее. *args/**kwargs нужны, когда число аргументов неизвестно заранее: декораторы, обёртки, функции-агрегаторы. Не используйте их как замену нормальной сигнатуре.
Потренируйте вопросы по Python на реальных задачах — откройте тренажёр. 1500+ вопросов, которые спрашивают на собеседованиях аналитика. Бесплатно.