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.