Перво-наперво: я должен предупредить вас, что у вас, вероятно, есть проблема "xy": если класс предназначен для работы с механизмами в его метаклассе, если они не запускаются при создании, скорее всего,класс не будет работать.
Второе: язык не будет делать это "сам по себе". И это не будет, потому что это сломало бы вещи по причине выше. Итак, есть несколько подходов, которые можно использовать, все они навязчивы и ожидают, что вы знаете, что делаете. Если ваш OptionaLinkSchema
зависит от каких-либо особенностей ModelSchemaMeta
, он просто сломается.
Я могу придумать 3 способа достижения эквивалента «удаления наследуемого метакласса»
Первый подход
Вы ничего не говорите по своему вопросу, но я не вижу ModelSchemaMeta
в исходном дереве Зефира - это метакласс, который вы создали? Если так, то, поскольку Marshmallow использует идиому «djangoish» наличия вложенного класса Meta
для объявления вещей о самом классе, вы можете использовать атрибут в этом пространстве имен Meta
, чтобы пропустить свой собственный метакласс.
Если это так, добавьте код, подобный этому, в ваш метод нежелательного метакласса '__new__
(я выбираю код синтаксического анализа "meta" из самого исходного кода Marshmallow):
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
...
Второй подход
Чрезвычайно более инвазивный способ сделать это, но это будет работать для любого дерева наследования классов, имеет код, который возьмет суперкласс с нежелательным метаклассом и создаст его клон. Тем не менее, поскольку имеется собственный метакласс, отличный от 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)):
...
(Любой, кто пытается использовать это, не то, что если 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
Отличие от первого подхода состоит в том, что ModelSchemaMeta
будет деревом наследования для метакласса, а конечный метакласс будет этим новым, производным метаклассом. Но это не будет зависеть от того, что вы контролируете код ModelSchemaMeta
- и, как я уже говорил, это больше "в рамках ожидаемых правил" языка, чем второй подход.