PythonJuniorTechnical
В чём разница между *args и **kwargs?
*args собирает позиционные аргументы в кортеж, **kwargs — именованные в словарь; используйте *args для вариативного числа однородных значений, **kwargs для опциональных настроек или forwarding-обёрток.
Механизм *args и **kwargs
*args позволяет функции принять произвольное количество позиционных аргументов; внутри функции они доступны как tuple. **kwargs принимает произвольные именованные аргументы и представляет их как dict[str, Any]. Имена args и kwargs — соглашение, а не синтаксическое требование.
Базовые примеры
def total(*args: float) -> float:
"""Сумма любого числа слагаемых."""
return sum(args)
print(total(1, 2, 3)) # 6
print(total()) # 0 — пустой кортеж не вызывает ошибку
def configure(**kwargs: str) -> dict[str, str]:
"""Принять произвольный набор настроек."""
allowed = {"host", "port", "db"}
unknown = set(kwargs) - allowed
if unknown:
raise ValueError(f"Unknown keys: {unknown}")
return kwargs
print(configure(host="localhost", db="prod")) # {'host': 'localhost', 'db': 'prod'}
Совместное использование
def log(level: str, *messages: str, sep: str = " ", **meta: str) -> None:
print(f"[{level}]", sep.join(messages), meta)
log("INFO", "user", "logged in", sep="|", user_id="42", ip="1.2.3.4")
# [INFO] user|logged in {'user_id': '42', 'ip': '1.2.3.4'}
Порядок параметров строго: обычные позиционные → *args → keyword-only → **kwargs.
Forwarding и декораторы
from functools import wraps
from typing import Any, Callable, TypeVar
F = TypeVar("F", bound=Callable[..., Any])
def retry(times: int = 3) -> Callable[[F], F]:
def decorator(fn: F) -> F:
@wraps(fn)
def wrapper(*args: Any, **kwargs: Any) -> Any:
for attempt in range(1, times + 1):
try:
return fn(*args, **kwargs)
except Exception as exc:
if attempt == times:
raise
print(f"Retry {attempt}/{times} after {exc}")
return wrapper # type: ignore[return-value]
return decorator
@retry(times=3)
def fetch_data(url: str, timeout: float = 5.0) -> bytes:
raise ConnectionError("no route")
Unpacking при вызове
def connect(host: str, port: int, db: str) -> str:
return f"{host}:{port}/{db}"
args = ("localhost", 5432)
kwargs = {"db": "jobstream"}
print(connect(*args, **kwargs)) # localhost:5432/jobstream
Подводные камни
- Функция с
*argsтеряет явную сигнатуру — IDE и mypy не смогут проверить типы аргументов без аннотацийTypeVarTuple(Python 3.11+). **kwargsпринимает только строковые ключи; попытка передать нестроковый ключ через**{1: 'x'}вызоветTypeError.- Мутабельные значения по умолчанию (
def f(x=[]):) — отдельная ловушка;**kwargsеё не устраняет, если вы сами создаёте изменяемые defaults. - Порядок: если поставить
**kwargsперед*args—SyntaxError. - Чрезмерное использование
**kwargsв публичном API скрывает контракт функции и усложняет рефакторинг. - При наследовании и
super().__init__(*args, **kwargs)легко случайно «проглотить» лишний аргумент, не вызвав ошибки. *argsвнутри функции — неизменяемый кортеж; если нужен список, явно конвертируйте:items = list(args).
Common mistakes
- Описывать args kwargs только как термин и не показывать механизм на минимальном примере.
- Игнорировать ошибки, пустые данные, конкурентный доступ или границы транзакции.
- Не связывать поведение с официальным контрактом Python и реальной эксплуатацией.
What the interviewer is testing
- Объясняет args kwargs через последовательность действий, а не через набор ключевых слов.
- Приводит короткий кодовый пример или production-сценарий с ожидаемым поведением.
- Называет хотя бы один риск: производительность, безопасность, транзакции, память или сопровождение.