Melewati kamus ke fungsi sebagai parameter kata kunci

346

Saya ingin memanggil fungsi dalam python menggunakan kamus.

Ini beberapa kode:

d = dict(param='test')

def f(param):
    print(param)

f(d)

Ini mencetak {'param': 'test'}tetapi saya ingin hanya mencetak test.

Saya ingin ini berfungsi serupa untuk parameter lainnya:

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

Apakah ini mungkin?

Dave Hillier
sumber

Jawaban:

528

Menemukannya sendiri pada akhirnya. Sederhana saja, saya baru saja melewatkan operator ** untuk membongkar kamus

Jadi contoh saya menjadi:

d = dict(p1=1, p2=2)
def f2(p1,p2):
    print p1, p2
f2(**d)
Dave Hillier
sumber
57
jika Anda ingin ini membantu orang lain, Anda harus mengulangi pertanyaan Anda: masalahnya bukan lewat kamus, yang Anda inginkan adalah mengubah dict menjadi parameter kata kunci
Javier
11
Perlu dicatat bahwa Anda juga dapat membongkar daftar ke argumen posisional: f2 (* [1,2])
Matthew Trevor
10
"dereference": istilah yang biasa, dalam konteks Python ini, adalah "unpack". :)
mipadi
2
Ini bagus, hanya digunakan dengan argparse / __ dict__ untuk membuatnya sangat mudah untuk melakukan argumen baris perintah parsing langsung ke opsi untuk objek kelas.
Horus
1
apa alasan kami ingin membongkar kamus saat meneruskannya sebagai argumen untuk suatu fungsi?
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

Beberapa detail tambahan yang mungkin berguna untuk diketahui (pertanyaan yang saya miliki setelah membaca ini dan pergi dan menguji):

  1. Fungsi dapat memiliki parameter yang tidak termasuk dalam kamus
  2. Anda tidak dapat mengganti parameter yang sudah ada di kamus
  3. Kamus tidak dapat memiliki parameter yang tidak ada dalam fungsi.

Contoh:

Nomor 1: Fungsi ini dapat memiliki parameter yang tidak termasuk dalam kamus

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

Nomor 2: Anda tidak dapat mengganti parameter yang sudah ada dalam kamus

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

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

Nomor 3: Kamus tidak dapat memiliki parameter yang tidak ada dalam fungsi.

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

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

Seperti yang diminta dalam komentar, solusi untuk Nomor 3 adalah menyaring kamus berdasarkan argumen kata kunci yang tersedia di fungsi:

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

Opsi lain adalah menerima (dan mengabaikan) kwarg tambahan di fungsi Anda:

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

Perhatikan lebih jauh daripada Anda dapat menggunakan argumen dan daftar posisi atau tuple secara efektif dengan cara yang sama seperti kwargs, berikut adalah contoh yang lebih maju yang menggabungkan argumen posisi dan kata kunci:

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
sumber
4
Menggunakan membongkar dengan format print.format sangat berguna. misal:'hello {greeting} {name}'.format( **{'name': 'Andrew', 'greeting': 'Mr'})
Martlark
Pertanyaan lama tapi masih sangat relevan. Terima kasih atas respon yang mendetail. Apakah Anda tahu cara untuk mengatasi kasus 3? Berarti secara python memetakan item kamus ke parameter fungsi, ketika ada lebih banyak item dalam kamus daripada ada parameter?
Spencer
2
@ Spencer solusi telah ditambahkan ke jawabannya.
David Parks
33

Dalam python, ini disebut "membongkar", dan Anda dapat menemukan sedikit tentang hal itu di tutorial . Dokumentasi itu menyebalkan, saya setuju, terutama karena betapa fantastiknya manfaatnya.

llimllib
sumber
20
Lebih baik menyalin konten tautan yang relevan ke dalam jawaban Anda, daripada mengandalkan tautan yang bertahan hingga akhir waktu.
Richard
3
@Richard, itu pendapat filosofis yang dalam tentang web, yang dengannya saya tidak bisa tidak setuju dengan sepenuh hati! Sayangnya, saya tidak punya ruang dalam margin di sini untuk membagikan bukti indah saya ...
llimllib
@llimllib, saya harus bertanya kepada Dr. Wiles kalau begitu!
Richard
6

Ini ya pergi - bekerja sembarang iterable lainnya:

d = {'param' : 'test'}

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

f(d)
Patrick Harrington
sumber
Tampaknya orang-orang merendahkan ini ketika menjawab pertanyaan awal, bukan pertanyaan yang diulang. Saya sarankan hapus saja posting ini sekarang.
dotancohen
@dotancohen tidak itu tidak pernah benar, ia gagal blok kode kedua yang selalu dengan pertanyaan. Dibutuhkan terlalu harfiah, cetakannya adalah contoh.
Dave Hillier
Itu memang menjawab pertanyaan, hanya saja tidak melakukannya melalui kamus membongkar. Pendekatannya sangat valid berdasarkan pertanyaan yang diposting.
Natecat