动态定义带继承和静态属性的类(sqlmodel / sqlalchemy 元类)

huangapple go评论70阅读模式
英文:

Dynamically define class with inheritance and static attributes (sqlmodel / sqlalchemy metaclasses)

问题

class SQLModel_custom(SQLModel, registry=self.mapper_registry):
metadata = MetaData(schema=self.schema)

我尝试了类似这样的方式:

type('SQLModel_custom', (SQLModel, self.mapper_registry), {'metadata': MetaData(schema=self.schema)})

但是这给我产生了以下错误:

TypeError: metaclass conflict: 派生类的元类必须是其所有基类的元类的(非严格的)子类

也许问题出在我在动态版本中定义父类时没有使用 registry=,但我不知道如何实现相同的结果。有什么建议吗?谢谢!

英文:

I have the following class definition that I would like to make dynamic:

class SQLModel_custom(SQLModel, registry=self.mapper_registry):
    metadata = MetaData(schema=self.schema)

I've tried something like that:

type('SQLModel_custom', (SQLModel, self.mapper_registry), {'metadata': MetaData(schema=self.schema)})

But this give me the following error:

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

Maybe the issue comes from the fact I'm not using registry= when defining parent classes in the dynamic version, but I don't see how I could acheive the same result.
Any advice? Thank you!

答案1

得分: 1

这里有两个问题:

  1. 正如错误消息所指示的,你调用了错误的元类。SQLModel 本身并不是用 type 构造的,而是 type 的某个子类。确定正确元类的规则可能很复杂,但在这里很简单:使用与(唯一的)父类相同的元类。

  2. 第二个参数中只包括基类;registry 不是一个基类,而是一个关键字参数,意图是传递给元类。(如果在元组中看到 self.mapper_registry,一旦你调用正确的元类,应该会看到相关错误。)

以下代码应该可以工作:

mc = type(SQLModel)

SQLModel_custom = mc('SQLModel_custom', 
                     (SQLModel,),
                     {'metadata': MetaData(schema=self.schema)},
                     registry=self.mapper_registry)

第一个问题没有我之前认为的那么严重;错误消息来自尝试使用 SQLModelself.mapper_registry 的类型来确定正确的元类。显然,type 本身可以返回适当元类的实例,而无需显式调用它。一个简单的例子:

class MyMeta(type):
    pass

class Foo(metaclass=MyMeta):
    pass

Bar = type('Bar', (Foo,), {})

# 对 type 的调用找出正确的元类是 type(Foo) == MyMeta,而不是 type 本身。
assert isinstance(Bar, MyMeta)
英文:

There are two problems here:

  1. As the error message indicates, you are calling the wrong metaclass. SQLModel itself is not constructed with type, but some other subclass of type. While the rules for determining the correct metaclass can be complicated, here it's simple: use the same one the (only) parent uses.

  2. Only base classes are included in the 2nd argument to the metaclass; registry is not a base class, but a keyword argument intended to be passed to the metaclass. (If you see self.mapper_registry in the tuple, you should see a relevant error once you call the correct metaclass.)

The following should work:

mc = type(SQLModel)

SQLModel_custom = mc('SQLModel_custom', 
                     (SQLModel,),
                     {'metadata': MetaData(schema=self.schema)},
                     registry=self.mapper_registry)

Point 1 is less of an issue than I previously thought; the error message comes from trying to use the types of SQLModel and self.mapper_registry to determine the correct metaclass. type itself apparently can return an instance of the proper metaclass without you needing to call it explicitly. A simple example:

class MyMeta(type):
    pass


class Foo(metaclass=MyMeta):
    pass


Bar = type('Bar', (Foo,), {})


# The call to type figures out that the correct metaclass is
# type(Foo) == MyMeta, not type itself.
assert isinstance(Bar, MyMeta)

huangapple
  • 本文由 发表于 2023年5月22日 21:51:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/76306898.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定