覆盖 getattr 和 setattr 的方式的根本区别
the fundamental differences of the way to overwrite getattr and setattr
我希望使属性不区分大小写。但是覆盖 __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__
不需要?
I assume getattr()
is a syntactic sugar for __getattr__
, but they behave differently.
那是因为假设不正确。 getattr()
遍历了整个属性查找过程,__getattr__
只是其中的一部分。
属性查找首先调用一个 different 挂钩,即 __getattribute__
method, which by default performs the familiar search through the instance dict and class hierarchy. __getattr__
will be called only if the attribute hasn't been found by __getattribute__
. From the __getattr__
documentation:
Called when the default attribute access fails with an AttributeError
(either __getattribute__()
raises an AttributeError
because name is not an instance attribute or an attribute in the class tree for self
; or __get__()
of a name property raises AttributeError
).
换句话说,__getattr__
是一个 extra 挂钩,用于访问不存在的属性,否则会引发 AttributeError
.
此外,getattr()
或 len()
等函数不是 dunder 方法的语法糖。他们几乎总是做更多的工作,使用 dunder 方法 hook 供该进程调用。有时会涉及多个挂钩,例如此处,或者通过调用 class 创建 class 的实例时。有时连接是相当直接的,例如在 len()
中,但即使在简单的情况下,也会进行额外的检查,钩子本身不负责。
Why does __setattr__
need to call super()
, while __getattr__
doesn't?
__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)
我希望使属性不区分大小写。但是覆盖 __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__
不需要?
I assume
getattr()
is a syntactic sugar for__getattr__
, but they behave differently.
那是因为假设不正确。 getattr()
遍历了整个属性查找过程,__getattr__
只是其中的一部分。
属性查找首先调用一个 different 挂钩,即 __getattribute__
method, which by default performs the familiar search through the instance dict and class hierarchy. __getattr__
will be called only if the attribute hasn't been found by __getattribute__
. From the __getattr__
documentation:
Called when the default attribute access fails with an
AttributeError
(either__getattribute__()
raises anAttributeError
because name is not an instance attribute or an attribute in the class tree forself
; or__get__()
of a name property raisesAttributeError
).
换句话说,__getattr__
是一个 extra 挂钩,用于访问不存在的属性,否则会引发 AttributeError
.
此外,getattr()
或 len()
等函数不是 dunder 方法的语法糖。他们几乎总是做更多的工作,使用 dunder 方法 hook 供该进程调用。有时会涉及多个挂钩,例如此处,或者通过调用 class 创建 class 的实例时。有时连接是相当直接的,例如在 len()
中,但即使在简单的情况下,也会进行额外的检查,钩子本身不负责。
Why does
__setattr__
need to callsuper()
, while__getattr__
doesn't?
__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)