Почему f(*d, **d) вызывает TypeError

Почему f(*d, **d) вызывает TypeError

@python_quiz

Разберем этот квиз

Коротко: при распаковке словаря через * передаются ключи как позиционные аргументы (итерация по ключам dict), а через ** — ключи и значения как именованные аргументы. В конкретном примере ключ "a" попадает и в позиционные, и в именованные аргументы, из‑за чего возникает конфликт привязки аргумента.

Исходный код

python

Что делает *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 получает два значения, и интерпретатор выбрасывает исключение:

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

python

Обратите внимание: в первом примере 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 (если хотите передать пары ключ=значение), либо преобразуйте данные заранее так, чтобы не было пересечения имён параметров.

Report Page