为 class 创建装饰器以应用数据 class 和 JsonSchemaMixin ...不工作

Create decorator for class to apply dataclass and JsonSchemaMixin ...not working

我确实审查了所有现有的 SO 问题,尽我所能搜索,尝试了几个不同的选项,但似乎无法得到我想要的工作。

背景

简单问题 - 我的代码中有一堆数据class。

我想将它们全部更改为从 dataclasses-jsonschema JsonSchemaMixin 继承,以便我可以通过编程方式为它们全部生成架构。

当然,简单的方法是我可以更改代码中所有实例以从 JsonSchemaMixin mixin 继承。

但我在想我也可以编写自己的装饰器来 a) 应用@dataclass,以及 b) 一次性混合 dataclasses-schema。 ...我的想法是错误的可能性较小,我可以尝试编写一个 class 装饰器(我只创建并使用过我自己的函数装饰器)

# OLD

@dataclass
class X:
    ....

# Add the mixin `by hand` everywhere

@dataclass
class X(JsonSchemaMixin):
    x: int
    y: float

# What I want is to make my own decorator to do both:

@dataclass_mixedin
class X:
    x: int
    y: float

问题

非常简单的问题 - 我无法让它工作:(

我试过的

尝试编号 1:

from dataclasses import dataclass
from dataclasses_jsonschema import JsonSchemaMixin

def dc_schema(cls):
    @dataclass
    class _decorated(JsonSchemaMixin, cls):
        pass
    return _decorated

@dc_schema
class Data:
    x: int
    y: float
        
a = Data(x=5,y=1.1)

以上失败,带有意外的关键字参数 x 和 y。

尝试数字 2:

def dc_schema2(cls):
    # Try applying dataclass() directly:
    class _decorated(JsonSchemaMixin, cls):
        pass
    _decorated = dataclass(_decorated)
    return _decorated

@dc_schema2
class Data2:
    x: int
    y: float

d = Data2(x=1, y=1.1)

再次 - 由于意外参数失败。

所以我猜我错过了 dataclass 遍历 class 结构来查找 class 带有注释的变量 (https://docs.python.org/3/library/dataclasses.html)。

但我不知道该怎么做

一个可能的解决方案是使用元class。 看这个例子:

from dataclasses import dataclass, is_dataclass
from dataclasses_jsonschema import JsonSchemaMixin


class EntityMeta(type):
    def __new__(cls, name, bases, class_dict):
        new_class = super().__new__(cls, name, bases, class_dict)
        return dataclass()(new_class)


class BaseEntity(JsonSchemaMixin, metaclass=EntityMeta):
    pass


class SomeClass(BaseEntity):
    id: str
    name: str


assert is_dataclass(SomeClass)
assert issubclass(SomeClass, JsonSchemaMixin)

x = SomeClass(1, "Hello World")

assert x.id == 1
assert x.name == "Hello World"

class SomeOtherClass(BaseEntity):
    foo: str
    name: str


x = SomeOtherClass("foo", "ufo")
assert x.foo == "foo"
assert x.name == "ufo"

print(SomeOtherClass.json_schema())

我不推荐这个,因为你 无论如何都需要从基础 class 继承,并且代码冗长程度差异很小。你失去了有用的功能,比如类型 在 MyPy(除非编写专用插件)或 Pylance 的帮助下进行编码时的提示,在 IDE 中提供此类帮助。

我建议考虑 pydantic:它可能有您想要的功能。