Работа с конфликтом метаклассов с декларативной базой SQL Alchemy - PullRequest
4 голосов
/ 02 сентября 2010

У меня есть class X, который происходит от класса с его собственным метаклассом Meta.Я также хочу извлечь X из декларативной базы в SQL Alchemy.Но я не могу сделать простой

def class MyBase(metaclass = Meta):
    #...

def class X(declarative_base(), MyBase):
    #...

, поскольку получаю ошибку конфликта метакласса: «метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его баз».Я понимаю, что мне нужно создать новый метакласс, который будет производным как от Meta, так и от любого метакласса, который использует декларативная база (DeclarativeMeta, я думаю?).Так что достаточно написать:

def class NewMeta(Meta, DeclarativeMeta): pass
def class MyBase(metaclass = NewMeta):
    #...
def class X(declarative_base(), MyBase):
    #...

Я попробовал это, и это кажется работать;но, боюсь, возможно, у меня возникли проблемы с этим кодом.

Я прочитал руководство, но оно слишком загадочно для меня.Что такое

РЕДАКТИРОВАТЬ:

Код, используемый для моих классов выглядит следующим образом:

class IterRegistry(type):
    def __new__(cls, name, bases, attr):
        attr['_registry'] = {}
        attr['_frozen'] = False
        print(name, bases)
        print(type(cls))
        return type.__new__(cls, name, bases, attr)
    def __iter__(cls):
        return iter(cls._registry.values())

class SQLEnumMeta(IterRegistry, DeclarativeMeta): pass  

class EnumType(metaclass = IterRegistry):
    def __init__(self, token):
        if hasattr(self, 'token'):
            return
        self.token = token
        self.id = len(type(self)._registry)
        type(self)._registry[token] = self

    def __new__(cls, token):
        if token in cls._registry:
            return cls._registry[token]
        else:
            if cls._frozen:
                raise TypeError('No more instances allowed')
            else:
                return object.__new__(cls)

    @classmethod
    def freeze(cls):
        cls._frozen = True

    def __repr__(self):
        return self.token

    @classmethod
    def instance(cls, token):
        return cls._registry[token]

class C1(Base, EnumType, metaclass = SQLEnumMeta):
    __tablename__ = 'c1'
    #...

1 Ответ

3 голосов
/ 02 сентября 2010

Редактировать: Теперь, посмотрев на IterRegistry и DeclarativeMeta, я думаю, с вашим кодом все в порядке.

IterRegistry определяет __new__ и __iter__, а DeclarativeMeta определяет __init__ и __setattr__.Поскольку перекрытия нет, прямой необходимости звонить super нет.Тем не менее, было бы неплохо сделать так, чтобы ваш код соответствовал будущему.


У вас есть контроль над определением Meta?Можете ли вы показать нам его определение?Я не думаю, что мы можем сказать, что это работает или не работает, если мы не увидим определение Meta.

Например, существует потенциальная проблема, если ваш Meta не вызывает super(Meta,cls).__init__(classname, bases, dict_)

Если вы запустите этот код

class DeclarativeMeta(type):
    def __init__(cls, classname, bases, dict_):
        print('DeclarativeMeta')
        # if '_decl_class_registry' in cls.__dict__:
        #     return type.__init__(cls, classname, bases, dict_)       
        # _as_declarative(cls, classname, dict_)
        return type.__init__(cls, classname, bases, dict_)

class Meta(type):
    def __init__(cls, classname, bases, dict_):
        print('Meta')
        return type.__init__(cls, classname, bases, dict_)

class NewMeta(Meta,DeclarativeMeta): pass

class MyBase(object):
    __metaclass__ = NewMeta
    pass

, то будет напечатана только строка 'Meta'.Другими словами, только Meta.__init__ запускается.DeclarativeMeta.__init__ пропускается.

С другой стороны, если вы определите

class Meta(type):
    def __init__(cls, classname, bases, dict_):
        print('Meta')
        return super(Meta,cls).__init__(classname, bases, dict_)

, тогда запускаются оба Meta.__init__ и DeclarativeMeta.__init__.

...