我有一个元类和类,它们都使用__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'
我认为问题是围绕MetaClass无法继承常规课程的问题,但我不确定。我也可以接受任何其他实现(可能不需要元类)来获得相同的行为 - 尽管我仍然希望像C.MISSING
这样的调用来提升AttributeError
。
对此有类似的问题(例如Get attributes for class and instance in python),但它们略有不同,并没有实现我想要的目标。
由于
答案 0 :(得分:0)
比这复杂一点 - 元类做接受普通类作为mixins - 这些mixin上提供的任何方法都可以在类上使用这些元类,就像它们是classmethods一样(但它们将被隐藏在实例中 - 作为属性查找实例在类上查找属性,但不在元类上查找。
然而__getattribute__
是野兽 - 即使没有涉及元类,也很难正确执行(与__getattr__
不同)。
我自己做了一些反复试验,得到type
__getattribute__
确实存在且与object
不同 - 这是为什么在执行class M(type, A)
继承时未调用自定义版本。而且,如果你交换继承顺序,你会发现super()
得到了一个极端情况,它没有填写正确调用type.__getattribute__
所需的参数。
我尝试使用type.__getattribute__
语句对if
的调用进行硬编码,并进入属性查找循环,我无法弄清楚 - 事情是非平凡的(但是可能是由于IPython内省 - 见下文)。
尽管如此,重复使用类和元类本身的代码是我从未想过的。
如果你想在__getattribute__
身体中做什么是非常重要的(而不是示例&#39; s print
),我建议你只考虑__getattribute__
身体普通函数,并在您将print
放在示例上时明确调用它 - 并且只在元类和类库上保留三行__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__
显式在元类和基类中,并将其分解为如上所述
>>> C().MY_ATTR
<__main__.C object at 0x7f0d4d908898> MY_ATTR
'hello attr'
>>> C.MY_ATTR
<class '__main__.C'> MY_ATTR
'hello attr'
>>>