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

Posted

技术标签:

【中文标题】使用 SQL Alchemy 声明性基础处理元类冲突【英文标题】:Dealing with metaclass conflict with SQL Alchemy declarative base 【发布时间】:2011-04-07 07:09:04 【问题描述】:

我有一个class X,它派生自一个具有自己的元类Meta 的类。我还想从 SQL Alchemy 中的声明性库派生 X。但我做不到简单的

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】:

编辑:现在查看了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__ 都会运行。

【讨论】:

谢谢。我忽略了那个确切的问题。我在我的问题中添加了细节,但现在事情似乎变得太复杂了.. 谢谢。我将用return super().__new__(cls, name, bases, attr) 替换return type.__new__(cls, name, bases, attr)。虽然不是万无一失,但至少它比仅仅希望在声明性基础中没有 __new__ 好一点。当然,所有这些多重继承都是非常不安全的,因为没有人对行为做出任何精确的约定...... 多重继承通常是可以的(在 Python 中)。但是从作为不同元类实例的类的多重继承很麻烦,这是有充分理由的(这就是为什么有关于显式合并元类的规则的原因)。我们可以理解成为马和鸟的含义(即使我们从未见过),但很难理解“成为马和好主意”可能意味着什么。 :-D

以上是关于使用 SQL Alchemy 声明性基础处理元类冲突的主要内容,如果未能解决你的问题,请参考以下文章

友元的友元类

SQL Alchemy - 事务不工作

49 mysql 索引 元类

将 SQLAlchemy 声明式基础与 SQL 模型一起使用

安装了sql-alchemy但导入sql_alchemy时失败

SQL Alchemy 默认值函数,用于在一组唯一的父子记录中模拟自动增量