*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
  1. Обычные позиционные (regular)
  2. *args
  3. Keyword-only (после *args, передаются только по имени)
  4. **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.0

3. Двойное определение аргумента.

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+ вопросов, которые спрашивают на собеседованиях аналитика. Бесплатно.