2016-11-22 71 views
3

是否可以使用get-set屬性覆蓋元類的__bases__字段(即派生自type的類)?下面的代碼適用於越來越C.__bases__,但不設置它:覆蓋元類中的__bases__

class Meta(type): 
    @property 
    def __bases__(cls): 
     print('Getting __bases__ via Meta.__bases__') 
     return super().__bases__ 

    @__bases__.setter 
    def __bases__(cls, value): 
     print('Setting __bases__ via Meta.__bases__') 
     super().__bases__ = value 

class A: pass 
class B: pass 
class C(A, B, metaclass=Meta): pass 

# >>> C.__bases__ 
# Getting __bases__ via Meta.__bases__ 
# (<class '__main__.A'>, <class '__main__.B'>) 
# >>> C.__bases__ = (B, A) 
# Setting __bases__ via Meta.__bases__ 
# AttributeError: 'super' object has no attribute '__bases__' 

我在setter函數嘗試了一些替代的super(),但他們沒有工作:

type.__setattr__(cls, '__bases__', value)線索遞歸。

object.__setattr__(cls, '__bases__', value)TypeError: can't apply this __setattr__ to type object

那麼,這是什麼歸結爲是如何設置cls.__bases__場時,它是由 在元類的屬性陰影。有任何想法嗎?


(是的,我知道定義__bases__屬性對類的實際__mro__沒有影響,儘管這可以通過重寫mro()安排)

+2

不,你不能。你爲什麼要這樣做,這個問題解決了什麼問題? –

+0

而'super()'只支持非數據描述符(所以只有'__get__')。沒有'__set__'被支持,所以'super().__ bases__ = value'將嘗試在超級代理上使用__bases__'屬性,而不是在任何基類上。 '__bases__'不能改變。 –

+0

回覆:爲什麼我想這樣做:這個想法是實現一種「類代理」,它通過複製它的'__mro__'來轉發對代理類的方法和屬性訪問。如果類代理的'__bases__'也與代理類的'__bases__'對應,那麼它就會很好,因此需要重寫它。 –

回答

2

super()不支持數據描述符,只有簡單的描述符,因爲只實現了super().__get__

換句話說,分配

super().__bases__ = value 

因爲super()代理對象不落實descriptor.__set__() method的,因而這種分配嘗試設置__bases__作爲代理對象的一個​​屬性失敗。

你不得不手動訪問type對象的描述:

class Meta(type): 
    @property 
    def __bases__(cls): 
     print('Getting __bases__ via Meta.__bases__') 
     return type.__dict__['__bases__'].__get__(cls) 

    @__bases__.setter 
    def __bases__(cls, value): 
     print('Setting __bases__ via Meta.__bases__') 
     return type.__dict__['__bases__'].__set__(cls, value) 

爲了對稱的緣故,我已經使用,吸附劑中的相同的手動描述訪問,雖然super().__bases__將工作太。

以上,我硬編碼type,而不是搜索MRO;你也可以使用一個輔助函數來找到合適的描述採用了全MRO搜索:

class Meta(type): 
    def _find_super(cls, name): 
     mro = type(cls).__mro__ 
     idx = mro.index(__class__) 
     for base in mro[idx + 1:]: 
      if name in base.__dict__: 
       return base.__dict__[name] 
     return 

    @property 
    def __bases__(cls): 
     print('Getting __bases__ via Meta.__bases__') 
     return cls._find_super('__bases__').__get__(cls) 

    @__bases__.setter 
    def __bases__(cls, value): 
     print('Setting __bases__ via Meta.__bases__') 
     return cls._find_super('__bases__').__set__(cls, value) 

不管怎樣,現在你可以攔截__bases__被設置:

>>> class A: pass 
... 
>>> class B: pass 
... 
>>> class C(A, B, metaclass=Meta): pass 
... 
>>> C.__bases__ 
Getting __bases__ via Meta.__bases__ 
(<class '__main__.A'>, <class '__main__.B'>) 
>>> C.__bases__ = (B, A) 
Setting __bases__ via Meta.__bases__ 
>>> C.__bases__ 
Getting __bases__ via Meta.__bases__ 
(<class '__main__.B'>, <class '__main__.A'>) 
+1

'__bases__' *是*可寫的。例如,定義C時不元類: '>>> C類(A,B):通過 >>>Ç.__ bases__ =(B,A)' 但是您對於'超級點()'不支持數據描述符解釋'AttributeError'。謝謝。 –

+0

@SJPalt:我糾正!在那個筆記中,我已經包括瞭如何直接訪問描述符。 –

+0

這很好,並按預期工作。我沒想過像這樣重新實現'super()'。謝謝您的回答。 –