实例和类的__getattribute__

时间:2017-03-02 02:06:44

标签: python inheritance metaclass

我有一个元类和类,它们都使用__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'

现在我想从BC中获取重复的代码并让它继承。幸运的是,我打电话给他们BC并为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),但它们略有不同,并没有实现我想要的目标。

由于

1 个答案:

答案 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'
>>>