Kamus dan nilai default


213

Dengan asumsi connectionDetailsadalah kamus Python, apa cara terbaik untuk mengembalikan kode seperti ini?

if "host" in connectionDetails:
    host = connectionDetails["host"]
else:
    host = someDefaultValue

Jawaban:


311

Seperti ini:

host = connectionDetails.get('host', someDefaultValue)

40
Perhatikan bahwa argumen kedua adalah nilai, bukan kunci.
Marcin

7
+1 untuk keterbacaan, tetapi if/elsejauh lebih cepat. Itu mungkin atau mungkin tidak berperan.
Tim Pietzcker

7
@ Tim, Bisakah Anda memberikan referensi mengapa if/elselebih cepat?
nishantjr

2
@Tim: Saya berasumsi bahwa salah satu keuntungan menggunakan bahasa tingkat yang lebih tinggi adalah bahwa penerjemah akan dapat 'melihat' di dalam fungsi dan mengoptimalkannya - bahwa pengguna tidak harus berurusan dengan optimasi mikro sebanyak . Bukankah itu tujuan dari kompilasi JIT?
nishantjr

3
@nishantjr: Python (setidaknya CPython, varian yang paling umum) tidak memiliki kompilasi JIT. PyPy mungkin memang menyelesaikan ini lebih cepat, tapi saya belum menginstalnya karena standar Python selalu cukup cepat untuk keperluan saya sejauh ini. Secara umum, itu tidak mungkin menjadi masalah dalam kehidupan nyata - jika Anda perlu menghitung angka waktu kritis, Python mungkin bukan bahasa pilihan ...
Tim Pietzcker

99

Anda juga dapat menggunakan defaultdictseperti ini:

from collections import defaultdict
a = defaultdict(lambda: "default", key="some_value")
a["blabla"] => "default"
a["key"] => "some_value"

Anda dapat melewati fungsi biasa alih-alih lambda:

from collections import defaultdict
def a():
  return 4

b = defaultdict(a, key="some_value")
b['absent'] => 4
b['key'] => "some_value"

7
Saya datang ke sini untuk beberapa masalah yang berbeda dari pertanyaan OP, dan solusi Anda benar-benar menyelesaikannya.
0xc0de

Saya akan memberi +1 tetapi sayangnya tidak cocok dengan getatau metode serupa.
0xc0de

Jawaban ini bermanfaat bagi saya untuk memastikan tambahan ke kamus termasuk kunci default. Implementasi saya agak terlalu panjang untuk dijelaskan dalam jawaban StackOverflow, jadi saya menulisnya di sini. persagen.com/2020/03/05/…
Victoria Stuart

24

Meskipun .get()ini adalah ungkapan yang bagus, ini lebih lambat daripada if/else(dan lebih lambat daripada try/exceptjika kehadiran kunci dalam kamus dapat diharapkan sebagian besar waktu):

>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="try:\n a=d[1]\nexcept KeyError:\n a=10")
0.07691968797894333
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="try:\n a=d[2]\nexcept KeyError:\n a=10")
0.4583777282275605
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="a=d.get(1, 10)")
0.17784020746671558
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="a=d.get(2, 10)")
0.17952161730158878
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="if 1 in d:\n a=d[1]\nelse:\n a=10")
0.10071221458065338
>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}", 
... stmt="if 2 in d:\n a=d[2]\nelse:\n a=10")
0.06966537335119938

3
Saya masih tidak melihat mengapa if/then akan lebih cepat. Kedua kasus memerlukan lookup kamus, dan kecuali seruan get()ini begitu jauh lebih lambat, apa account lain untuk perlambatan?
Jens

1
@ Jean: Panggilan fungsi mahal.
Tim Pietzcker

1
Yang seharusnya tidak menjadi masalah besar dalam kamus yang padat penduduk, benar? Berarti pemanggilan fungsi tidak akan terlalu berarti jika pencarian yang sebenarnya mahal. Mungkin hanya penting dalam contoh mainan.
AturSams

2
@zehelvion: Pencarian kamus O(1)terlepas dari ukuran kamus, sehingga overhead panggilan fungsi relevan.
Tim Pietzcker

35
itu aneh jika overhead memanggil fungsi akan membuat Anda memutuskan untuk tidak menggunakan get. Gunakan apa yang bisa dibaca dengan baik oleh sesama anggota tim Anda.
Jochen Bedersdorfer

19

Untuk beberapa standar yang berbeda coba ini:

connectionDetails = { "host": "www.example.com" }
defaults = { "host": "127.0.0.1", "port": 8080 }

completeDetails = {}
completeDetails.update(defaults)
completeDetails.update(connectionDetails)
completeDetails["host"]  # ==> "www.example.com"
completeDetails["port"]  # ==> 8080

3
Ini adalah solusi idiomatis yang baik, tetapi ada jebakan. Hasil yang tidak terduga dapat terjadi jika connectionDetails disertakan dengan Noneatau BlankString sebagai salah satu nilai dalam pasangan nilai kunci. The defaultskamus berpotensi memiliki salah satu dari nilai-nilai tidak sengaja blanked keluar. (lihat juga stackoverflow.com/questions/6354436 )
dreftymac

9

Ada metode dalam kamus python untuk melakukan ini: dict.setdefault

connectionDetails.setdefault('host',someDefaultValue)
host = connectionDetails['host']

Namun metode ini menetapkan nilai connectionDetails['host']untuk someDefaultValuejika kunci hostbelum ditentukan, tidak seperti apa pertanyaan yang diajukan.


1
Perhatikan bahwa setdefault()nilai pengembalian, jadi ini bekerja dengan baik: host = connectionDetails.setdefault('host', someDefaultValue). Berhati-hatilah karena akan disetel connectionDetails['host']ke nilai default jika kunci tidak ada di sana sebelumnya.
ash108

7

(ini jawaban yang terlambat)

Alternatifnya adalah dengan subkelas dictkelas dan mengimplementasikan __missing__()metode, seperti ini:

class ConnectionDetails(dict):
    def __missing__(self, key):
        if key == 'host':
            return "localhost"
        raise KeyError(key)

Contoh:

>>> connection_details = ConnectionDetails(port=80)

>>> connection_details['host']
'localhost'

>>> connection_details['port']
80

>>> connection_details['password']
Traceback (most recent call last):
  File "python", line 1, in <module>
  File "python", line 6, in __missing__
KeyError: 'password'

4

Menguji kecurigaan @Tim Pietzcker tentang situasi di PyPy (5.2.0-alpha0) untuk Python 3.3.5, saya menemukan bahwa memang keduanya .get()dan if/ elsecara melakukan yang serupa. Sebenarnya sepertinya dalam kasus if / else bahkan hanya ada satu pencarian jika kondisi dan tugas melibatkan kunci yang sama (bandingkan dengan kasus terakhir di mana ada dua pencarian).

>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:\n a=d[1]\nexcept KeyError:\n a=10")
0.011889292989508249
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="try:\n a=d[2]\nexcept KeyError:\n a=10")
0.07310474599944428
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(1, 10)")
0.010391917996457778
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="a=d.get(2, 10)")
0.009348208011942916
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 1 in d:\n a=d[1]\nelse:\n a=10")
0.011475925013655797
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:\n a=d[2]\nelse:\n a=10")
0.009605801998986863
>>>> timeit.timeit(setup="d={1:2, 3:4, 5:6, 7:8, 9:0}",
.... stmt="if 2 in d:\n a=d[2]\nelse:\n a=d[1]")
0.017342638995614834

1

Anda dapat menggunakan fungsi lamba untuk ini sebagai satu-liner. Buat objek baru connectionDetails2yang diakses seperti fungsi ...

connectionDetails2 = lambda k: connectionDetails[k] if k in connectionDetails.keys() else "DEFAULT"

Sekarang gunakan

connectionDetails2(k)

dari pada

connectionDetails[k]

yang mengembalikan nilai kamus jika kada di tombol, jika tidak maka akan kembali"DEFAULT"


Saya membesarkan hati Anda tetapi masalah dengan solusi Anda adalah bahwa dikts bekerja dengan [] tetapi lambdas bekerja dengan ()
yukashima huksay
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.