棉花糖模式和 Class 继承

Marshmallow Schema and Class Inheritance

我是第一次使用 Marshmallow,很遗憾在互联网上找不到答案。

有两个类,一个继承另一个。 两者都应该是可序列化和可反序列化的。 反序列化后,它们应该作为 Python 对象再次可用。 因此我使用 post_load 装饰器。 但似乎这会导致问题。 在下面的最小工作示例中,我得到以下异常:type object argument after ** must be a mapping, not Bicycle.

现在可以查找属性 amount_of_tires 并在必要时传递数据。 但这似乎不是正确的解决方案。

是否有解决此问题的最佳实践?

from marshmallow import Schema, fields
from marshmallow.decorators import post_load


class Vehicle:
    def __init__(self, weight):
        self.weight = weight


class VehicleSchema(Schema):
    weight = fields.Float()

    @post_load
    def make_vehicle(self, data, **kwargs) -> Vehicle:
        return Vehicle(**data)


class Bicycle(Vehicle):
    def __init__(self, weight, amount_of_tires):
        Vehicle.__init__(self, weight=weight)
        self.amount_of_tires = amount_of_tires


class BicycleSchema(VehicleSchema):
    amount_of_tires = fields.Integer()

    @post_load
    def make_bicycle(self, data, **kwargs) -> Bicycle:
        return Bicycle(**data)


my_bicycle = Bicycle(weight=10, amount_of_tires=2)


schema = BicycleSchema()
json_string = schema.dumps(my_bicycle)

deserialised_my_bicycle = schema.loads(json_string)
print(deserialised_my_bicycle)


make_vehiclemake_bicycle 都注册为 post_load 钩子,因此它们被一个接一个地调用。当 make_vehicle 被调用时,make_bicycle 已经被调用,所以 type(data) == Bicycle,因此你看到的错误。

我推荐以下解决方案:

class VehicleSchema(Schema):
    model_class = Vehicle

    weight = fields.Float()

    @post_load
    def make_vehicle(self, data, **kwargs) -> Vehicle:
        return type(self).model_class(**data)

class BicycleSchema(VehicleSchema):
    model_class = Bicycle
    amount_of_tires = fields.Integer()

要使用此解决方案获得更准确的类型提示 - 可以在基础 VehicleSchema class 上使用 GenericTypeVar(请参阅 https://docs.python.org/3/library/typing.html#typing.Generic) .