Python kararlarında 'has_key ()' veya 'in' kullanmalı mıyım?


911

Daha iyi ne yapacağımı merak ediyorum:

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

veya:

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

Yanıtlar:


1287

in kesinlikle daha pitonik.

Aslında has_key()Python 3.x'te kaldırıldı .


3
Bir ek olarak, Python 3'te, d.values içinde >>> 1 () denemek yerine tuşları, değerlerinde varlığını denetlemek için
riza

217
Kaçınılması gereken bir yarı-gotcha şunu yaptığınızdan emin olmaktır: "key in some_dict.keys ()" yerine "some_dict in key". Her ikisi de anlamsal olarak eşdeğerdir, ancak performans açısından ikincisi çok daha yavaştır (O (n) ve O (1)). Ben insanlar daha açık ve bu nedenle daha iyi olduğunu düşünerek "in dict.keys ()" yaptığını gördüm.
Adam Parkin

2
@AdamParkin Cevabımda yorumunu gösterdim stackoverflow.com/a/41390975/117471
Bruno Bronosky

8
@AdamParkin Python 3'te, keys()bir kopyadan ziyade sözlüğe sadece set benzeri bir görünümdür x in d.keys(), O da (1). Yine de, x in ddaha Pythonic.
Arthur Tacca

2
@AdamParkin İlginç, bunu görmedim. Çünkü aritmetik bir işlem yapıyor (hash hesaplama) ve bir arama yapıyor x in d.keys()bellek ayırma ile tam bir geçici nesne inşa ve imha gerekir varsayalım x in d.keys(). Bunun d.keys()sadece 10 kat daha uzun olduğunu unutmayın , ki bu hala uzun değildir. Kontrol etmedim ama hala sadece O (1) olduğundan eminim.
Arthur Tacca

253

in sadece zarafetle değil (ve kullanımdan kaldırılmamış ;-) değil, aynı zamanda performansta da eller kazanır, örneğin:

$ 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

Aşağıdaki gözlem her zaman doğru olmasa da, genellikle Python'da daha hızlı çözümün daha zarif ve Pythonic olduğunu fark edeceksiniz ; bu yüzden -mtimeitÇOK faydalı - sadece burada ve burada yüz nanosaniye tasarruf etmekle ilgili değil ! -)


4
Bunun için teşekkürler, "some_dict" in aslında O (1) çok daha kolay olduğunu doğrulayın (1999'u söylemek için 99'u artırmayı deneyin ve çalışma zamanının yaklaşık olarak aynı olduğunu göreceksiniz).
Adam Parkin

2
has_keyde O (1) gibi görünüyor.
dan-gph


42

Kullanım dict.has_key()if (ve ancak) kodunuzu önceki 2.3 den (zaman Python sürümleri tarafından çalıştırılabilir olması gerekir key in dicttanıtıldı).


1
2013'teki WebSphere güncellemesi, ana komut dosyası dili olarak Jython 2.1'i kullanır. Yani, maalesef, not ettiğinizden beş yıl sonra hala not etmekte fayda var.
ArtOfWarfare

23

inPerformansınızı gerçekten öldürdüğü bir örnek var .

Eğer kullanırsanız insadece uygular bir O'dan söz (1) konteyner __getitem__ve has_key()ancak __contains__(gibi bir O (1) O (K) aramasına arama dönecek inyoluyla doğrusal arama geri düşer __getitem__).

Düzeltme açıkça önemsizdir:

def __contains__(self, x):
    return self.has_key(x)

6
Bu cevap yayınlandığında uygulanabilir, ancak okuyucuların% 99,95'i bunu güvenle göz ardı edebilir. Gelen en Eğer bir şeyle çalışıyorsanız durumlarda, bu bunu bileceksiniz gizleyebilir.
wizzwizz4

2
Bu gerçekten bir sorun değil. has_key()olan Python 2 sözlüklere özgü . in/ __contains__kullanılacak doğru API'dir; tam bir taramanın kaçınılmaz olduğu kapsayıcılar için yine de birhas_key() yöntem yoktur ve eğer O (1) yaklaşımı varsa, o zaman kullanıma özel olacaktır ve sorun için doğru veri türünü seçmek için geliştiriciye kadar.
Martijn Pieters

15

has_keybir sözlük yöntemidir, ancak inherhangi bir koleksiyon üzerinde çalışır ve __contains__eksik olsa bile , inbulmak için koleksiyonu yinelemek için başka bir yöntem kullanır.


1
Ayrıca "x in xrange (90, 200) yineleyiciler üzerinde de çalışır <=> 90 <= x <200"
u0b34a0f6ae 28:09

1
…: Bu çok kötü bir fikir gibi görünüyor: 2 yerine 50 operasyon.
Clément

1
@ Clément Python 3'te, nesneler inüzerinde testler yapmak oldukça verimlidir range. xrangeYine de Python 2'deki verimliliğinden emin değilim . ;)
PM 2Ring

@ Clément Python 3'te değil; bir değerin aralıkta olup olmadığını __contains__önemsiz bir şekilde hesaplayabilir .
Martijn Pieters

1
@AlexandreHuat Zamanlamanız rangeher seferinde yeni bir örnek oluşturma yükünü içerir . Tek bir önceden var olan örnek kullanarak "aralıktaki tamsayı" testi zamanlamalarımda yaklaşık% 40 daha hızlı.
MisterMiyagi

14

Dict.has_key () yöntemi kullanımdan kaldırıldı, 'in' kullanın - sublime text editor 3

Burada 'çağlar' adında bir sözlük örneği aldım -

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"

6
Doğru, ama zaten cevaplandı, Stackoveflow'a hoş geldiniz, örnek için teşekkürler, her zaman cevapları kontrol edin!
igorgue

@igorgue ona aşağı vekiller emin değilim. Cevabı zaten cevaplanmış olanlara benzer olabilir, ancak bir örnek verir. Bu bir SO yanıtı olacak kadar değerli değil mi?
Akshat Agarwal

14

Adam Parkin'in yorumlarıyla Alex Martelli'nin performans testlerini genişletiyor ...

$ 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

Harika istatistikler, bazen örtük açık olmaktan daha iyi olabilir (en azından verimlilikte) ...
varun

Teşekkürler @varun. Bu cevabı unutmuştum. Bu tür testleri daha sık yapmam gerekiyor. İnsanların bir şeyler yapmak için The Best Way ™ ' i tartıştığı uzun konuları düzenli olarak okuyorum . Ama bunun kanıt elde etmenin ne kadar kolay olduğunu nadiren hatırlıyorum .
Bruno Bronosky

0

Böyle bir şey varsa:

t.has_key(ew)

Python 3.X ve üzeri sürümlerde çalıştırmak için aşağıdaki şekilde değiştirin:

key = ew
if key not in t

6
Hayır, testi ters çevirdin. değer başvuruları da sözlükte bir anahtarsa t.has_key(ew)döndürür . değer sözlükte değilse döndürür . Dahası, takma ad çok, çok gereksizdir. Doğru yazım . Bu, 8 yıl önce kabul edilen cevabın size söylediği şeydir. Trueewkey not in tTruekey = ewif ew in t
Martijn Pieters
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.