强制具有多重继承的 class 在 Python 中具有特定的元 class
Force a class with multiple inheritance to have a specific metaclass in Python
我有一个 class(一个 Marshmallow schema)子class是两个 parents:
class OptionalLinkSchema(JsonApiSchema, ModelSchema): pass
JsonApiSchema
有元class SchemaMeta
ModelSchema
有元class ModelSchemaMeta(SchemaMeta)
(它是ModelSchema
的子class)。
现在,我不想要ModelSchemaMeta
元class用于我的class,我只想要更简单的[=16] =].但是,根据Python docs、"The most derived metaclass is selected",意思是无论我做什么,ModelSchemaMeta
都会被选为metaclass。
即使我尝试手动选择元class,同样的事情也会发生:
class OptionalLinkSchema(JsonApiSchema, ModelSchema, metaclass=SchemaMeta): pass
print(type(OptionalLinkSchema))
<class 'marshmallow_sqlalchemy.schema.ModelSchemaMeta'>
有没有办法覆盖 Python 始终选择派生程度最高的元数据的行为class?
首先要做的事情:我应该警告你,你可能有一个 "xy" 问题:
如果 class 被设计为使用其 metaclass 中的机制进行计数,如果这些机制在创建时不是 运行,则 class 可能无法工作.
第二件事:语言不会那样做 "on its own"。它不会因为上面的原因它会破坏东西。因此,可以采取一些方法,这些方法都是侵入性的,并且期望您知道自己在做什么。如果您的 OptionaLinkSchema
依赖于 ModelSchemaMeta
的任何功能,它就会崩溃。
我可以想到 3 种方法来实现 "stripping an inherited metaclass"
的等价物
第一种方法
您没有就您的问题发表任何意见,但我在 Marshmallow 的源代码树中没有看到 ModelSchemaMeta
- 它是您创建的元 class 吗?如果是这样,由于 Marshmallow 使用 "djangoish" 嵌套 Meta
class 的成语来声明有关 class 本身的内容,您可以在此 Meta
中使用属性] 命名空间以跳过您自己的元class。
如果是这样,请将这样的代码添加到您不需要的元class' __new__
方法中(我正在从 Marshmallow 的源代码本身中选择 "meta" 解析代码):
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
...
第二种方法
一种更具侵入性的方法,但它适用于任何 class 继承树,它具有将 superclass 与不需要的 metaclass 和创建它的克隆。但是,由于涉及 type
以外的自定义元 class,因此在这种情况下这种工作的可能性很小 - 因为 "cloning" 进程不会提供 "grandparent" metaclass 它想要转换为最终 class 中的属性的预期原始字段。此外,Marsmallow 使用 class 注册表 - 创建这样的中间克隆也会在那里注册克隆。
简而言之:这种方法不适用于你的情况,但我在这里描述它,因为它可能对遇到这个问题的其他人有用:
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)):
...
(任何人试图使用这个不是因为如果 SecondClass 本身是从使用不需要的元 class 的其他人派生的,则必须修改策略以递归地剥离元 class超级class也是)
第三种方法
另一种更简单的方法是手动创建派生元class,然后将调用硬编码到所需的元class,跳过超级class。
这东西可能会产生 "sober" 足够的代码以实际用于生产。
因此,您不需要 "set an arbitrary metaclass" - 相反,您可以创建一个有效的元数据class 来满足您的需求。
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
与第一种方法的不同之处在于,ModelSchemaMeta
将是元class 的继承树,而最终元class 将是这个新的派生元class。但这并不取决于您对 ModelSchemaMeta
代码的控制 - 正如我之前所说,这比第二种方法更 "within the expected rules" 语言。
我有一个 class(一个 Marshmallow schema)子class是两个 parents:
class OptionalLinkSchema(JsonApiSchema, ModelSchema): pass
JsonApiSchema
有元classSchemaMeta
ModelSchema
有元classModelSchemaMeta(SchemaMeta)
(它是ModelSchema
的子class)。
现在,我不想要ModelSchemaMeta
元class用于我的class,我只想要更简单的[=16] =].但是,根据Python docs、"The most derived metaclass is selected",意思是无论我做什么,ModelSchemaMeta
都会被选为metaclass。
即使我尝试手动选择元class,同样的事情也会发生:
class OptionalLinkSchema(JsonApiSchema, ModelSchema, metaclass=SchemaMeta): pass
print(type(OptionalLinkSchema))
<class 'marshmallow_sqlalchemy.schema.ModelSchemaMeta'>
有没有办法覆盖 Python 始终选择派生程度最高的元数据的行为class?
首先要做的事情:我应该警告你,你可能有一个 "xy" 问题: 如果 class 被设计为使用其 metaclass 中的机制进行计数,如果这些机制在创建时不是 运行,则 class 可能无法工作.
第二件事:语言不会那样做 "on its own"。它不会因为上面的原因它会破坏东西。因此,可以采取一些方法,这些方法都是侵入性的,并且期望您知道自己在做什么。如果您的 OptionaLinkSchema
依赖于 ModelSchemaMeta
的任何功能,它就会崩溃。
我可以想到 3 种方法来实现 "stripping an inherited metaclass"
的等价物第一种方法
您没有就您的问题发表任何意见,但我在 Marshmallow 的源代码树中没有看到 ModelSchemaMeta
- 它是您创建的元 class 吗?如果是这样,由于 Marshmallow 使用 "djangoish" 嵌套 Meta
class 的成语来声明有关 class 本身的内容,您可以在此 Meta
中使用属性] 命名空间以跳过您自己的元class。
如果是这样,请将这样的代码添加到您不需要的元class' __new__
方法中(我正在从 Marshmallow 的源代码本身中选择 "meta" 解析代码):
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
...
第二种方法
一种更具侵入性的方法,但它适用于任何 class 继承树,它具有将 superclass 与不需要的 metaclass 和创建它的克隆。但是,由于涉及 type
以外的自定义元 class,因此在这种情况下这种工作的可能性很小 - 因为 "cloning" 进程不会提供 "grandparent" metaclass 它想要转换为最终 class 中的属性的预期原始字段。此外,Marsmallow 使用 class 注册表 - 创建这样的中间克隆也会在那里注册克隆。
简而言之:这种方法不适用于你的情况,但我在这里描述它,因为它可能对遇到这个问题的其他人有用:
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)):
...
(任何人试图使用这个不是因为如果 SecondClass 本身是从使用不需要的元 class 的其他人派生的,则必须修改策略以递归地剥离元 class超级class也是)
第三种方法
另一种更简单的方法是手动创建派生元class,然后将调用硬编码到所需的元class,跳过超级class。 这东西可能会产生 "sober" 足够的代码以实际用于生产。
因此,您不需要 "set an arbitrary metaclass" - 相反,您可以创建一个有效的元数据class 来满足您的需求。
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
与第一种方法的不同之处在于,ModelSchemaMeta
将是元class 的继承树,而最终元class 将是这个新的派生元class。但这并不取决于您对 ModelSchemaMeta
代码的控制 - 正如我之前所说,这比第二种方法更 "within the expected rules" 语言。