2017-03-02 24 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