Аргументы: *args, **kwargs, / и *

⏱ 30 мин · 🧩 заданий: 5

Виды параметров

def f(pos_only, /, normal, *args, kw_only, **kwargs):
    ...
Вид Как передаётся Пример
только позиционный (до /) только по позиции len(obj)
обычный по позиции или по имени f(1) или f(x=1)
*args все лишние позиционные — в кортеж print(*values)
только именованный (после * или *args) только по имени sorted(x, key=..., reverse=...)
**kwargs все лишние именованные — в словарь dict(**fields)

Порядок в объявлении строгий: позиционные → / → обычные → *args (или просто *) → только именованные → **kwargs.

*args

def log(level, *messages):
    print(f"[{level}]", type(messages), messages)
    print(f"[{level}]", " ".join(str(m) for m in messages))

log("INFO", "Сервер", "запущен", "на порту", 8000)
log("WARN")

*args собирает лишние позиционные аргументы в кортеж. Имя args — соглашение; важна звёздочка.

**kwargs

def create_user(name, **extra):
    print(type(extra), extra)
    user = {"name": name}
    user.update(extra)
    return user

print(create_user("Анна", age=28, city="Москва"))
print(create_user("Борис"))

**kwargs собирает лишние именованные аргументы в словарь (с сохранением порядка).

Только именованные параметры

Параметры после * (или после *args) можно передать только по имени. Это защищает от ошибок с флагами:

def send(to, subject, *, urgent=False, retries=3):
    return f"→ {to}: {subject} urgent={urgent} retries={retries}"

print(send("anna@x.ru", "Отчёт", urgent=True))
print(send("anna@x.ru", "Отчёт", True))   # TypeError: что значило бы True — непонятно

Правило хорошего API

Булевы флаги и редкие настройки делайте только именованными: вызов send(..., urgent=True) читается однозначно, а send(..., True, 5) — нет.

Только позиционные параметры (Python 3.8+)

Параметры до / нельзя передать по имени. Так можно позже переименовать параметр, не ломая чужой код, и принимать имя name в **kwargs:

def distance(x, y, /):
    return (x ** 2 + y ** 2) ** 0.5

print(distance(3, 4))
print(distance(x=3, y=4))     # TypeError: positional-only arguments passed as keyword

Многие встроенные функции так устроены: len(obj=[1]) не работает.

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

def point(x, y, z=0):
    return f"({x}, {y}, {z})"

coords = [1, 2, 3]
opts = {"y": 20, "z": 30}
print(point(*coords))           # позиционно из списка
print(point(10, **opts))        # по имени из словаря
print(point(*[1], **{"y": 2}))

print(*["a", "b", "c"], sep="-")   # print принимает *values

Проброс аргументов

Комбинация *args, **kwargs позволяет написать функцию-обёртку, которая передаёт любые аргументы дальше, не зная их состава. Это основа декораторов:

def logged(func, *args, **kwargs):
    print(f"Вызов {func.__name__} с args={args} kwargs={kwargs}")
    result = func(*args, **kwargs)
    print(f"Результат: {result}")
    return result

logged(pow, 2, 10)
logged(sorted, [3, 1, 2], reverse=True)

Ошибки, которые стоит узнавать

def f(a, b):
    return a + b

for call in ["f(1)", "f(1, 2, 3)", "f(1, a=2)", "f(1, c=2)"]:
    try:
        eval(call)
    except TypeError as e:
        print(f"{call:12} → {e}")

Итог

  • *args — кортеж лишних позиционных, **kwargs — словарь лишних именованных.
  • После * — только именованные параметры; до / — только позиционные.
  • При вызове *seq и **mapping распаковывают аргументы.
  • def wrapper(*args, **kwargs): return func(*args, **kwargs) — универсальный проброс.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Программа на Python

Напишите функцию total(*nums), которая принимает любое количество чисел и возвращает их сумму. total() → 0, total(1, 2, 3) → 6.

Какие тесты проверяют код (4)
assert total() == 0
assert total(5) == 5
assert total(1, 2, 3) == 6
assert total(*range(101)) == 5050
Задание 2 Программа на Python

Напишите функцию make_tag(tag, text, **attrs), которая возвращает HTML-тег. Атрибуты выводятся в порядке передачи: make_tag("a", "Сайт", href="/", target="_blank") → '<a href="/" target="_blank">Сайт</a>'. Без атрибутов: make_tag("b", "x") → '<b>x</b>'.

Какие тесты проверяют код (2)
assert make_tag("b", "x") == "<b>x</b>"
assert make_tag("a", "Сайт", href="/", target="_blank") == '<a href="/" target="_blank">Сайт</a>'
Задание 3 Заполнить таблицу

def f(a, b=2, *args, c, d=4, **kwargs): ... — какой вызов допустим?

ДопустимTypeError
f(1, c=3)
f(1)
f(1, 2, 3, 4, c=5, x=6)
f(c=3, a=1)
f(1, 2, 3)
Отметьте ячейку — «+», пустая — «−».
Задание 4 Программа на Python

Напишите функцию connect(host, port=5432, *, timeout=10, ssl=False): timeout и ssl должны передаваться только по имени. Функция возвращает строку "host:port timeout=… ssl=…".

Какие тесты проверяют код (3)
assert connect("db") == "db:5432 timeout=10 ssl=False"
assert connect("db", 6543, ssl=True) == "db:6543 timeout=10 ssl=True"
try:
    connect("db", 1, 30)
    ok = False
except TypeError:
    ok = True
assert ok, "connect(\"db\", 1, 30) должен вызывать TypeError: timeout — только по имени"

Задание 5 Один вариант ответа

Что означает / в def pow(base, exp, /, mod=None)?

Войдите, чтобы отмечать пройденные уроки.