Python Affetme - İzin ve Ördek Yazma


44

Python'da sık sık "izin istemek" yerine "af dilemenin" (istisna yakalama) daha iyi olduğunu duyuyorum (tip / durum kontrolü). Python'da ördek yazmaya zorlama açısından, bu

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

daha iyi veya daha kötü

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

performans, okunabilirlik, "pitonik" veya başka bir önemli faktör açısından?


17
üçüncü bir seçenek var, hiçbir şey yapmayın ve herhangi bir foo'yu bara olmadan bir böcek olarak kabul edin
jk.

hasattrDahili olarak tam olarak dene / yakala ile işitildiğini hatırlıyorum . Doğru olup olmadığından emin değil ... (mülklere farklı davranacak, değil mi? Belki de düşünüyorum getattr..)
Izkata

@Izkata: uygulamasıhasattr , C-API eşdeğerini kullanır getattr( Truebaşarılı olursa döndür , Falseyoksa), ancak C'deki istisnaları ele almak çok daha hızlıdır.
Martijn Pieters,

2
Martijn'in cevabını kabul ettim, ancak bir özellik belirlemeye çalışıyorsanız, try / catch komutunu kullanmayı düşünmelisiniz, çünkü pasif olmayan bir özellik olabilir, bu durumda hasattr doğru olur , ama yine de AttributeError'ı yükseltecek.
darkfeline

Yanıtlar:


60

Bu, istisnanın ne sıklıkta atılacağını düşündüğünüze bağlı.

Her iki yaklaşım da, bence, eşit derecede geçerlidir, en azından okunabilirlik ve pitoniklik açısından. Senin nesnelerin% 90 Ama eğer değil özniteliği bariki yaklaşım arasındaki belirgin performans farkı fark edeceksiniz:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

Senin nesnelerin% 90 Ama eğer yok özniteliği, tablolar döndü edilmiştir:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

Bu nedenle, performans açısından, koşullarınıza en uygun yaklaşımı seçmeniz gerekir.

Sonunda, timeitmodülün bazı stratejik kullanımları yapabileceğiniz en Pythonic şey olabilir.


1
Birkaç hafta önce bu soruyu sordum: programmers.stackexchange.com/questions/161798/… Orada gevşek dilde yazılan dillerde fazladan tip kontrolleri yapmanız gerekip gerekmediğini sordum. Biliyorum sende olduğunu gördüm.
Tulains Córdova

@ user1598390: Homojen bir tür karışımı bekleyen bir API tanımladığınızda , bazı testler yapmanız gerekir. Çoğu zaman yapmazsın. Bu, paradigmalar hakkında bir bütün olarak kurallar çıkaramadığınız belirli bir alandır, korkarım.
Martijn Pieters,

Herhangi bir ciddi sistem gelişimi, bir API tanımlamayı içerir. Bu nedenle, katı dillerin bunun için en iyisi olduğunu düşünüyorum, çünkü derleyici derleme zamanında sizin için türleri kontrol ettiğinden daha az kod yazmanız gerekir.
Tulains Córdova

1
@GarethRees: Yanıtın son yarısı için test edilen fonksiyona bir argüman ilettiğim bir kalıp oluşturur.
Martijn Pieters

1
Bunun için hasattr, aslında, başlık altında bir denemenin C-api eşdeğeri yaptığını, çünkü bir nesnenin Python'da bir özniteliğe sahip olup olmadığını belirlemek için tek genel yolunu ortaya çıkardığını unutmayın.
user2357112

11

Python'da genellikle Python yöntemiyle daha iyi performans elde edersiniz. Diğer dillerde ise, akış kontrolü için istisnalar kullanmak genellikle korkunç bir fikir olarak kabul edilir, çünkü istisnalar tipik olarak olağanüstü bir ek yük getirir. Ancak, bu teknik Python'da açıkça onaylandığı için, tercüman bu kod türü için optimize edilmiştir.

Tüm performans sorularında olduğu gibi, kesin olmanın tek yolu kodunuzu belirlemektir. Her iki sürümü de yazın ve hangisinin daha hızlı çalıştığını görün. Deneyimlerime rağmen, "Python yolu" genellikle en hızlı yoldur.


3

Performans, ikincil bir endişe olduğunu düşünüyorum. Bir profiler ortaya çıkarsa, olası yasadışı argümanları nasıl ele aldığınıza veya vermeyeceğiniz gerçek darboğazlara odaklanmanıza yardımcı olur.

Okunabilirlik ve basitlik, diğer taraftan, her zaman önemli bir konudur. Burada zor kurallar yoktur, sadece kararınızı kullanın.

Bu evrensel bir konudur, ancak çevreye veya dile özgü sözleşmeler önemlidir. Örneğin, Python'da genellikle beklediğiniz özniteliği kullanmak ve olası bir AttributeError'ın arayan kişiye ulaşmasına izin vermek genellikle iyidir.


-1

Doğruluk açısından, istisnaların ele alınmasının yol olduğunu düşünüyorum (bazen hasattr () yaklaşımını kendim de kullanıyorum). Hasattr () 'a dayanarak ortaya çıkan temel sorun, kod sözleşmelerinin ihlallerini sessiz hatalara dönüştürmesidir (bu, JavaScript'te var olmayan mülklere zarar vermeyen büyük bir sorundur).


3
Cevabınız, başkaları tarafından daha önce belirtilenlerin ötesine geçmiyor gibi görünüyor. Diğer sitelerden farklı olarak, Programcılar yanıtın nedenini açıklayan yanıtları beklemektedir . Sessiz başarısızlık sorunuyla iyi bir noktaya dokunuyorsunuz, ancak buna adaleti sağlamak zorunda değilsiniz. Aşağıdakileri okumak yardımcı olabilir: programmers.stackexchange.com/help/how-to-answer

Yaptığım son cevap çok geniş olduğu için eleştirildi. Kısa ve özlü deneyeceğim.
Joe,
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.