AttributeErrors:@property 和 __getattr__ 之间出现不需要的交互

ARF*_*ARF 7 python properties attributeerror getattr

AttributeErrors我在@property与 python 结合使用时遇到了一个问题__getattr__():

示例代码:

>>> 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'
Run Code Online (Sandbox Code Playgroud)

就我而言,这是一个高度误导性的错误消息,因为它隐藏了deeply_nested_factory_fn()存在错误的事实。


根据 Tadhg McDonald-Jensen 的回答中的想法,我目前最好的解决方案如下。任何关于如何摆脱__main__.前缀 to和回溯中的AttributeError引用的提示将不胜感激。attributeErrorCatcher

>>> 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'
Run Code Online (Sandbox Code Playgroud)

小智 2

以防万一其他人发现这一点:上面的示例的问题是AttributeError在 内部引发了an __getattr__。相反,人们应该跟注self.__getattribute__(attr)让其加注。

例子

def deeply_nested_factory_fn():
    a = 2
    return a.invalid_attr

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

test = Test()
test.my_prop
Run Code Online (Sandbox Code Playgroud)

这产生

AttributeError                            Traceback (most recent call last)
Cell In [1], line 15
     12         return deeply_nested_factory_fn()
     14 test = Test()
---> 15 test.my_prop

Cell In [1], line 9, in Test.__getattr__(self, name)
      7 if name == 'abc':
      8     return 'abc'
----> 9 return self.__getattribute__(name)

Cell In [1], line 12, in Test.my_prop(self)
     10 @property
     11 def my_prop(self):
---> 12     return deeply_nested_factory_fn()

Cell In [1], line 3, in deeply_nested_factory_fn()
      1 def deeply_nested_factory_fn():
      2     a = 2
----> 3     return a.invalid_attr

AttributeError: 'int' object has no attribute 'invalid_attr'
Run Code Online (Sandbox Code Playgroud)