处理SQL Alchemy声明基的metaclass冲突

4 投票
1 回答
2184 浏览
提问于 2025-04-16 03:37

我有一个 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

编辑:现在看过 IterRegistryDeclarativeMeta 之后,我觉得你的代码是没问题的。

IterRegistry 定义了 __new____iter__,而 DeclarativeMeta 定义了 __init____setattr__。因为它们之间没有重叠,所以不一定需要调用 super。不过,调用 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__ 都会被执行。

撰写回答