Почему f(*d, **d) вызывает TypeError
@python_quizРазберем этот квиз
Коротко: при распаковке словаря через * передаются ключи как позиционные аргументы (итерация по ключам dict), а через ** — ключи и значения как именованные аргументы. В конкретном примере ключ "a" попадает и в позиционные, и в именованные аргументы, из‑за чего возникает конфликт привязки аргумента.
Исходный код

Что делает *d и **d
- *d — распаковывает итерируемый объект. Для словаря это последовательность его ключей. Для d = {'a':1, 'b':2} результат — последовательность ['a', 'b'] (порядок в общем случае зависит от версии Python и вставок, но ключи именно так).
- При вызове f(*d) фактически выполняется f('a', 'b').
- **d — распаковывает ключи и значения как именованные аргументы:
a=1,b=2.
- При вызове f(**d) фактически выполняется f(a=1, b=2).
Почему возникает ошибка
При одновременном использовании *d и d вызов f(*d, d) разворачивается примерно в f('a', 'b', a=1, b=2). При привязке аргументов:
- Первый позиционный аргумент 'a' привязывается к параметру a.
- Затем ключ
a=1из именованных аргументов пытается тоже присвоить параметру a.
Это даёт конфликт — аргумент a получает два значения, и интерпретатор выбрасывает исключение:

Демонстрация альтернатив и результатов

Обратите внимание: в первом примере a — это строка 'a' (именно ключ словаря), а во втором — значение 1.
Разбор вариантов ответов (логика)
- Вариант с сообщением о том, что функция получит несколько значений для аргумента a — соответствует реальному поведению: конфликт привязки вызывает
TypeError. - Вариант с выводом вида a ('b',) {'a': 1, 'b': 2} неверен, потому что выполнение не доходит до печати — вызывается исключение до входа в функцию.
- Вариант с выводом a ('b',) {'b': 2} описывает поведение при вызове f(*d) и f(d) по отдельности, но не для одновременного f(*d, d).
- Вариант о том, что ключи ** должны быть строками и поэтому ошибка, не применим: в нашем словаре ключи действительно строки, так что ошибок типа "keywords must be strings" не возникает.
Вывод
При распаковке одного и того же словаря одновременно через * и легко получить дублирование аргументов: ключ может попасть и в позиционные, и в именованные параметры. Это ломает привязку аргументов и вызывает TypeError. Чтобы избежать ошибки, используйте либо только *d (если хотите передать ключи как позиционные), либо только d (если хотите передать пары ключ=значение), либо преобразуйте данные заранее так, чтобы не было пересечения имён параметров.