2017-03-02 66 views
0

我有一个元类和和类,都使用__getattribute__拦截属性调用。他们是这样的:__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' 

我认为问题是围绕一个元不能够从一个普通类继承的东西,但我不能确定。我也开放给任何其他实现(可能不需要元类)获取相同的行为 - 但我仍然想要调用像C.MISSING来提高AttributeError

也有类似的问题(例如Get attributes for class and instance in python),但它们略有不同,并没有达到我想要的。

感谢

+0

OOC,如果您先从“A”继承,会发生什么情况'B类(A,类型):'? – ShadowRanger

+0

是的,我曾试过这个。 C.MY_ATTR引发异常。 '你好 __getattribute__ TypeError:期待1个参数,得到0' – freebie

回答

0

这是比这更复杂一些 - 元类接受普通班作为混入 - 并提供有关这些混入任何方法将可在使用这些元类,好像他们是classmethods类(但是它们将从实例中隐藏起来 - 因为实例上的属性查找查找类上的属性,但不查找元类上的属性)。

但是__getattribute__是一只野兽 - 即使没有涉及元类,也很难做到这一点(与__getattr__不同)。

我做了一些试验和错误我自己,并得到了type__getattribute__确实存在,是从不同的object的 - 这就是为什么当你做class M(type, A)继承您的自定义版本不叫。而且,如果交换继承顺序,您会发现super()会出现一个转角情况,它不会填充所需的参数以正确调用type.__getattribute__

我试图用硬编码的if语句调用type.__getattribute__,并在属性查询循环了,我无法弄清楚 - 事情是不平凡的(但可能是由于IPython的反思 - 参见下文) 。

尽管如此,重用类和元类本身的代码是我从来没有看到的东西。

如果你想在__getattribute__身上做的是平凡的(而不是在这个例子的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的首先查找Ç__class__属性 -

等待,在双重检查,在常规的Python这样做时提示,不会出现这些电话 - 这是IPython的触发话,而在它的自省魔法的东西。它可能是我所陷入的递归循环是由于IPython的内省 - 尽管如此,您将避免大量不必要的魔法,让元类和基类中的元素明确表达出来,并将其与上述因素分开。

>>> C().MY_ATTR 
<__main__.C object at 0x7f0d4d908898> MY_ATTR 
'hello attr' 
>>> C.MY_ATTR 
<class '__main__.C'> MY_ATTR 
'hello attr' 
>>> 
+0

非常感谢,谢谢。我确实尝试将逻辑分离成一个外部函数,但是这导致了它自己的一组错误。现在已经有一段时间了,所以我不记得具体。虽然,我可能一直在使用REPL,并且正在接受检查错误 - 就像您提到的那样。我会看看我是否有在git中提交的代码,或者我是否可以重现。再次感谢 – freebie