Haruskah saya menggunakan 'has_key ()' atau 'in' pada Python dicts?

911

Saya bertanya-tanya apa yang lebih baik untuk dilakukan:

d = {'a': 1, 'b': 2}
'a' in d
True

atau:

d = {'a': 1, 'b': 2}
d.has_key('a')
True
igorgue
sumber

Jawaban:

1287

in jelas lebih pythonic.

Bahkan has_key()telah dihapus dengan Python 3.x .

tonfa
sumber
3
Sebagai tambahan, dalam Python 3, untuk memeriksa keberadaan nilai, alih-alih kunci, coba >>> 1 dalam d.values ​​()
riza
217
Satu semi-gotcha yang harus dihindari adalah memastikan Anda melakukannya: "masukkan some_dict" daripada "key some_dict.keys ()". Keduanya setara secara semantik, tetapi kinerja-yang kedua jauh lebih lambat (O (n) vs O (1)). Saya telah melihat orang-orang melakukan "dalam dict.keys ()" berpikir itu lebih eksplisit & oleh karena itu lebih baik.
Adam Parkin
2
@AdamParkin Saya menunjukkan komentar Anda dalam jawaban saya stackoverflow.com/a/41390975/117471
Bruno Bronosky
8
@AdamParkin Dalam Python 3, keys()hanya tampilan seperti set ke kamus daripada salinan, begitu x in d.keys()juga O (1). Tetap saja, x in dini lebih Pythonic.
Arthur Tacca
2
@AdamParkin Menarik, saya tidak melihat itu. Saya kira itu karena x in d.keys()harus membangun dan menghancurkan objek sementara, lengkap dengan alokasi memori yang menyertainya, di mana x in d.keys()hanya melakukan operasi aritmatika (menghitung hash) dan melakukan pencarian. Perhatikan bahwa d.keys()hanya sekitar 10 kali selama ini, yang masih belum terlalu lama. Saya belum memeriksa tetapi saya masih cukup yakin itu hanya O (1).
Arthur Tacca
253

in menang dengan mudah, tidak hanya dalam keanggunan (dan tidak ditinggalkan ;-) tetapi juga dalam kinerja, misalnya:

$ python -mtimeit -s'd=dict.fromkeys(range(99))' '12 in d'
10000000 loops, best of 3: 0.0983 usec per loop
$ python -mtimeit -s'd=dict.fromkeys(range(99))' 'd.has_key(12)'
1000000 loops, best of 3: 0.21 usec per loop

Meskipun pengamatan berikut tidak selalu benar, Anda akan melihat bahwa biasanya , dalam Python, solusi yang lebih cepat lebih elegan dan Pythonic; itu sebabnya -mtimeitsangat membantu - ini bukan hanya tentang menyelamatkan seratus nanodetik di sana-sini! -)

Alex Martelli
sumber
4
Terima kasih untuk ini, membuat verifikasi bahwa "di some_dict" sebenarnya O (1) jauh lebih mudah (coba tingkatkan 99 menjadi mengatakan 1999, dan Anda akan menemukan runtime hampir sama).
Adam Parkin
2
has_keytampaknya juga O (1).
dan-gph
96

Menurut python docs :

has_key()tidak digunakan lagi key in d.

Nadia Alramli
sumber
1
has_key()sekarang dihapus dengan Python 3
Vadim Kotov
42

Gunakan dict.has_key()jika (dan hanya jika) kode Anda harus dapat dijalankan oleh versi Python lebih awal dari 2.3 (saat key in dictdiperkenalkan).

John Machin
sumber
1
Pembaruan WebSphere pada 2013 menggunakan Jython 2.1 sebagai bahasa skrip utama. Jadi ini sayangnya masih merupakan hal yang berguna untuk dicatat, lima tahun setelah Anda mencatatnya.
ArtOfWarfare
23

Ada satu contoh di mana in sebenarnya membunuh kinerja Anda.

Jika Anda menggunakan inpada wadah O (1) yang hanya mengimplementasikan __getitem__dan has_key()tetapi tidak, __contains__Anda akan mengubah pencarian O (1) menjadi pencarian O (N) (seperti injatuh kembali ke pencarian linear melalui__getitem__ ).

Perbaikan jelas sepele:

def __contains__(self, x):
    return self.has_key(x)
schlenk
sumber
6
Jawaban ini berlaku ketika diposting, tetapi 99,95% pembaca dapat dengan aman mengabaikannya. Dalam kebanyakan kasus, jika Anda bekerja dengan sesuatu yang tidak jelas ini Anda akan mengetahuinya.
wizzwizz4
2
Ini benar-benar bukan masalah. has_key()adalah khusus untuk Python 2 kamus . in/ __contains__adalah API yang benar untuk digunakan; bagi mereka wadah di mana scan penuh tidak dapat dihindari tidak ada has_key()metode pula , dan jika ada O a (1) pendekatan maka yang akan digunakan-kasus tertentu dan begitu sampai pengembang untuk memilih tipe data yang tepat untuk masalah tersebut.
Martijn Pieters
15

has_keyadalah metode kamus, tetapi inakan bekerja pada koleksi apa pun, dan bahkan ketika __contains__hilang, inakan menggunakan metode lain untuk mengulangi koleksi untuk mengetahuinya.

u0b34a0f6ae
sumber
1
Dan juga bekerja pada iterator "x in xrange (90, 200) <=> 90 <= x <200"
u0b34a0f6ae
1
...: Ini sepertinya ide yang sangat buruk: 50 operasi bukannya 2.
Clément
1
@ Clément Dalam Python 3, sebenarnya cukup efisien untuk melakukan intes pada rangeobjek. Saya tidak begitu yakin tentang efisiensinya pada Python 2 xrange. ;)
PM 2Ring
@ Clément tidak dalam Python 3; __contains__dapat dengan mudah menghitung apakah suatu nilai berada dalam kisaran atau tidak.
Martijn Pieters
1
@AlexandreHuat Waktu Anda termasuk overhead untuk membuat rangeinstance baru setiap kali. Dengan menggunakan contoh tunggal, yang sudah ada sebelumnya , tes "integer in range" sekitar 40% lebih cepat dalam timing saya.
MisterMiyagi
14

Solusi untuk dict.has_key () sudah tidak digunakan lagi, gunakan 'in' - editor teks luhur 3

Di sini saya telah mengambil contoh kamus bernama 'age' -

ages = {}

# Add a couple of names to the dictionary
ages['Sue'] = 23

ages['Peter'] = 19

ages['Andrew'] = 78

ages['Karren'] = 45

# use of 'in' in if condition instead of function_name.has_key(key-name).
if 'Sue' in ages:

    print "Sue is in the dictionary. She is", ages['Sue'], "years old"

else:

    print "Sue is not in the dictionary"
Greena modi
sumber
6
Benar, tapi sudah dijawab, selamat datang di Stackoveflow, terima kasih untuk contohnya, selalu periksa jawabannya!
igorgue
@igorgue aku tidak yakin tentang downvotes padanya. Jawabannya mungkin mirip dengan yang sudah dijawab, tetapi dia memberikan contoh. Bukankah itu cukup layak untuk menjadi jawaban SO?
Akshat Agarwal
14

Memperluas tes kinerja Alex Martelli dengan komentar Adam Parkin ...

$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 301, in main
    x = t.timeit(number)
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
    d.has_key(12)
AttributeError: 'dict' object has no attribute 'has_key'

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(  99))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0872 usec per loop

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(1999))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0858 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d'
10000000 loops, best of 3: 0.031 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d'
10000000 loops, best of 3: 0.033 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d.keys()'
10000000 loops, best of 3: 0.115 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d.keys()'
10000000 loops, best of 3: 0.117 usec per loop
Bruno Bronosky
sumber
Statistik luar biasa, terkadang implisit mungkin lebih baik daripada eksplisit (setidaknya dalam efisiensi) ...
varun
Terima kasih, @varun. Saya lupa tentang jawaban ini. Saya perlu melakukan pengujian semacam ini lebih sering. Saya secara teratur membaca utas panjang di mana orang berdebat tentang The Best Way ™ untuk melakukan sesuatu. Tapi saya jarang ingat betapa mudahnya mendapatkan bukti .
Bruno Bronosky
0

Jika Anda memiliki sesuatu seperti ini:

t.has_key(ew)

ubah ke bawah untuk berjalan di Python 3.X ke atas:

key = ew
if key not in t
Harshita Jhavar
sumber
6
Tidak, Anda membalikkan tes. t.has_key(ew)kembali Truejika ewreferensi nilai juga merupakan kunci dalam kamus. key not in tkembali Truejika nilainya tidak ada dalam kamus. Apalagi key = ewalias sangat, sangat berlebihan. Ejaan yang benar adalah if ew in t. Itulah jawaban yang diterima dari 8 tahun sebelumnya yang sudah Anda katakan.
Martijn Pieters