Mig*_*ell 1 python metaclass python-3.x marshmallow
class OptionalLinkSchema(JsonApiSchema, ModelSchema): pass
Run Code Online (Sandbox Code Playgroud)
JsonApiSchema 有元类 SchemaMetaModelSchema具有元类ModelSchemaMeta(SchemaMeta)(它是的子类ModelSchema)。现在,我不希望ModelSchemaMeta我的课元类,我只想简单SchemaMeta。但是,根据Python文档,“选择了最派生的元类”,这意味着无论我做什么,ModelSchemaMeta都将被选作元类。
即使我尝试手动选择一个元类,也会发生相同的事情:
class OptionalLinkSchema(JsonApiSchema, ModelSchema, metaclass=SchemaMeta): pass
print(type(OptionalLinkSchema))
Run Code Online (Sandbox Code Playgroud)
<class 'marshmallow_sqlalchemy.schema.ModelSchemaMeta'>
Run Code Online (Sandbox Code Playgroud)
有没有办法替代始终选择最派生的元类的Python行为?
首先,第一件事:我应该警告您,那里可能存在一个“ xy”问题:如果一个类旨在使用其元类中的机制进行计算,如果这些机制在创建时未运行,则该类很可能会获胜。工作。
第二件事:语言不会“独自”做到这一点。而且不会,因为上面的原因会破坏事情。因此,可以采用一些方法,这些方法都是侵入性的,并且希望您知道自己在做什么。如果您OptionaLinkSchema依赖上的任何功能ModelSchemaMeta,它只会中断。
我可以想到三种实现“剥离继承的元类”的等效方法
您没有回答任何问题,但是ModelSchemaMeta在棉花糖的源代码树中看不到-它是您创建的元类吗?如果是这样,由于棉花糖使用具有嵌套Meta类的“ djangoish”成语来声明有关类本身的内容,因此可以在此Meta命名空间中使用属性来跳过自己的元类。
如果是这样,请将这样的代码添加到您不希望的metaclass的__new__方法中(我是从棉花糖的源代码本身中选择“元”解析代码):
class ModelSchemaMeta(SchemaMeta):
def __new__(mcs, name, bases, attrs):
meta = attrs.get("Meta")
if getattr(meta, "skip_model_schema_meta", False):
# force ShemaMeta metaclass
cls = super().__new__(SchemaMeta, name, bases, attrs)
# manually call `__init__` because Python does not do that
# if the value returned from `__new__` is not an instance
# of `mcs`
cls.__init__(name, bases, attrs)
return cls
# Your original ModelSchemaMeta source code goes here
class OptionalLinkSchema(...):
...
class Meta:
skip_model_schema_meta = True
...
Run Code Online (Sandbox Code Playgroud)
一种严重的更具侵入性的方法,但这对于任何类继承树都适用,其代码将采用带有不需要的元类的超类并创建其副本。但是,由于除了type涉及之外还存在自定义元类,因此在这种情况下,这种工作的可能性很小-因为“克隆”过程不会为“祖父母”元类提供希望转换为原属性的预期原始字段。期末班。此外,Marsmallow使用类注册表-创建这样的中间克隆将在其中注册该克隆。
简而言之:这种方法不适用于您的情况,但我在此进行描述,因为它可能对遇到此问题的其他人有用:
def strip_meta(cls,):
"builds a clone of cls, stripping the most derived metaclass it has.
There are no warranties the resulting cls will work at all - specially
if one or more of the metaclasses that are being kept make transformations
on the attributes as they are declared in the class body.
"
new_meta = type(cls).__mro__[1:]
return (new_meta(cls.__name__, cls.__mro__, dict(cls.__dict__)))
class OptionalLinkSchema(FirstClass, strip_meta(SecondClass)):
...
Run Code Online (Sandbox Code Playgroud)
(任何尝试使用此方法的人都不是如果SecondClass本身是从使用不需要的元类的其他类派生的,则还必须修改该策略以递归地剥离超类的元类)
另一种更简单的方法是手动创建派生的元类,然后仅硬编码对所需元类的调用,而跳过超类。这件事可能会产生“清醒”的代码,足以在生产中实际使用。
因此,您不必“设置任意的元类”-而是创建一个可以执行所需操作的有效元类。
class NewMeta(ModelSchemaMeta, SchemaMeta):
"""Order of inheritance matters: we ant to skip methods on the first
baseclass, by hardwiring calls to SchemaMeta. If we do it the other way around,
`super` calls on SchemaMeta will go to ModelSchemaMeta).
Also, if ModeSchemaMeta inherits from SchemaMeta, you can inherit from it alone
"""
def __new__(*args, **kw):
return SchemaMeta.__new__(*args, **kw)
def __init__(*args, **kw):
return SchemaMeta.__init__(*args, **kw)
# repeat for other methos you want to use from SchemaMeta
# also, undesired attributes can be set to "None":
undesired_method_in_model_schema_meta = None
Run Code Online (Sandbox Code Playgroud)
此方法与第一种方法的区别在于,ModelSchemaMeta它将是元类的继承树,而最终的元类将是这个新的派生元类。但这并不取决于您对ModelSchemaMeta代码的控制-而且,正如我之前所说,与第二种方法相比,这更是“在预期规则之内”。