2016-04-12 14 views
2

arasında istenmeyen etkileşim Ben Python __getattr__() ile birlikte bir @property büyüdü AttributeErrors ile ilgili bir sorun:AttributeErrors: @ özellik ve __getattr__

örnek kod: Benim durumumda

>>> def deeply_nested_factory_fn(): 
...  a = 2 
...  return a.invalid_attr 
... 
>>> class Test(object): 
...  def __getattr__(self, name): 
...   if name == 'abc': 
...    return 'abc' 
...   raise AttributeError("'Test' object has no attribute '%s'" % name) 
...  @property 
...  def my_prop(self): 
...   return deeply_nested_factory_fn() 
... 
>>> test = Test() 
>>> test.my_prop 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in __getattr__ 
AttributeError: 'Test' object has no attribute 'my_prop' 

, bu bir son derece yanıltıcı hata mesajı, çünkü deeply_nested_factory_fn() bir hataya sahip olduğu gerçeğini gizler. Tadhg McDonald-Jensen yanıtında fikrine dayanarak


, benim şu anda en iyi çözüm şudur. __main__. önekinden AttributeError'a ve attributeErrorCatcher referansından geri izlemeye nasıl gidileceği ile ilgili ipuçları çok takdir edilecektir. Eğer münhasıran yeni tarzı sınıfları kullanmak isteyen iseniz

>>> def catchAttributeErrors(func): 
...  AttributeError_org = AttributeError 
...  def attributeErrorCatcher(*args, **kwargs): 
...   try: 
...    return func(*args, **kwargs) 
...   except AttributeError_org as e: 
...    import sys 
...    class AttributeError(Exception): 
...     pass 
...    etype, value, tb = sys.exc_info() 
...    raise AttributeError(e).with_traceback(tb.tb_next) from None 
...  return attributeErrorCatcher 
... 
>>> def deeply_nested_factory_fn(): 
...  a = 2 
...  return a.invalid_attr 
... 
>>> class Test(object): 
...  def __getattr__(self, name): 
...   if name == 'abc': 
...    # computing come other attributes 
...    return 'abc' 
...   raise AttributeError("'Test' object has no attribute '%s'" % name) 
...  @property 
...  @catchAttributeErrors 
...  def my_prop(self): 
...   return deeply_nested_factory_fn() 
... 
>>> class Test1(object): 
...  def __init__(self): 
...   test = Test() 
...   test.my_prop 
... 
>>> test1 = Test1() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 4, in __init__ 
    File "<stdin>", line 11, in attributeErrorCatcher 
    File "<stdin>", line 10, in my_prop 
    File "<stdin>", line 3, in deeply_nested_factory_fn 
__main__.AttributeError: 'int' object has no attribute 'invalid_attr' 
+0

'__main__' kısmını kaldırmak için sınıf tanımında '__qualname__ =" AttributeError "' yapmak zorundasınız ama bana güvenin, ** yapmak değil ** hata sadece AttributeError demek istemez, çünkü şaşkınlık riskiyle karşılaşırsınız Neden AttributeError 'dışındaki heck bir AttributeError yakalamadı. –

cevap

1

Bir AttributeError gibi görünen ama aslında bir AttributeError olmadığından __getattr__ tetiklemez özel İstisna oluşturabilirsiniz.

GÜNCELLEME:

@property 
@AttributeError_alt.wrapper 
def my_prop(self): 
    return deeply_nested_factory_fn() 

ve hata mesajı: olarak size mal tanımladığınızda Sonra

class AttributeError_alt(Exception): 
    @classmethod 
    def wrapper(err_type, f): 
     """wraps a function to reraise an AttributeError as the alternate type""" 
     @functools.wraps(f) 
     def alt_AttrError_wrapper(*args,**kw): 
      try: 
       return f(*args,**kw) 
      except AttributeError as e: 
       new_err = err_type(e) 
       new_err.__traceback__ = e.__traceback__.tb_next 
       raise new_err from None 
     return alt_AttrError_wrapper 

: traceback mesajı ölçüde hatayı tekrar kaldırmadan önce .__traceback__ niteliğini yeniden atayarak iyileştirilmiştir Bu gibi görünecek:

Traceback (most recent call last): 
    File ".../test.py", line 34, in <module> 
    test.my_prop 
    File ".../test.py", line 14, in alt_AttrError_wrapper 
    raise new_err from None 
    File ".../test.py", line 30, in my_prop 
    return deeply_nested_factory_fn() 
    File ".../test.py", line 20, in deeply_nested_factory_fn 
    return a.invalid_attr 
AttributeError_alt: 'int' object has no attribute 'invalid_attr' 

bildirimde bulunur. Yeniden , raise new_err from None için bir satır, ancak özellik çağrısı içinden satırların üzerindedir. Ayrıca return f(*args,**kw) için bir satır olacaktır, ancak .tb_next ile atlanmıştır.


senin sorununa en iyi çözüm already been suggested vardır ve bunu en iyi seçenek olduğunu düşünüyorum neden benim cevap previous revision görebilirsiniz oldukça eminim.orada dürüstçe eğer yanlış o zaman bastırılmış ediliyor bir hata aksi gizli olacak birine zincirleme kanlı RuntimeError yükseltmek rağmen: o zaman

def assert_no_AttributeError(f): 
    @functools.wraps(f) 
    def assert_no_AttrError_wrapper(*args,**kw): 
     try: 
      return f(*args,**kw) 
     except AttributeError as e: 
      e.__traceback__ = e.__traceback__.tb_next 
      raise RuntimeError("AttributeError was incorrectly raised") from e 
    return assert_no_AttrError_wrapper 

ama bu işte özellik süslemeleri eğer böyle bir hata alırsınız:

def __getattribute__(self,attr): 
    try: 
     return object.__getattribute__(self,attr) 
    except AttributeError as e: 
     if str(e) == "{0.__class__.__name__!r} object has no attribute {1!r}".format(self,attr): 
      raise #normal case of "attribute not found" 
     else: #if the error message was anything else then it *causes* a RuntimeError 
      raise RuntimeError("Unexpected AttributeError") from e 
0:

Traceback (most recent call last): 
    File ".../test.py", line 27, in my_prop 
    return deeply_nested_factory_fn() 
    File ".../test.py", line 17, in deeply_nested_factory_fn 
    return a.invalid_attr 
AttributeError: 'int' object has no attribute 'invalid_attr' 

The above exception was the direct cause of the following exception: 

Traceback (most recent call last): 
    File ".../test.py", line 32, in <module> 
    x.my_prop 
    File ".../test.py", line 11, in assert_no_AttrError_wrapper 
    raise RuntimeError("AttributeError was incorrectly raised") from e 
RuntimeError: AttributeError was incorrectly raised 

sadece tüm aramalar için alışılmadık bir hata olup olmadığını kontrol etmek __getattribute__ aşırı bir AttributeError yükseltmek sadece tek bir şey daha sonra bekliyorsanız o zaman istediğiniz olsa da

Bu şekilde bir şeyler yanlış gittiğinde, beklemeyeceğinizi hemen öğreneceksiniz!

+0

Bu gerçekten işleri daha iyi yapmaz: Yığın izi hala kapalı! Hatanın, çözümünüzün ürettiği yığın izlemesi tarafından önerilen "iç" işlevinde değil "deeply_nested_factory_fn" işlevinde olduğunu unutmayın. - Dedi ki, stacktrace sorunu çözülebiliyorsa, sys.exit (1) 'i engelleyen fikrini gerçekten çok istiyorum. – ARF

+0

Biraz kafa çizdikten sonra, çok mutsuz olduğum bir çözüm buldum. Hala ilgileniyorsanız, değiştirilen soruya bir göz atın. – ARF

+0

Bu, kullanıcıların bu özel durumu yakalamak için "AttributeError dışında" yapmasını engeller. – user2357112

2

, sen __getattribute__ yerine __getattr__ aşılmasına neden olabilir:

class Test(object): 
    def __getattribute__(self, name): 
     if name == 'abc': 
      return 'abc' 
     else: 
      return object.__getattribute__(self, name) 
    @property 
    def my_prop(self): 
     return deeply_nested_factory_fn() 

Şimdi yığın izleme düzgün deeply_nested_factory_fn belirtilecek.

Traceback (most recent call last): 
    File "C:\python\myprogram.py", line 16, in <module> 
    test.my_prop 
    File "C:\python\myprogram.py", line 10, in __getattribute__ 
    return object.__getattribute__(self, name) 
    File "C:\python\myprogram.py", line 13, in my_prop 
    return deeply_nested_factory_fn() 
    File "C:\python\myprogram.py", line 3, in deeply_nested_factory_fn 
    return a.invalid_attr 
AttributeError: 'int' object has no attribute 'invalid_attr' 
+0

Teşekkürler, bu çözümdeki sorunum diğer özellikler için performans isabetidir. (Bunlardan bazıları döngülere erişebilir.) – ARF

+0

@ARF, eğer performans söz konusu ise, o zaman sık kullanılan özniteliklere yerel referanslar alın. '__getattribute__' aşırı yüklenerek yavaş yavaş _without_ yapmasını tavsiye ederim. –

+0

Yani "a = test.a" ve "test" yerine "a" kullanarak mı demek istiyorsunuz? – ARF