Нахождение __metaclass__ используется перед переопределением в Python - PullRequest
3 голосов
/ 13 января 2012

Я хочу переопределить __metaclass__, но хочу вернуться к метаклассу, который был бы использован, если бы я не переопределил.

class ComponentMetaClass(type):

    def __new__(cls, name, bases, dct):

        return <insert_prev_here>.__new__(cls, name, bases, dct)


class Component(OtherObjects):
     __metaclass__ = ComponentMetaClass

Из того, что я понимаю, __metaclass__, используемый по умолчанию, идетчерез процесс проверки определения в области видимости класса, затем в базах и затем в глобальном.Обычно вы будете использовать тип в переопределении, и обычно это глобальный тип, однако, мои OtherObjects, возможно, переопределили __metaclass__.Поэтому при использовании типа я бы проигнорировал их определение, и они не запустились бы, верно?

edit: заметьте, что я не знаю, что такое OtherObjects до времени выполнения

Ответы [ 2 ]

3 голосов
/ 13 января 2012

Как говорит @unutbu: «Внутри одной иерархии классов метаклассы должны быть подклассами друг друга. То есть метакласс Компонента должен быть подклассом метакласса OtherObjects.»

Это означает, что ваша проблема немного сложнее, чем вы, во-первых - не только вы должны вызывать правильный метакласс из базовых классов, но и ваш текущий метакласс должен также правильно наследовать.

(взломать код, столкнуться со странным поведением, вернуться через 90 минут) Это было действительно сложно - мне пришлось создать класс, который получает желаемый метакласс в качестве параметра и который методом __call__ динамически генерирует новый метакласс, изменяя его базы и добавляя к нему атрибут __superclass.

Но это должно делать то, что вы хотите, и даже больше - вам просто нужно наследовать все ваши метаклассы от BaseComponableMeta и вызывать суперклассы в иерархии через атрибут метакласса "__superclass":

from itertools import chain

class Meta1(type):
    def __new__(metacls, name, bases, dct):
        print name
        return type.__new__(metacls, name, bases, dct)

class BaseComponableMeta(type):
    def __new__(metacls, *args, **kw):
        return metacls.__superclass.__new__(metacls, *args, **kw)

class ComponentMeta(object):
    def __init__(self, metaclass):
        self.metaclass = metaclass
    def __call__(self, name, bases,dct):
        #retrieves the deepest previous metaclass in the object hierarchy
        bases_list = sorted ((cls for cls in chain(*(base.mro() for base in bases)))
        , key=lambda s: len(type.mro(s.__class__)))   
        previous_metaclass = bases_list[-1].__class__
        # Adds the "__superclass" attribute to the metaclass, so that it can call
        # its bases:
        metaclass_dict = dict(self.metaclass.__dict__).copy()
        new_metaclass_name = self.metaclass.__name__ 
        metaclass_dict["_%s__superclass" % new_metaclass_name] = previous_metaclass
        #dynamicaly generates a new metaclass for this class:
        new_metaclass = type(new_metaclass_name, (previous_metaclass, ), metaclass_dict)
        return new_metaclass(name, bases, dct)

# From here on, example usage:

class ComponableMeta(BaseComponableMeta):
    pass

class NewComponableMeta_1(BaseComponableMeta):
    def __new__(metacls, *args):
        print "Overriding the previous metaclass part 1"
        return metacls.__superclass.__new__(metacls, *args)

class NewComponableMeta_2(BaseComponableMeta):
    def __new__(metacls, *args):
        print "Overriding the previous metaclass part 2"
        return metacls.__superclass.__new__(metacls, *args)

class A(object):
    __metaclass__ = Meta1


class B(A):
    __metaclass__ = ComponentMeta(ComponableMeta)

# trying multiple inheritance, and subclassing the metaclass once:
class C(B, A):
    __metaclass__ = ComponentMeta(NewComponableMeta_1)

# Adding a third metaclass to the chain:
class D(C):
    __metaclass__ = ComponentMeta(NewComponableMeta_2)

# class with a "do nothing" metaclass, which calls its bases metaclasses:  
class E(D):
    __metaclass__ = ComponentMeta(ComponableMeta)
1 голос
/ 13 января 2012

В пределах одной иерархии классов метаклассы должны быть подклассами друг друга.То есть метакласс Component должен быть подклассом метакласса OtherObjects.

Если вы не назовете __metaclass__ для Component, то по умолчанию будет использоваться метакласс OtherObjects.


Если ComponentMetaClass и OtherObjectsMetaоба наследуют (независимо) от type:

class OtherObjectsMeta(type): pass
class ComponentMetaClass(type): pass

class OtherObjects(object):
    __metaclass__ = OtherObjectsMeta

class Component(OtherObjects):
     __metaclass__ = ComponentMetaClass

, тогда вы получите эту ошибку:

TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

, но если вы сделаете ComponentMetaClass в качестве подкласса OtherObjectsMeta

class ComponentMetaClass(OtherObjectsMeta): pass

тогда ошибка исчезнет.


Возможно, я неправильно понял ваш вопрос.Если хотите ComponentMetaClass.__new__ для вызова OtherObjectsMeta.__new__, то используйте super:

class OtherObjectsMeta(type): 
    def __new__(meta, name, bases, dct):
        print('OtherObjectsMeta')
        return super(OtherObjectsMeta,meta).__new__(meta,name,bases,dct)
class ComponentMetaClass(OtherObjectsMeta):
    def __new__(meta, name, bases, dct):
        print('ComponentMetaClass')
        return super(ComponentMetaClass,meta).__new__(meta,name,bases,dct)  

Относительно альтернативы использованию метаклассов, упомянутых в комментариях.Используйте super:

class Base(object):
    def method(self): pass

class Base1(Base):
    def method(self):
        print('Base1')
        super(Base1,self).method()

class Base2(Base): 
    def method(self):
        print('Base2')
        super(Base2,self).method()

class Component(Base1,Base2):
    pass

c = Component()
c.method()
...