*args и **kwargs

*args и **kwargs

Иногда заранее неизвестно, сколько аргументов получит функция. Встроенная print() принимает любое количество значений, max() — тоже. Python позволяет создавать такие функции с помощью *args и **kwargs.

*args — переменное число позиционных аргументов

Звёздочка перед именем параметра означает: «собери все лишние позиционные аргументы в кортеж»:

def total(*args):
    return sum(args)

print(total(1, 2, 3))        # 6
print(total(10, 20))         # 30
print(total(1, 2, 3, 4, 5))  # 15
print(total())               # 0 — пустой кортеж

Внутри функции args — обычный кортеж. Имя args — конвенция, технически можно назвать иначе (*numbers, *values), но *args понятен всем Python-разработчикам.

Можно сочетать с обычными параметрами, но *args должен идти после них:

def log(level, *messages):
    for msg in messages:
        print(f"[{level}] {msg}")

log("INFO", "Сервер запущен", "Порт 8080")
# [INFO] Сервер запущен
# [INFO] Порт 8080

log("ERROR", "База недоступна")
# [ERROR] База недоступна

**kwargs — переменное число именованных аргументов

Двойная звёздочка собирает все лишние именованные аргументы в словарь:

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"  {key}: {value}")

print_info(name="Алиса", age=25, city="Москва")
# name: Алиса
# age: 25
# city: Москва

kwargs — конвенция (keyword arguments). Внутри функции это обычный словарь.

Практичный пример — функция создания HTML-тега:

def tag(name, content, **attrs):
    attr_str = " ".join(f'{k}="{v}"' for k, v in attrs.items())
    if attr_str:
        return f"<{name} {attr_str}>{content}</{name}>"
    return f"<{name}>{content}</{name}>"

print(tag("a", "Ссылка", href="https://python.org", target="_blank"))
# <a href="https://python.org" target="_blank">Ссылка</a>

print(tag("p", "Параграф"))
# <p>Параграф</p>

Комбинирование всех видов параметров

Python позволяет объединять обычные параметры, *args и **kwargs в одной функции. Обязательный порядок:

def full_function(a, b, *args, **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"kwargs={kwargs}")

full_function(1, 2, 3, 4, x=10, y=20)
# a=1, b=2
# args=(3, 4)
# kwargs={'x': 10, 'y': 20}

Порядок строго такой: обычные → *args → именованные с дефолтом → **kwargs.

Распаковка при вызове: * и **

Обратная операция — передать список/кортеж как позиционные аргументы, а словарь — как именованные:

def add(a, b, c):
    return a + b + c

numbers = [1, 2, 3]
print(add(*numbers))   # то же что add(1, 2, 3) → 6

params = {"a": 10, "b": 20, "c": 30}
print(add(**params))   # то же что add(a=10, b=20, c=30) → 60

Это особенно полезно при передаче аргументов «по цепочке»:

def create_user(name, role, active):
    print(f"Создан пользователь {name} (роль: {role}, активен: {active})")

defaults = {"role": "user", "active": True}
create_user("Алиса", **defaults)
# Создан пользователь Алиса (роль: user, активен: True)

Проверь себя

Что выведет следующий код?

def info(*args, **kwargs):
    print(len(args), len(kwargs))

info(1, 2, 3, x=10, y=20)

args = (1, 2, 3) → длина 3. kwargs = {'x': 10, 'y': 20} → длина 2. Вывод: 3 2.

Итог

  • *args собирает лишние позиционные аргументы в кортеж.
  • **kwargs собирает лишние именованные аргументы в словарь.
  • Порядок в сигнатуре: обычные → *args**kwargs.
  • *iterable при вызове распаковывает в позиционные аргументы.
  • **dict при вызове распаковывает в именованные аргументы.
  • Имена args и kwargs — конвенция, не ключевые слова.

В следующем уроке разберём область видимости переменных: почему переменная внутри функции не видна снаружи, и как это изменить с помощью global и nonlocal.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике