Python:为什么u getattr_u_u_u_u_u_u_u_u_u_u_u________________?
我正在与Python:为什么u getattr_u_u_u_u_u_u_u_u_u_u_u________________?,python,exception,exception-handling,python-3.x,python-2.7,Python,Exception,Exception Handling,Python 3.x,Python 2.7,我正在与\uuu getattr\uuuu斗争。我有一个复杂的递归代码库,让异常传播很重要 class A(object): @property def a(self): raise AttributeError('lala') def __getattr__(self, name): print('attr: ', name) return 1 print(A().a) 结果: ('attr:
\uuu getattr\uuuu
斗争。我有一个复杂的递归代码库,让异常传播很重要
class A(object):
@property
def a(self):
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
结果:
('attr: ', 'a')
1
为什么会有这种行为?为什么没有抛出异常?这种行为没有记录()
getattr()
可以只使用A.\uu dict\uuu
。有什么想法吗?我刚把代码改成
class A(object):
@property
def a(self):
print "trying property..."
raise AttributeError('lala')
def __getattr__(self, name):
print('attr: ', name)
return 1
print(A().a)
而且,正如我们所看到的,事实上,财产是首先被审判的。但由于它声称不存在(通过提高AttributeError
),被称为“最后手段”
它没有明确的文档记录,但可以在“当属性查找未在通常位置找到属性时调用”下计算。当属性访问失败且出现AttributeError时,调用\uuuuu getattr\uuuuu
。也许这就是为什么你认为它“捕捉”了错误。然而,事实并非如此,是Python的属性访问功能捕获了它们,然后调用\uuu getattr\uu
但是\uu getattr\uuu
本身不会捕获任何错误。如果在\uuu getattr\uuuu>中引发AttributeError,则会得到无限递归。表示:
如果该类还定义了\uuuuuGetAttr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
,则后者将不会被调用,除非\uuuuuuuuuuuuuuuuuuuuuuuuGetAttribute\uuuuu
我(通过inclusio unius est exclusio alterius)读到这句话,说属性访问将调用\uuuu getattr\uuuuu
如果对象恰好引发AttributeError
——无论是直接引发还是在描述符\uuuuu get\uuuuu
(例如属性fget)内引发;请注意,应该“返回(计算的)属性值或引发AttributeError
异常”
作为类比,运算符特殊方法可能会引发未实现错误
,因此,将尝试使用其他运算符方法(例如\uuuu radd\uuuu
for\uu add\uuuu
)。使用\uu getattr\uuuuu
和同一类中的属性是危险的,因为它可能导致非常难以调试的错误
如果属性的getter抛出AttributeError
,则AttributeError
将被静默捕获,并调用\uuu getattr\uuuu
。通常,这会导致\uuuuu getattr\uuuuu
异常失败,但如果您非常不走运,则不会发生异常,您甚至无法轻松将问题追溯到\uuuu getattr\uuuu
除非您的属性getter是微不足道的,否则您永远无法100%确定它不会抛出AttributeError
。异常可能被抛出几个级别
以下是您可以做的:
避免在同一类中使用属性和\uuuuu getattr\uuuu
添加一个尝试。。。除了
block之外,所有属性getter都不是微不足道的
保持属性获取程序简单,这样您就知道它们不会抛出AttributeError
编写自己版本的@属性
装饰器,它捕获属性错误
,并将其作为运行时错误
重新抛出
另见
编辑:如果有人正在考虑解决方案4(我不推荐),可以这样做:
def property_(f):
def getter(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]
return property(getter)
class C(object):
def __getattr__(self, name):
...
@property
@replace_attribute_error_with_runtime_error
def complicated_property(self):
...
...
然后在重写\uu getattr\uuuu
的类中使用@property
而不是@property
,当您将@property
与\uu getattr\uuuuu
组合时,您将注定失败:
class Paradise:
pass
class Earth:
@property
def life(self):
print('Checking for paradise (just for fun)')
return Paradise.breasts
def __getattr__(self, item):
print("sorry! {} does not exist in Earth".format(item))
earth = Earth()
try:
print('Life in earth: ' + str(earth.life))
except AttributeError as e:
print('Exception found!: ' + str(e))
提供以下输出:
Checking for paradise (just for fun)
sorry! life does not exist in Earth
Life in earth: None
当你真正的问题是打电话给天堂.乳房时
\uuuu getattr\uuuu
总是在出现AtributeError
时调用。将忽略异常的内容
可悲的是,这个问题没有解决方案,因为hasattr(地球,“生命”)
将返回True
(仅仅因为\uuuuu getattr\uuuuuuuu
被定义),但仍然会通过属性“生命”实现,因为它不存在,而真正的根本问题是天堂
我的部分解决方案包括在@property
块中使用try-except,这些块已知会遇到属性错误
异常。经常遇到这个问题,因为我实现了很多\uu getattr\uuuuu
方法,并且有很多@property
方法。我想出了一个装饰器来获得一条更有用的错误消息:
def replace_attribute_error_with_runtime_error(f):
@functools.wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
# logging.exception(e)
raise RuntimeError(
'{} failed with an AttributeError: {}'.format(f.__name__, e)
)
return wrapped
然后像这样使用它:
def property_(f):
def getter(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]
return property(getter)
class C(object):
def __getattr__(self, name):
...
@property
@replace_attribute_error_with_runtime_error
def complicated_property(self):
...
...
基础异常的错误消息将包括其实例引发基础AttributeError
的类的名称。
如果愿意,您也可以将其记录下来。建议使用什么方法?不使用属性或使用\uuuu getattribute\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,捕获AttributeError
。我无法理解您为什么要在属性中显式提升AttributeError
。或者除了(get | set)attr[ibute]\uuuu
方法以外的任何地方,真的。显式引发AttributeError不是这件事的意义。。。问题是,我有一个复杂的程序,其中引发了一个AttributeError(由于编程错误),但我不知道在哪里,因为\uuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>对我隐藏了它。@glglglglglgl:我不像引发了AttributeError
,我只是犯了编程。最后,我什么也看不到,因为它被\uuuuGetAttr\uuuuuuuu
捕获了。我想说文档的失败之处在于它声明属性错误可以在\uuuuuu get\uuuuuuuu
中提出(这是您在这里有效地做的),但没有解释什么是ef