Python中的__getattr__方法

3

我今天遇到了这段代码,看起来很混乱。

class ClassOne:
    def __init__(self,some_object):
        self.not_important_attribute=some_object   
class ClassTwo:
    def __init__(self,some_object):
        self.tmp=ClassOne(some_object)
    def __getattr__(self,attr):
        return getattr(self.tmp,attr)
a=ClassTwo('not_important_string')
print(getattr(a,'undefined_attribute',3))

当我们在最后一行使用getattr时,会触发SubClass中的__getattr__方法,然后委托给getattr(self.tmp,attr)函数,如果属性未定义且没有给出默认值,则会引发异常。但是最后一行的值3是如何经过所有过程并最终返回到getattr(a,'undefined_attribute',3)函数的呢?这是因为在委托getattr(self.tmp,attr)时没有为默认值留下空间,这怎么可能呢?


是的,这不是继承机制,我会进行编辑。 - Tuấn Đoàn
1
如果 _getattr_ 引发 AttributeError,则函数 getattr 将返回默认值(在您的情况下为 3)。 - Andrei Berenda
@AndreyBerenda 我不确定这是正确的,当调用方法__getattr__时,它将返回我们在函数内定义的值,而不是类外部的默认值,请查看此链接 - Tuấn Đoàn
4个回答

2
最初的回答:在你的情况下

getattr(self.tmp,attr)

如果getattr函数有第三个参数(默认值),则会返回默认值而不是引发AttributeError错误。

原始答案: "最初的回答"


如果getattr有第三个参数,你是在讨论哪一个getattr,是类内的getattr(self.tmp,attr)还是类外的getattr(a,'undefined_attribute',3) - Tuấn Đoàn
@TuấnĐoàn 两者都在调用 ClassOne 类的属性 not_important_attribute,请看我的答案获取更多细节。 - developer_hatch
我在谈论getattr(a,'undefined_attribute',3)。getattr(self.tmp,attr)会引发AttributeError,而getattr(a,'undefined_attribute',3)由于默认值返回3。 - Andrei Berenda
@AndreyBerenda 当调用 __getattr__ 方法时,它将返回我们在函数内定义的值,而不是类外部的默认值,请查看此链接 - Tuấn Đoàn
我在谈论这个例子 print(getattr(b,'undefined_attribute',3)) - Andrei Berenda

1

逐步解释为什么显示3数值:

超类具有属性not_important_attribute,并在构造函数调用时设置它

class ClassOne:
    def __init__(self,some_object):
        self.not_important_attribute=some_object   

ClassTwo构造函数中,您创建了一个ClassOne实例并将其保存到tmp变量中。这意味着当您覆盖__getattr__时,您将请求ClassOne属性的值。
print(getattr(a,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(a,'any',3))
3 #doesn't found method, returns default

这是直接执行的相同操作:
b = ClassOne("not_important_string")
print(getattr(b,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(b,'any',3))
3 # doesn't found method, returns default  

0

调用 getattr(a,'undefined_attribute',3) 时,您正在调用标准的Python getattr函数并传递一个默认值。这实际上是将您自定义的 getattr 包装在ClassTwo中。我们可以通过稍微修改 getattr 来看到这一点。

   def __getattr__(self, attr, *args, **kwargs):
        print(args)  # -> empty
        print(kwargs)  # -> empty
        return getattr(self.tmp, attr)

你实际上可以绕过这个包装,直接从ClassTwo中调用getattr,方法是使用print(a.__getattr__('undefined_attribute', 3))。这种方法会引发异常。

因此,getattr(a,'undefined_attribute',3)是标准的Python方法,它在内部调用了来自ClassTwo的自定义getattr。


0
class ClassTwo:
...
    def __getattr__(self,attr):
        return getattr(self.tmp,attr)

getattr(a,'undefined_attribute',3) 方法是一个包装器,用于调用 __getattribute__,如果 __getattribute__ 失败,则会调用 __getattr__。它实现了对异常 AttributeError 的处理。在您的情况下,getattr(a,'undefined_attribute',3) 调用了上面的 ClassTwo.__getattr__

看起来你认为当它到达 return getattr(self.tmp,attr) 时,它会抛出错误或返回某种错误并在那个点完全停止和中断。然而,在 Python 中,程序将异常传递到调用堆栈。在调用堆栈的上游,如果该异常得到处理,它将正常退出/完成。在这种情况下,AttributeError 沿着调用堆栈传递回 getattr(a,'undefined_attribute',3)。这个 getattr 对于 AttributeError 有默认值,因此它返回 3 并正常退出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接