*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.