Python - 'Eğer dikte foo' vs 'deneyin: dict [foo]'


24

Bu, ördek yazmanın doğası ve pitonik kalmayla ilgili daha az sorun olduğunu sanıyorum.

Her şeyden önce - özellikle, diskin yapısı oldukça tahmin edilebilir olduğunda ve verilen bir anahtar tipik olarak mevcut olmadığında ama bazen olduğu zaman, ilk önce iki yaklaşımı düşünüyorum:

if myKey in dict:
    do_some_work(dict[myKey])
else:
    pass

Ve elbette Ye Olde 'affetme ve izin verme' yaklaşımı.

try:
    do_some_work(dict[myKey])
except KeyError:
    pass

Bir seyahatci Python adamı olarak, ikincisinin çok fazla tercih ettiğini gördüğümü hissediyorum, ki sadece garip hissettiriyor çünkü Python'da doktorlar , başarının yokluğununtry/excepts aksine gerçek bir hata olduğunda tercih ediliyor gibi görünüyor ?

Zaman zaman belirti içinde myDict'te anahtar yoksa ve her zaman bu anahtara sahip olmadığı biliniyorsa , bir deneme / bağlamsal yanıltıcılık dışında mı? Bu bir programlama hatası değildir, yalnızca verinin bir gerçeğidir - bu dikte sadece belirli bir tuşa sahip değildi.

Bu, özellikle denemenin çok fazla hata yakalamadığından emin olmak için gerçekten yararlı görünen görünüyor / else / else sözdizimine baktığınızda çok önemli görünüyor. Gibi bir şey yapabilirsin:

try:
    foo += bar
except TypeError:
    pass
else:
    return some_more_work(foo)

Bu, muhtemelen bazı kötü kodların sonucu olan her türlü garip hatanın yutulmasına yol açmayacak mı? Yukarıdaki kod yalnızca eklemeye çalıştığınızı görmenizi engelliyor 2 + {}olabilir ve kodunuzun bir kısmının korkunç derecede yanlış gittiğini asla fark edemeyebilirsiniz. Tüm Türleri Kontrol Etmemizi önermiyorum, bu yüzden JavaScript değil Python ve Python - ama yine de dene / hariç bağlamında, programı yapmaması gereken bir şeyi yaparak yakalaması gerekiyor gibi görünüyor. devam etmesini sağlamak.

Yukarıdaki örneğin bir saman adam argümanı olduğunun farkındayım ve aslında kasıtlı olarak kötü. Ancak, pitonik inancını better to ask forgiveness than permissionveremem yardım edemem ama sanki kumdaki çizginin gerçekte doğru uygulama arasında olduğu sorusunu sorarsa, eğer / denemek / hariç olmak üzere, özellikle ne beklemeniz gerektiğini bildiğiniz zaman Çalıştığınız veriler.

Burada hız kaygılarından ya da en iyi uygulamalardan bahsetmiyorum bile, sadece sanırım her iki tarafa da gidebilecek gibi görünen davaların algılanan Venn şemasıyla sessizce kafam karıştı. çünkü 'birileri birisinin Pythonic olduğunu söyledi'. Bu sözdiziminin uygulanması hakkında yanlış sonuçlar çıkardım mı?

python 

Deneme haricindeki eğilimin hata gizlenme riski taşıdığı konusunda hemfikirim. Benim düşüncem, görmeyi beklediğiniz durumlar haricinde, genellikle denemekten kaçınmanız gerektiğidir (elbette bazı istisnalar bu kural için geçerlidir).
Gordon Bean

Yanıtlar:


26

Kullanım olsun yöntemini yerine:

some_dict.get(the_key, default_value)

... default_valueeğer değilse the_key, döndürülen değer nerede some_dict. Atlarsanız default_value, Noneanahtar eksikse döndürülür.

Genel olarak, Python, insanlar ilk şey kontrol daha haricinde / denemek tercih etme eğilimindedir - bkz EAFP girişi sözlüğü . "Üyelik testi" işlevlerinin çoğunun sahne arkasında istisnalar kullandığına dikkat edin.


Yine de bu aynı sorun değil mi? Biz çalışmak çalışıyorsanız dictve bulunan unsurları temel alarak işi, gibi geliyor if myDict.get(a_key) is not None: do_work(myDict[a_key])daha uzun ve henüz sıçrayan önce seyir örtük başka bir örnektir - artı biraz daha az okunabilir IMHO bu. Belki de dict.get(a_key, a_default)doğru bağlamda anlamıyorum , ancak 'el-not-if' if / elses 'yazmanın ya da sihirli değerlerin yazılmasının öncüsü gibi görünüyor. Bu yöntemin ne yaptığını sevdim, daha derinlerine bakacağım.

1
- @Stick yapmanız: data = myDict.get(a_key, default)ve ya do_workdoğru olanı yapacağız verildiğinde default(örn. NoneYa yapmanız) if data: do_work(data). Her iki durumda da yalnızca bir arama gerekli. (Her if data is not None: ...iki durumda da, hala sadece bir arama olabilir ve sonra kendi mantığınız ele geçirebilir.)
Ocak'ta

1
Bu yüzden dict.get () 'nin şu anki projemde bana bir sürü çaba kazandırdığına karar verdim. Satır sayımı düşürdü, çok kötü görünümlü try/except/elseçöpleri temizledi ve IMHO'yu kodumun okunabilirliğini tamamen iyileştirdi. Daha önce duyduğum ama kalbe almayı ihmal etmeyi başarabildiğim değerli bir ders aldım: "Çok fazla kod yazıyorsanız, durun ve dil özelliklerini inceleyin". Teşekkürler!!

@Stick - duyduğuma sevindim :) Bu tavsiyeyi beğendim, daha önce hiç duymadım.
saat

1
Teknik olarak en hızlı hangisi?
Olivier Pons

4

Eksik anahtar bir kural mı yoksa istisna mı?

Pragmatik bir bakış açısından, fırlatma / yakalama daha yavaştır ve anahtarın tabloda olup olmadığını kontrol eder. Eğer eksik anahtar ortak bir durum ise - koşulu kullanmalısınız.

Koşul lehine olan bir başka şey başka bir cümledir - ne zaman bir blok yürütülürse, aynı istisna sınıfının bile try bloğundaki birkaç ifadeden atılabileceğini düşünmek daha kolaydır.

Madde dışındaki kod normal kusurun bir parçası olmamalıdır (örneğin, anahtar orada değilse ekleyin) - hatayı işlemelidir.


4
"Pratik açıdan, fırlatma / yakalama, ya anahtarın tabloda olup olmadığını kontrol etmekten çok daha yavaştır" - hayır değil. Yine de mutlaka değil. Son kontrol ettiğimde, en yaygın Python uygulamaları try/ catchversiyonunu daha hızlı yapıyor.
saat

2
Python'a atma / yakalama, python'da akış kontrolü için sıklıkla kullanıldığı ölçüde, akıllıca bir işlem performansının o kadar büyük değildir. Ek olarak, bazı durumlarda test ve erişim arasında bir dit değiştirilebilme olasılığı vardır.
whatsisname,

@whatsisname - Cevabımı buna eklemeyi düşündüm, ancak eğer böyle bir yarış tehlikeniz varsa, TBH, EAFP'nin LBYL'e karşı çok daha büyük bir problemi olduğunu: P
Ocak'ta

1

"Pythonic" ve özellikle de ördek yazarak olmanın ruhunda - deneyin / hariç bana neredeyse kırılgan görünüyor.

İstediğiniz tek şey dikte benzeri erişim bir şey, ancak __getitem__beklediğiniz gibi değil bir şey yapmak için geçersiz kılınabilir. Örneğin defaultdict, standart kütüphaneden kullanma:

In [1]: from collections import defaultdict

In [2]: foo = defaultdict(int)

In [3]: foo['a'] = 1

In [4]: foo['b']  # Someone did access checking with a try/except exactly as you have in the question
Out[4]: 0

In [5]: 'a' in foo
Out[5]: True

In [6]: 'b' in foo  # We never set it, but now it exists!
Out[6]: True

In [7]: 'c' in foo
Out[7]: False

Varsayılan getiri değeri Falsy olduğundan, erişim kontrolü çoğu zaman işe yarayacak şekilde gerçekleşir. Kodları daha basit tutacağız, bu yüzden iş arkadaşlarım ve ben aylarca yaptık.

Ne yazık ki, birkaç ay sonra, bu ekstra anahtarlar aslında sorunlara ve takip edilmesi zor hatalara neden 0oldu , çünkü geçerli bir değer haline geldi. Bazen de olur kullanmak ino özdeş olması gerekirdi, kodun farklı şeyler yapmak bulunan, çok önemli var kontrol etmek.

Kırık görünmesini önerme sebebim Python'un ördek yazma ruhunda, tek yapmanız gereken dikine benzeyen bir şey ve kalıtımdan defaultdictalabileceğiniz herhangi bir nesneyi yapacağınız gibi mükemmel şekilde gereksinim duyan bir uyum dict. Arayan kişiyi daha sonra uygulamadan sonra değiştirmeyeceğini garanti edemezsiniz, bu yüzden en az yan etkisi olan bir şey yapmalısınız.

İlk sürümü kullan if myKey in mydict.


Ayrıca, bunun özellikle sorudaki örnekle ilgili olduğunu unutmayın . "İzinten ziyade bağışlama istemek daha iyi" ifadesinin temeli, büyük ölçüde, istediğinizi elde etmediğiniz zaman doğru şekilde davranmanızı sağlamak anlamına gelir. Örneğin, bir dosyanın var olup olmadığını kontrol etmek ve ardından okumaya çalışmak - kafamın üstünden düşünebileceğim en az 3 şey yanlış gidebilir:

  • Bir yarış koşulu, dosyanın silinmiş / taşınmış / etc olması durumudur.
  • Dosyada okuma izniniz yok
  • Dosya bozulmuş

Birincisi "izin istemek" için deneyebilirsiniz, ancak bir yarış koşulunun doğası gereği her zaman işe yaramazdı; ikincisi kontrol etmeyi unutmak çok kolaydır; ve üçüncü olamaz dosyayı okumaya çalışıyorum olmadan kontrol edilmesi. Her durumda, neredeyse kesinlikle öyle de, aynı olacaktır başarısız sonra neler yaptığını bu örnekte, bu ise hariç / bir deneyin kullanarak "af dile" daha iyi.

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.