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
mnowotka
sumber

Jawaban:

311

Seperti ini:

host = connectionDetails.get('host', someDefaultValue)
MattH
sumber
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"
tamerlaha
sumber
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
Tim Pietzcker
sumber
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
Jerome Baum
sumber
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.

Sriram
sumber
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'
Laurent LAPORTE
sumber
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
Sampai
sumber
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"

Bobak Hashemi
sumber
Saya membesarkan hati Anda tetapi masalah dengan solusi Anda adalah bahwa dikts bekerja dengan [] tetapi lambdas bekerja dengan ()
yukashima huksay