if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Hangisi tercih edilmeli ve neden?
Yanıtlar:
hasattr
dahili ve hızlı bir şekilde try/except
blokla aynı görevi yerine getirir : çok özel, optimize edilmiş, tek görevli bir araçtır ve bu nedenle, uygulanabilir olduğunda, çok genel amaçlı alternatife tercih edilmelidir.
hasattr
edecek tüm özel durumları yakalamak Python 2.x Bkz Cevabımı bir örnek ve önemsiz geçici çözüm için.
try
Operasyonun çalışması gerektiğini ifade edebilir . Niyeti try
her zaman böyle olmasa da, yaygındır, bu nedenle daha okunaklı kabul edilebilir.
Performanstaki farkı gösteren herhangi bir tezgah var mı?
zaman senin arkadaşın
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "nonexistent")'
1000000 loops, best of 3: 1.87 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'hasattr(c, "a")'
1000000 loops, best of 3: 0.446 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.a
except:
pass'
1000000 loops, best of 3: 0.247 usec per loop
$ python -mtimeit -s 'class C(object): a = 4
c = C()' 'try:
c.nonexistent
except:
pass'
100000 loops, best of 3: 3.13 usec per loop
$
|positive|negative
hasattr| 0.446 | 1.87
try | 0.247 | 3.13
try
yaklaşık iki kat daha hızlıdır hasattr()
. Değilse try
, yaklaşık 1.5 kat daha yavaştır hasattr()
(ve her ikisi de öznitelik var olduğundan önemli ölçüde daha yavaştır). Bunun nedeni muhtemelen mutlu yolda try
neredeyse hiçbir şey yapmadığı içindir (Python zaten bunları kullanıp kullanmadığınıza bakılmaksızın istisnaların ek yükünü ödüyor), ancak hasattr()
bir ad araması ve işlev çağrısı gerektiriyor. Mutsuz yolda, her ikisi de bazı istisna işleme ve a yapmak zorundadır goto
, ancak hasattr()
bunu Python bayt kodu yerine C'de yapar.
Üçüncü ve genellikle daha iyi bir alternatif vardır:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Avantajlar:
getattr
Martin Geiser'in işaret ettiği kötü istisna-yutma davranışına sahip değil - eski Pythons'ta, hasattr
a KeyboardInterrupt
.
Nesnenin bir özniteliğe sahip olup olmadığını kontrol etmenizin normal nedeni, özniteliği kullanabilmenizdir ve bu da doğal olarak ona yol açar.
Öznitelik atomik olarak okunur ve nesneyi değiştiren diğer iş parçacıkları için güvenlidir. (Yine de, bu büyük bir sorunsa, erişmeden önce nesneyi kilitlemeyi düşünebilirsiniz.)
Daha kısa try/finally
ve genellikle daha kısadır hasattr
.
Geniş bir except AttributeError
blok AttributeErrors
beklediğinizden farklı olabilir ve bu da kafa karıştırıcı davranışlara yol açabilir.
Bir özniteliğe erişim, yerel bir değişkene erişmekten daha yavaştır (özellikle düz bir örnek özniteliği değilse). (Dürüst olmak gerekirse, Python'daki mikro optimizasyon genellikle aptalca bir iştir.)
Dikkat etmeniz gereken bir obj.attribute
nokta, Yok olarak ayarlanan vakayı önemsiyorsanız , farklı bir sentinel değer kullanmanız gerekmesidir.
Neredeyse her zaman kullanırım hasattr
: çoğu durumda doğru seçimdir.
Sorunlu durum bir sınıf geçersiz kılar zaman olduğu __getattr__
: hasattr
edecek tüm istisnaları yakalamak yerine yakalama AttributeError
beklediğiniz gibi. Başka bir deyişle, b: False
bir ValueError
istisna görmek daha uygun olsa da aşağıdaki kod yazdırılacaktır :
class X(object):
def __getattr__(self, attr):
if attr == 'a':
return 123
if attr == 'b':
raise ValueError('important error from your database')
raise AttributeError
x = X()
print 'a:', hasattr(x, 'a')
print 'b:', hasattr(x, 'b')
print 'c:', hasattr(x, 'c')
Böylece önemli hata ortadan kalktı. Bu, artık yalnızca yakalamaların olduğu Python 3.2'de ( issue9666 ) düzeltildi .hasattr
AttributeError
Kolay bir çözüm, aşağıdaki gibi bir yardımcı program işlevi yazmaktır:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
Bu getattr
durumu ele alalım ve daha sonra uygun istisnayı gündeme getirebilir.
hasattr
en azından yakalamayacak KeyboardInterrupt
vs.
safehasattr
, sadece kullanmak getattr
size neredeyse her zaman, hangi kullanmak için gidiyoruz yerel değişkene değeri kopyalamak için.
hasattr
geliştirildiğini bilmiyordum .
hasattr
ve kontrol etmeye gittiğim güne kadar bilmiyordum . Hasatrın yeni yutulduğu yerde komik bzr böceklerimiz vardı.
Bunun, işlevinizin tasarım gereği öznitelik olmadan nesneleri kabul edip edemeyeceğine bağlı olduğunu söyleyebilirim. , örneğin işlev için iki çağırıcınız varsa, biri özniteliğe sahip bir nesne sağlar ve diğeri onsuz bir nesne sağlar.
Öznitelik olmadan bir nesneyi elde edeceğiniz tek durum bazı hatalardan kaynaklanıyorsa, daha yavaş olsa da istisna mekanizmasını kullanmanızı tavsiye ederim çünkü daha temiz bir tasarım olduğuna inanıyorum.
Sonuç olarak: Bunun bir verimlilik sorunundan çok bir tasarım ve okunabilirlik sorunu olduğunu düşünüyorum.
Özniteliğe sahip olmamak bir hata durumu değilse , istisna işleme varyantının bir sorunu vardır: obj.attribute'a erişirken dahili olarak gelebilecek AttributeErrors'ı da yakalar (örneğin, öznitelik bir özellik olduğundan, erişime bazı kodlar çağırır).
Bu konu, Sebastian Witowski'nin EuroPython 2016 Yazma Hızlı Python konuşmasında ele alındı. İşte performans özetiyle birlikte slaydının bir kopyası. Ayrıca , bu tartışmada atlamadan önce , bu anahtar kelimeyi etiketlemek için burada bahsetmeye değer terminoloji görünümünü de kullanıyor .
Öznitelik gerçekten eksikse, affetmek için yalvarmak izin istemekten daha yavaş olacaktır. Bu nedenle, genel bir kural olarak, özniteliğin eksik olmasının veya tahmin edebileceğiniz diğer sorunların çok yüksek olduğunu biliyorsanız, izin isteme yöntemini kullanabilirsiniz. Aksi takdirde, kodun çoğu zaman okunabilir kodla sonuçlanmasını bekliyorsanız
# CASE 1 -- Attribute Exists
class Foo(object):
hello = 'world'
foo = Foo()
if hasatter(foo, 'hello'):
foo.hello
## 149ns ##
try:
foo.hello
except AttributeError:
pass
## 43.1 ns ##
## 3.5 times faster
# CASE 2 -- Attribute Absent
class Bar(object):
pass
bar = Bar()
if hasattr(bar, 'hello'):
bar.hello
## 428 ns ##
try:
bar.hello
except AttributeError :
pass
## 536 ns ##
## 25% slower
2. seçeneği öneririm. Başka bir iş parçacığı özniteliği ekliyor veya kaldırıyorsa 1. Seçenek bir yarış durumuna sahiptir.
Ayrıca piton bir sahiptir deyim EAFP ( 'kolay izni daha af sormak') LBYL ( 'göz atlamadan önce') daha iyi olduğunu,.
Pratik bir bakış açısıyla, çoğu dilde bir koşullu kullanmak, her zaman bir istisnayı ele almaktan oldukça hızlı olacaktır.
Mevcut işlevin dışında bir yerde bulunmayan bir özniteliğin durumunu ele almak istiyorsanız, istisna daha iyi bir yoldur. Koşullu yerine bir istisna kullanmak isteyebileceğinizin bir göstergesi, koşulun yalnızca bir bayrak oluşturması ve mevcut işlemi iptal etmesi ve başka bir yerde bu bayrağı kontrol edip buna göre eylemde bulunmasıdır.
Bununla birlikte, Rax Olgud'un da belirttiği gibi, başkalarıyla iletişim, kodun önemli bir özelliğidir ve "bu, olmasını beklediğim bir şeydir" yerine "bu istisnai bir durumdur" diyerek söylemek istedikleriniz daha önemli olabilir. .
İlk.
Daha kısa, daha iyidir. İstisnalar istisnai olmalıdır.
for
ifadenin sonunda bir tane vardır ve hasattr
birini de kullanır. Ancak "ne kadar kısa olursa o kadar iyidir" (ve "daha basit daha iyidir"!) YAPIN, bu nedenle daha basit, daha kısa, daha spesifik hasattr gerçekten tercih edilir.
En azından programda neler olup bittiğine bağlı olduğunda, okunabilirliğin insan kısmını dışarıda bıraktığında (aslında çoğu zaman performanstan daha önemlidir (en azından bu durumda - bu performans süresi ile), Roee Adler ve diğerlerinin işaret ettiği gibi).
Yine de ona bu perspektiften bakarsak, o zaman bu bir seçim meselesi haline gelir.
try: getattr(obj, attr)
except: ...
ve
try: obj.attr
except: ...
çünkü hasattr
sonucu belirlemek için sadece ilk durumu kullanır. Düşünmek için yiyecek ;-)