我希望使属性不区分大小写。但是覆盖__getattr__
和__setattr__
有点不同,如以下玩具示例所示:
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()
是__getattr__
的语法糖,但是它们的行为有所不同。__setattr__
需要呼叫super()
,而__getattr__
却不需要?答案 0 :(得分:6)
我假设
getattr()
是__getattr__
的语法糖,但是它们的行为有所不同。
那是因为假设是不正确。 getattr()
经历了整个属性查找过程,其中__getattr__
仅是一部分。
属性查找首先调用一个 different 钩子,即__getattribute__
method,该钩子默认情况下通过实例dict和类层次结构执行熟悉的搜索。 __getattr__
仅在__getattribute__
找不到属性时才会被调用。来自__getattr__
documentation:
当默认属性访问失败并带有
AttributeError
时调用(__getattribute__()
会引发AttributeError
,因为 name 不是实例属性,也不是self
的类树;或 name 属性的__get__()
产生AttributeError
)。
换句话说,__getattr__
是一个 extra 钩子,用于访问不存在的属性,否则将引发AttributeError
。
此外,诸如getattr()
或len()
之类的函数对于dunder方法也不是语法糖。他们几乎总是做更多的工作,使用dunder方法将 hook 进行调用。有时涉及多个钩子,例如在这里,或者在通过调用类创建类的实例时。有时连接是相当直接的,例如在len()
中,但是即使在简单的情况下,也要进行额外的检查,以确保钩子本身不负责。
为什么
__setattr__
需要呼叫super()
,而__getattr__
却不需要?
__getattr__
是可选钩子。没有默认实现,这就是super().__getattr__()
不起作用的原因。 __setattr__
是不是可选的,因此object
为您提供了默认的实现。
请注意,通过使用getattr()
,您创建了一个无限循环! instance.non_existing
先呼叫__getattribute__('non_existing')
,然后再呼叫__getattr__('non_existing')
,此时您使用getattr(..., 'non_existing')
,后者先呼叫__getattribute__()
和__getattr__
,等等。
在这种情况下,您应该改用__getattribute__
:
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)