__getattribute__ 实例和 class
__getattribute__ on instance and class
我有一个 metaclass 和 class,它们都使用 __getattribute__
来拦截属性调用。它们看起来像这样:
class B(type):
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
class C(metaclass=B):
MY_ATTR = 'hello attr'
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
这符合我的预期:
C.MY_ATTR
# hello <class 'C'>
# 'hello attr'
C().MY_ATTR
# hello <C object at 0x10bcbbda0>
# 'hello attr'
现在我想从 B
和 C
中取出重复的代码并让它被继承。幸运的是,我称它们为 B
和 C
,并为 A
留了空间。我们开始吧:
class A:
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
class B(type, A):
pass
class C(A, metaclass=B):
MY_ATTR = 'hello attr'
不幸的是,这不再像以前那样:
C.MY_ATTR
# 'hello attr'
C().MY_ATTR
# hello <C object at 0x10bcbbda0>
# 'hello attr'
我认为问题出在无法从常规 class 继承的 MetaClass 周围,但我不确定。我也对获得相同行为的任何其他实现(可能不需要 metaclass)持开放态度——尽管我仍然希望像 C.MISSING
这样的调用引发 AttributeError
.
也有类似的问题(例如 Get attributes for class and instance in python),但它们略有不同,并没有达到我想要的效果。
谢谢
比那复杂一点-
Metaclasses do 接受普通的 classes 作为 mixin - 并且这些 mixin 上提供的任何方法都将在使用这些元的 classes 上可用class 就好像它们是 class 方法一样(但它们将从实例中隐藏 - 因为 n 实例上的属性查找查找 class 上的属性,而不是元 class).
然而 __getattribute__
是一个野兽 - 即使不涉及元 class 也很难正确地做到这一点(不像 __getattr__
)。
我自己做了一些试验和错误,发现 type
的 __getattribute__
确实存在并且不同于 object
的 - 这就是为什么你自定义版本在执行 class M(type, A)
继承时不会调用。而且,如果你交换继承顺序,你会看到 super()
得到一个极端情况,它没有填写正确调用 type.__getattribute__
所需的参数。
我试图用 if
语句硬编码对 type.__getattribute__
的调用,并进入了一个属性查找循环,我无法弄清楚 - 这件事很重要(但是可能是由于 IPython 自省 - 见下文)。
尽管如此,在 class 和 metaclass 上重用代码本身是我从未见过的事情。
如果你想在 __getattribute__
主体中做的事情很重要(与示例的 print
相对),我建议你将 __getattribute__
主体分解为一个普通函数,并在示例中放置 print
的位置显式调用它 - 并且只在 metaclass 和 class base:[=30= 上保留三行 __getattribute__
]
def getattribute(instance_or_class, attr_name):
# complex attr_name consuming body goes here.
# even colateral effects on "self" can be applied
# to the "instance_or_class" object
print(instance_or_class, attr_name)
class B(type):
def __getattribute__ (cls, name):
getattribute(cls, name)
return super().__getattribute__(name)
class C(metaclass=B):
MY_ATTR = 'hello attr'
def __getattribute__ (self, name):
getattribute(self, name)
return super().__getattribute__(name)
现在,我看到与您的第一个输出不同的一件事 - 我实际上得到:
In [43]: C().MY_ATTR
<class '__main__.C'> __class__
<class '__main__.C'> __class__
<__main__.C object at 0x7fa420a314a8> MY_ATTR
Out[43]: 'hello attr'
即Python先查找C__class__
属性-
等等,仔细检查,在常规 Python 提示符下执行此操作时,不会发生这些调用 - 然后是 Ipython 触发,同时它是内省魔法。我陷入的递归循环可能是由于 IPython 的自省 - 尽管如此,你将避免很多不需要的魔法让 __getattribute__
在 metaclass 和 base classes,并按上述方式分解。
>>> C().MY_ATTR
<__main__.C object at 0x7f0d4d908898> MY_ATTR
'hello attr'
>>> C.MY_ATTR
<class '__main__.C'> MY_ATTR
'hello attr'
>>>
我有一个 metaclass 和 class,它们都使用 __getattribute__
来拦截属性调用。它们看起来像这样:
class B(type):
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
class C(metaclass=B):
MY_ATTR = 'hello attr'
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
这符合我的预期:
C.MY_ATTR
# hello <class 'C'>
# 'hello attr'
C().MY_ATTR
# hello <C object at 0x10bcbbda0>
# 'hello attr'
现在我想从 B
和 C
中取出重复的代码并让它被继承。幸运的是,我称它们为 B
和 C
,并为 A
留了空间。我们开始吧:
class A:
def __getattribute__ (self, name) :
print(f'hello {self}')
return super().__getattribute__(name)
class B(type, A):
pass
class C(A, metaclass=B):
MY_ATTR = 'hello attr'
不幸的是,这不再像以前那样:
C.MY_ATTR
# 'hello attr'
C().MY_ATTR
# hello <C object at 0x10bcbbda0>
# 'hello attr'
我认为问题出在无法从常规 class 继承的 MetaClass 周围,但我不确定。我也对获得相同行为的任何其他实现(可能不需要 metaclass)持开放态度——尽管我仍然希望像 C.MISSING
这样的调用引发 AttributeError
.
也有类似的问题(例如 Get attributes for class and instance in python),但它们略有不同,并没有达到我想要的效果。
谢谢
比那复杂一点- Metaclasses do 接受普通的 classes 作为 mixin - 并且这些 mixin 上提供的任何方法都将在使用这些元的 classes 上可用class 就好像它们是 class 方法一样(但它们将从实例中隐藏 - 因为 n 实例上的属性查找查找 class 上的属性,而不是元 class).
然而 __getattribute__
是一个野兽 - 即使不涉及元 class 也很难正确地做到这一点(不像 __getattr__
)。
我自己做了一些试验和错误,发现 type
的 __getattribute__
确实存在并且不同于 object
的 - 这就是为什么你自定义版本在执行 class M(type, A)
继承时不会调用。而且,如果你交换继承顺序,你会看到 super()
得到一个极端情况,它没有填写正确调用 type.__getattribute__
所需的参数。
我试图用 if
语句硬编码对 type.__getattribute__
的调用,并进入了一个属性查找循环,我无法弄清楚 - 这件事很重要(但是可能是由于 IPython 自省 - 见下文)。
尽管如此,在 class 和 metaclass 上重用代码本身是我从未见过的事情。
如果你想在 __getattribute__
主体中做的事情很重要(与示例的 print
相对),我建议你将 __getattribute__
主体分解为一个普通函数,并在示例中放置 print
的位置显式调用它 - 并且只在 metaclass 和 class base:[=30= 上保留三行 __getattribute__
]
def getattribute(instance_or_class, attr_name):
# complex attr_name consuming body goes here.
# even colateral effects on "self" can be applied
# to the "instance_or_class" object
print(instance_or_class, attr_name)
class B(type):
def __getattribute__ (cls, name):
getattribute(cls, name)
return super().__getattribute__(name)
class C(metaclass=B):
MY_ATTR = 'hello attr'
def __getattribute__ (self, name):
getattribute(self, name)
return super().__getattribute__(name)
现在,我看到与您的第一个输出不同的一件事 - 我实际上得到:
In [43]: C().MY_ATTR
<class '__main__.C'> __class__
<class '__main__.C'> __class__
<__main__.C object at 0x7fa420a314a8> MY_ATTR
Out[43]: 'hello attr'
即Python先查找C__class__
属性-
等等,仔细检查,在常规 Python 提示符下执行此操作时,不会发生这些调用 - 然后是 Ipython 触发,同时它是内省魔法。我陷入的递归循环可能是由于 IPython 的自省 - 尽管如此,你将避免很多不需要的魔法让 __getattribute__
在 metaclass 和 base classes,并按上述方式分解。
>>> C().MY_ATTR
<__main__.C object at 0x7f0d4d908898> MY_ATTR
'hello attr'
>>> C.MY_ATTR
<class '__main__.C'> MY_ATTR
'hello attr'
>>>