Przekazanie słownika do funkcji jako parametrów słowa kluczowego

346

Chciałbym wywołać funkcję w Pythonie za pomocą słownika.

Oto kod:

d = dict(param='test')

def f(param):
    print(param)

f(d)

To drukuje, {'param': 'test'}ale chciałbym, żeby to po prostu wydrukować test.

Chciałbym, aby działał podobnie dla większej liczby parametrów:

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

czy to możliwe?

Dave Hillier
źródło

Odpowiedzi:

528

W końcu to wymyśliłem. To proste, po prostu brakowało mi operatora **, aby rozpakować słownik

Tak więc mój przykład brzmi:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print p1, p2
f2(**d)
Dave Hillier
źródło
57
jeśli chcesz, aby pomogło to innym, przeformułuj swoje pytanie: problemem nie było przekazanie słownika, chciałem przekształcić dyktat w parametry słowa kluczowego
Javier
11
Warto zauważyć, że można również rozpakowywać listy do argumentów pozycyjnych: f2 (* [1,2])
Matthew Trevor,
10
„dereferencja”: zwykłym terminem w tym kontekście Pythona jest „rozpakuj”. :)
mipadi
2
Jest to świetne, po prostu użyłem go z argparse / __ dict__, aby naprawdę łatwo było parsować argumenty wiersza poleceń bezpośrednio w opcjach dla obiektu klasy.
Horus
1
z jakiego powodu chcielibyśmy rozpakować słownik, przekazując go jako argument funkcji?
Mona Jalal
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

Kilka dodatkowych szczegółów, które mogą być pomocne w poznaniu (pytania, które miałem po przeczytaniu tego i poszedłem i przetestowałem):

  1. Funkcja może mieć parametry, które nie są uwzględnione w słowniku
  2. Można nie zastąpi parametr, który jest już w słowniku
  3. Słownik nie może mieć parametrów, które nie są w funkcji.

Przykłady:

Numer 1: Funkcja może mieć parametry, które nie są uwzględnione w słowniku

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

Numer 2: Nie możesz przesłonić parametru, który jest już w słowniku

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

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

Numer 3: Słownik nie może mieć parametrów, które nie są w funkcji.

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

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

Zgodnie z żądaniem w komentarzach rozwiązaniem numeru 3 jest filtrowanie słownika na podstawie argumentów słów kluczowych dostępnych w funkcji:

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

Inną opcją jest zaakceptowanie (i zignorowanie) dodatkowych kwargs w twojej funkcji:

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

Zauważ, że dalej nie możesz używać argumentów pozycyjnych i list lub krotek w taki sam sposób jak kwargs, oto bardziej zaawansowany przykład obejmujący zarówno argumenty pozycyjne, jak i słowa kluczowe:

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}
David Parks
źródło
4
Korzystanie z rozpakowywania za pomocą print.format jest szczególnie przydatne. np .:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark,
Stare pytanie, ale wciąż bardzo aktualne. Dziękuję za szczegółową odpowiedź. Czy znasz jakieś sposoby obejścia przypadku 3? Czy pythonicznie oznacza odwzorowanie elementów słownika na parametry funkcji, gdy w słowniku jest więcej elementów niż parametrów?
spencer
2
@spencer do odpowiedzi dodano rozwiązanie.
David Parks
33

W Pythonie nazywa się to „rozpakowywaniem” i można o tym trochę przeczytać w samouczku . Zgadzam się z tym, że dokumentacja jest do bani, szczególnie ze względu na to, jak fantastycznie jest przydatna.

llimllib
źródło
20
Lepiej jest skopiować odpowiednią treść linku do swojej odpowiedzi, niż polegać na linku, który przetrwał do końca czasu.
Richard
3
@Richard to głęboka filozoficzna opinia o sieci, z którą nie mogłem się bardziej nie zgodzić! Niestety, brakuje mi miejsca na tym marginesie, aby podzielić się moim wspaniałym dowodem ...
llimllib
@llimllib, będę musiał zapytać doktora Wilesa!
Richard
6

Proszę bardzo - działa tylko każda inna iterowalna:

d = {'param' : 'test'}

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

f(d)
Patrick Harrington
źródło
Wygląda na to, że ludzie głosują za tym, ponieważ odpowiada on na pierwotne pytanie, a nie na przeformułowane pytanie. Sugeruję teraz usunięcie tego postu.
dotancohen,
@dotancohen nie, to nigdy nie było poprawne, zawodzi drugi blok kodu, który zawsze był z pytaniem. Wziął to zbyt dosłownie, wydruk był przykładem.
Dave Hillier
Odpowiada jednak na pytanie, po prostu nie robi tego poprzez rozpakowywanie słownika. Jego podejście jest całkowicie uzasadnione na podstawie zadanego pytania.
Natecat