Передача словаря в функцию в качестве параметров ключевых слов


346

Я хотел бы вызвать функцию в Python, используя словарь.

Вот некоторый код:

d = dict(param='test')

def f(param):
    print(param)

f(d)

Это печатает, {'param': 'test'}но я хотел бы просто напечатать test.

Я хотел бы, чтобы он работал аналогично для других параметров:

d = dict(p1=1, p2=2)
def f2(p1, p2):
    print(p1, p2)
f2(d)

Это возможно?

Ответы:


530

В конце разобрался для себя. Это просто, мне просто не хватало оператора ** для распаковки словаря

Итак, мой пример становится:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print p1, p2
f2(**d)

57
если вы хотите, чтобы это помогло другим, вам следует перефразировать ваш вопрос: проблема не в том, чтобы передать словарь, а в том, что вы хотели превратить диктат в параметры ключевого слова
Javier

11
Стоит отметить, что вы также можете распаковать списки для позиционных аргументов: f2 (* [1,2])
Мэтью Тревор

10
«разыменование»: обычный термин в данном контексте Python - «распаковать». :)
Mipadi

2
Это замечательно, просто используйте его с argparse / __ dict__, чтобы действительно было легко выполнять синтаксический анализ аргументов командной строки непосредственно в опциях для объекта класса.
Гор

1
по какой причине мы хотели бы распаковать словарь при передаче его в качестве аргумента функции?
Мона Джалал

128
In[1]: def myfunc(a=1, b=2):
In[2]:    print(a, b)

In[3]: mydict = {'a': 100, 'b': 200}

In[4]: myfunc(**mydict)
100 200

Несколько дополнительных деталей, которые могут быть полезны для понимания (вопросы, которые у меня возникли после прочтения и проверки):

  1. Функция может иметь параметры, которые не включены в словарь
  2. Вы не можете переопределить параметр, который уже находится в словаре
  3. В словаре не может быть параметров, которых нет в функции.

Примеры:

Номер 1: функция может иметь параметры, которые не включены в словарь

In[5]: mydict = {'a': 100}
In[6]: myfunc(**mydict)
100 2

Номер 2: Вы не можете переопределить параметр, который уже находится в словаре

In[7]: mydict = {'a': 100, 'b': 200}
In[8]: myfunc(a=3, **mydict)

TypeError: myfunc() got multiple values for keyword argument 'a'

Номер 3: в словаре не может быть параметров, которых нет в функции.

In[9]:  mydict = {'a': 100, 'b': 200, 'c': 300}
In[10]: myfunc(**mydict)

TypeError: myfunc() got an unexpected keyword argument 'c'

Как и было запрошено в комментариях, решением для номера 3 является фильтрация словаря на основе аргументов ключевых слов, доступных в функции:

In[11]: import inspect
In[12]: mydict = {'a': 100, 'b': 200, 'c': 300}
In[13]: filtered_mydict = {k: v for k, v in mydict.items() if k in [p.name for p in inspect.signature(myfunc).parameters.values()]}
In[14]: myfunc(**filtered_mydict)
100 200

Другой вариант - принять (и игнорировать) дополнительные kwargs в вашей функции:

In[15]: def myfunc2(a=None, **kwargs):
In[16]:    print(a)

In[17]: mydict = {'a': 100, 'b': 200, 'c': 300}

In[18]: myfunc2(**mydict)
100

Обратите внимание на то, что вы можете использовать позиционные аргументы и списки или кортежи практически так же, как и kwargs, вот более сложный пример, включающий позиционные и ключевые аргументы:

In[19]: def myfunc3(a, *posargs, b=2, **kwargs):
In[20]:    print(a, b)
In[21]:    print(posargs)
In[22]:    print(kwargs)

In[23]: mylist = [10, 20, 30]
In[24]: mydict = {'b': 200, 'c': 300}

In[25]: myfunc3(*mylist, **mydict)
10 200
(20, 30)
{'c': 300}

4
Использование распаковки с print.format особенно полезно. Например:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark

Старый вопрос, но все еще очень актуальный. Спасибо за подробный ответ. Знаете ли вы какие-либо способы обойти дело 3? Имеется в виду pythonically сопоставить элементы словаря с параметрами функции, когда в словаре больше элементов, чем параметров?
Spencer

2
@spencer решение было добавлено к ответу.
Дэвид Паркс

33

В Python это называется «распаковка», и вы можете найти немного об этом в руководстве . Я не согласен с документацией, особенно потому, что это фантастически полезно.


20
Лучше скопировать соответствующее содержание ссылки в ваш ответ, чем полагаться на то, что ссылка сохранилась до конца времени.
Ричард

3
@Richard - это глубокое философское мнение о сети, с которым я не мог не согласиться от души! Увы, мне не хватает места на этом поле, чтобы поделиться своим чудесным доказательством ...
llimllib

@llimllib, тогда я должен спросить доктора Уайлса!
Ричард

6

Вот и все - работает просто любой другой итеративный:

d = {'param' : 'test'}

def f(dictionary):
    for key in dictionary:
        print key

f(d)

Кажется, что люди отрицательно относятся к этому, поскольку он ответил на первоначальный вопрос, а не на перефразированный вопрос. Я предлагаю просто удалить этот пост сейчас.
Dotancohen

@dotancohen нет, это никогда не было правильно, он завершает работу со вторым блоком кода, который всегда был с вопросом. Это было слишком буквально, печать была примером.
Дейв Хиллиер,

Он действительно отвечает на вопрос, но не распаковывает словарь. Его подход совершенно обоснован, основываясь на опубликованном вопросе.
Natecat
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.