2016-11-22 70 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()'。谢谢您的回答。 –