hasattr () vs var olmayan özniteliklerle başa çıkmak için try-exclu bloğu


Yanıtlar:


82

hasattrdahili ve hızlı bir şekilde try/exceptblokla 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.


8
Ancak, yarış koşullarını idare etmek için hala dene / yakala bloğuna ihtiyacınız var (iş parçacığı kullanıyorsanız).
Douglas Leeder

1
Ya da az önce karşılaştığım özel durum: değeri olmayan bir django OneToOneField: hasattr (obj, alan_adı) False döndürüyor, ancak field_name ile bir nitelik var: sadece bir DoesNotExist hatası veriyor.
Matthew Schinckel

3
Not hasattredecek tüm özel durumları yakalamak Python 2.x Bkz Cevabımı bir örnek ve önemsiz geçici çözüm için.
Martin Geisler

5
İlginç bir yorum : tryOperasyonun çalışması gerektiğini ifade edebilir . Niyeti tryher zaman böyle olmasa da, yaygındır, bu nedenle daha okunaklı kabul edilebilir.
Ioannis Filippidis

88

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

16
İlginç, somut sayılar sağlamak için +1. Aslında, "dene" ortak durumu içerdiğinde etkilidir (yani bir Python istisnası gerçekten istisnai olduğunda).
Eric O Lebigot

Bu sonuçları nasıl yorumlayacağımı bilmiyorum. Burada hangisi daha hızlı ve ne kadar?
Stevoisiak

2
@ StevenM.Vascellaro: Öznitelik varsa, tryyaklaşı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 tryneredeyse 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.
Kevin

24

Üçüncü ve genellikle daha iyi bir alternatif vardır:

attr = getattr(obj, 'attribute', None)
if attr is not None:
     print attr

Avantajlar:

  1. getattrMartin Geiser'in işaret ettiği kötü istisna-yutma davranışına sahip değil - eski Pythons'ta, hasattra KeyboardInterrupt.

  2. 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.

  3. Ö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.)

  4. Daha kısa try/finallyve genellikle daha kısadır hasattr.

  5. Geniş bir except AttributeErrorblok AttributeErrorsbeklediğinizden farklı olabilir ve bu da kafa karıştırıcı davranışlara yol açabilir.

  6. 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.attributenokta, Yok olarak ayarlanan vakayı önemsiyorsanız , farklı bir sentinel değer kullanmanız gerekmesidir.


1
+1 - Bu, dict.get ('my_key', 'default_value') ile uyum içindedir ve daha yaygın olarak bilinmelidir

1
Varlığı kontrol etmek ve özelliği varsayılan değerle kullanmak istediğiniz ortak kullanım durumu için harika.
dsalaj

18

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__: hasattredecek tüm istisnaları yakalamak yerine yakalama AttributeErrorbeklediğiniz gibi. Başka bir deyişle, b: Falsebir ValueErroristisna 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 .hasattrAttributeError

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 getattrdurumu ele alalım ve daha sonra uygun istisnayı gündeme getirebilir.


2
Bu aynı zamanda Python2.6'da biraz geliştirildi, böylece hasattren azından yakalamayacak KeyboardInterruptvs.
poolie

Veya yerine safehasattr, sadece kullanmak getattrsize neredeyse her zaman, hangi kullanmak için gidiyoruz yerel değişkene değeri kopyalamak için.
poolie

@poolie Bu güzel, bunun böyle hasattrgeliştirildiğini bilmiyordum .
Martin Geisler

Evet o iyi. Bunu da bugüne kadar birine kaçınmasını söyleyeceğim hasattrve kontrol etmeye gittiğim güne kadar bilmiyordum . Hasatrın yeni yutulduğu yerde komik bzr böceklerimiz vardı.
poolie

2.7'yi 3.6'ya yükseltirken sorunla karşılaştı. Bu cevap, sorunu anlamama ve çözmeme yardımcı oluyor.
Kamesh Jungi

13

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.


1
Kodu okuyan insanlar için "dene" nin neden bir anlamı olduğu konusunda ısrar eden +1. :)
Eric O Lebigot

5

Ö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 bence büyük ölçüde göz ardı edilen büyük bir sorundur.
Rick Monica'yı destekliyor

5

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

3 İZİN VEYA Affetme?

# 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

4

Test ettiğiniz özelliklerden yalnızca biri ise, kullan derim hasattr. Bununla birlikte, var olabilecek veya olmayabilecek niteliklere birkaç erişim yapıyorsanız, bir tryblok kullanmak sizi biraz yazmadan kurtarabilir.


3

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,.


2

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. .


+1, koşullu teste kıyasla "denemenin" "bu istisnai bir durum" olarak yorumlanabileceği gerçeğinde ısrarcı olmak için. :)
Eric O Lebigot

0

İlk.

Daha kısa, daha iyidir. İstisnalar istisnai olmalıdır.


5
Python'da istisnalar çok yaygındır - her forifadenin sonunda bir tane vardır ve hasattrbirini 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.
Alex Martelli

@Alex, Python ayrıştırıcısının bu ifadeleri 1'e dönüştürmesi çok yaygın olduğu anlamına gelmez. Bu sözdizimsel şekeri yapmalarının bir nedeni var: bu nedenle, blok dışında try yazmanın zalimliğine takılıp kalmazsınız.
Bilinmeyen

İstisna istisnai ise, o zaman "
açıklık

0

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ü hasattrsonucu belirlemek için sadece ilk durumu kullanır. Düşünmek için yiyecek ;-)

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.