Python 覆盖getattr和setattr方法的根本区别

Python 覆盖getattr和setattr方法的根本区别,python,python-3.x,getattr,setattr,Python,Python 3.x,Getattr,Setattr,我希望使属性不区分大小写。但是覆盖uuu getattr_uuu和uuu setattr_uuu有些不同,如下例所示: class A(object): x = 10 def __getattr__(self, attribute): return getattr(self, attribute.lower()) ## following alternatives don't work ## # def __getattr__(self, a

我希望使属性不区分大小写。但是覆盖uuu getattr_uuu和uuu setattr_uuu有些不同,如下例所示:

class A(object):

    x = 10

    def __getattr__(self, attribute):
        return getattr(self, attribute.lower())

    ## following alternatives don't work ##

#    def __getattr__(self, attribute):
#        return self.__getattr__(attribute.lower()) 

#    def __getattr__(self, attribute):
#        return super().__getattr__(attribute.lower()) 

    def __setattr__(self, attribute, value):
        return super().__setattr__(attribute.lower(), value)

    ## following alternative doesn't work ##

#    def __setattr__(self, attribute, value):
#        return setattr(self, attribute.lower(), value)

a = A()
print(a.x) ## output is 10
a.X = 2
print(a.X) ## output is 2
我被两点弄糊涂了

我假设getattr是uu getattr_uuu的语法糖,但它们的行为不同。 为什么setattr需要调用super,而getattr不需要调用super? 我假设getattr是uu getattr_uuu的语法糖,但它们的行为不同

那是因为这个假设是不正确的。getattr经历了整个属性查找过程,其中u getattr_u只是其中的一部分

属性查找首先调用另一个钩子,即,默认情况下,它通过实例dict和类层次结构执行熟悉的搜索__仅当_getattribute__未找到该属性时,才会调用getattr。从:

当默认属性访问失败且出现AttributeError时调用,因为名称不是实例属性,也不是self类树中的属性,所以\uuu getattribute\uuuuu会引发AttributeError;或者名称属性的“获取”会引发AttributeError

换句话说,getattr是一个额外的钩子,用于访问不存在的属性,否则会引发AttributeError

此外,getattr或len等函数不是dunder方法的语法糖。他们几乎总是做更多的工作,dunder方法是该进程调用的一个钩子。有时会涉及多个钩子,比如在这里,或者通过调用类来创建类的实例时。有时连接相当直接,例如在len中,但即使在简单的情况下,也会进行额外的检查,而钩子本身并不负责

为什么setattr需要调用super,而getattr不需要调用super

__getattr__;是一个可选的钩子。没有默认实现,这就是super.\uuu getattr\uuuu不工作的原因__setattr_u;不是可选的,所以对象为您提供了一个默认实现

注意,通过使用getattr,您创建了一个无限循环!instance.non_existing将调用uuu getattribute_uu'non_existing',然后调用u_getattr_uu'non_existing',此时使用getattr…,“non_existing”调用u_ugetAttribute_uuu',然后调用u_ugetattr_uu,等等

在这种情况下,您应该替代_getattribute _uu:


__对任何不存在的属性调用getattr\uuuuu;您不希望通过对不存在的属性使用getattr来创建无限循环!顺便说一句,你错过了这里的delattr。这也意味着您正在尝试学习如何在不阅读定制属性访问的数据模型文档的情况下完成这项工作,这解释了您的许多困惑。虽然与描述符、类型对象等的交互可能会变得复杂,但这一点您还不必担心,文档对此进行了很好的解释,而这不是一种您可以猜测其工作方式并从中得出结论的事情。。请注意,它直接解释了uuu getattr_uuuuuuuu和uuu setattr_uuuuuu之间的有意不对称,还解释了uuu setattr_uuuu应如何使用基类方法,等等。可能还值得解释的是,spamx从来都不是x的语法糖。uu spam_uuuuuuuuuu,而不仅仅是在u getattr_uuuuuuuuuuuu的情况下。例如,boolx尝试x.uuu bool_uuuuuuuuux.uuuu len_uuuuuuu;iterx尝试x.uuu iter_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;x+y根据类型关系按顺序尝试x.uuu添加_uuuy和y.uu radd_uuux;等等。直接调用dunder方法不是你想做的事情,除非在特定的特殊情况下,比如调用你的super的setattr。@abarnert:是的,即使是在简单的情况下,比如len调用len,也有一些类型和边界检查,你不一定想绕过。我相信你还记得那个家伙的问题,他想调用len作为优化,以避免浪费时间将AttributeError转换为TypeError,并且坚持认为timeit必须被破坏,因为它说他的代码是慢的而不是快的,并且拒绝接受查找、构建和调用绑定方法需要时间…
class A(object):
    x = 10

    def __getattribute__(self, attribute):
        return super().__getattribute__(attribute.lower())

    def __setattr__(self, attribute, value):
        return super().__setattr__(attribute.lower(), value)