gpt4 book ai didi

python - 使用 __getattr__ 并满足子类的预期行为

转载 作者:太空狗 更新时间:2023-10-30 02:21:35 25 4
gpt4 key购买 nike

我是 a simple database layer 的作者——目前几乎肯定是唯一的用户,如果在多个项目中的话—— (灵感来自 minimongo )用于名为 kale 的 MongoDB。我目前在模型的基类中使用 __getattr__ 导致了一些难以跟踪的错误。

我遇到的问题was articulated去年 6 月,David Halter 在本网站上简明扼要地说明了这一点。讨论很有趣,但是没有提供解决方案。

简而言之:

>>> class A(object):
... @property
... def a(self):
... print "We're here -> attribute lookup found 'a' in one of the usual places!"
... raise AttributeError
... return "a"
...
... def __getattr__(self, name):
... print "We're here -> attribute lookup has not found the attribute in the usual places!"
... print('attr: ', name)
... return "not a"
...
>>> print(A().a)
We're here -> attribute lookup found 'a' in one of the usual places!
We're here -> attribute lookup has not found the attribute in the usual places!
('attr: ', 'a')
not a
>>>

请注意,这种矛盾的行为不是我阅读 the official python documentation 时所期望的。 :

object.__getattr__(self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name.

(如果他们提到 AttributeError 是“属性查找”知道是否在“通常位置”找到属性的方法,那就太好了。澄清括号似乎我充其量是不完整的。)

在实践中,无论何时在 @property 描述符事物中引发 AttributeError,这都会导致跟踪由编程错误引起的错误的问题。

>>> class MessedAttrMesser(object):
... things = {
... 'one': 0,
... 'two': 1,
... }
...
... def __getattr__(self, attr):
... try:
... return self.things[attr]
... except KeyError as e:
... raise AttributeError(e)
...
... @property
... def get_thing_three(self):
... return self.three
...
>>>
>>> blah = MessedAttrMesser()
>>> print(blah.one)
0
>>> print(blah.two)
1
>>> print(blah.get_thing_three)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in __getattr__
AttributeError: 'get_thing_three'
>>>

在这种情况下,通过检查整个类很明显发生了什么。但是,如果您依赖于来自堆栈跟踪的消息,AttributeError: 'get_thing_three',它就没有意义,因为很明显,get_thing_three 看起来是一个有效的属性当他们来的时候。

kale 的目的是为构建模型提供一个基类。因此,基本模型代码对最终程序员是隐藏的,并且像这样掩盖错误的原因并不理想。

最终程序员(咳咳我)可能会选择在他们的模型上使用@property 描述符,并且他们的代码应该可以并且失败他们期望的方式。

问题

如何允许 AttributeError 传播到定义了 __getattr__ 的基类?

最佳答案

基本上,您不能 --- 或者至少不能以简单且万无一失的方式。如您所述,AttributeError 是 Python 用来确定属性是否“在通常位置找到”的机制。尽管 __getattr__ 文档没有提到这一点,但在 __getattribute__ 的文档中已经说得更清楚了,如 this answer 中所述。到您已经链接到的问题。

您可以覆盖 __getattribute__ 并在那里捕获 AttributeError,但是如果您捕获了一个错误,您将没有明显的方法来判断它是否是一个“真正的”错误(意味着确实没有找到该属性),或者在查找过程中用户代码引发的错误。理论上,您可以检查回溯以寻找特定的东西,或者进行各种其他黑客攻击来尝试验证属性的存在,但这些方法将比现有行为更加脆弱和危险。

另一种可能性是编写您自己的基于 property 的描述符,但会捕获 AttributeErrors 并将它们重新引发为其他内容。但是,这将要求您使用此属性替换而不是内置 property。此外,这意味着从描述符方法内部引发的 AttributeErrors 不会作为 AttributeErrors 传播,而是作为其他东西传播(无论你用什么替换它们)。这是一个例子:

class MyProp(property):
def __get__(self, obj, cls):
try:
return super(MyProp, self).__get__(obj, cls)
except AttributeError:
raise ValueError, "Property raised AttributeError"

class A(object):
@MyProp
def a(self):
print "We're here -> attribute lookup found 'a' in one of the usual places!"
raise AttributeError
return "a"

def __getattr__(self, name):
print "We're here -> attribute lookup has not found the attribute in the usual places!"
print('attr: ', name)
return "not a"

>>> A().a
We're here -> attribute lookup found 'a' in one of the usual places!
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
A().a
File "<pyshell#6>", line 6, in __get__
raise ValueError, "Property raised AttributeError"
ValueError: Property raised AttributeError

这里 AttributeErrors 被 ValueErrors 代替。如果您只想确保异常“突破”属性访问机制并且可以传播到下一个级别,那么这可能没问题。但是,如果您有复杂的异常捕获代码并希望看到 AttributeError,它将错过此错误,因为异常类型已更改。

(此外,这个例子显然只处理属性 getter,不处理 setter,但应该清楚如何扩展这个想法。)

我想作为此解决方案的扩展,您可以将此 MyProp 想法与自定义 __getattribute__ 结合起来。基本上,您可以定义一个自定义异常类,例如 PropertyAttributeError,并将属性替换重新引发 AttributeError 作为 PropertyAttributeError。然后,在您的自定义 __getattribute__ 中,您可以捕获 PropertyAttributeError 并将其重新引发为 AttributeError。基本上 MyProp__getattribute__ 可以充当“分流器”,通过将错误从 AttributeError 转换为其他内容,然后再次将其转换回 AttributeError 来绕过 Python 的正常处理一旦这样做是“安全的”。但是,我觉得这样做不值得,因为 __getattribute__ 会对性能产生重大影响。

一个小附录:a bug已经在 Python 错误跟踪器上提出了这个问题,并且最近有关于可能解决方案的事件,所以它可能会在未来的版本中得到修复。

关于python - 使用 __getattr__ 并满足子类的预期行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15401180/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com