使用SQL Alchemy声明基类处理元类冲突

4

我有一个继承自具有自己元类Meta的类X。 我还想让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的必要。然而,这样做会让你的代码更具有未来性。


您能控制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__

谢谢。我忽略了那个确切的问题。我在我的问题中添加了细节,但是似乎事情在这一点上变得过于复杂了。 - max
谢谢。我将用return super().__new__(cls, name, bases, attr)替换return type.__new__(cls, name, bases, attr)。虽然不是万无一失,但至少比仅仅希望在Declarative Base中没有__new__要好一些。当然,所有这些多重继承都非常不安全,因为没有人对行为做出精确的约定... - max
多重继承通常是可以的(在Python中)。但是从不同元类的类的多重继承是很棘手的,有很好的理由(这就是为什么有一个明确合并元类的规则)。我们可以理解某物同时是一匹马和一只鸟意味着什么(即使我们从未见过),但很难看出“既是一匹马又是一个好主意”可能意味着什么。:-D - Veky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接