我有一个简单类的(动态)定义,如下所示:
class Simple:
val: int = 1
我打算用这个定义来构建一个pydantic.BaseModel
,所以它可以从Simple
类定义;基本上是这样做的,但通过type
,在元类结构下,Simple
类是从。
from pydantic import BaseModel
class SimpleModel(Simple, BaseModel):
pass
# Actual ways tried:
SimpleModel = type('SimpleModel', (Simple, BaseModel), {})
# or
SimpleModel = type('SimpleModel', (BaseModel, ), Simple.__annotations__)
然而,这种方法并没有返回一个带有Simple
类参数的模型类。
我知道BaseModel
已经在底层使用了一个相当复杂的元类,然而,我的预期实现也是在元类下,我打算动态地将Simple
类从pydantic转移到BaseModel
。
请多多指教。
我首先设法得到这个工作,将我的Simple
类转换为pydantic的数据类,然后从中获得pydantic模型。
我不是pydantic方面的专家,所以我不介意你对这个方法的看法。
from pydantic.dataclasses import dataclass
SimpleModel = dataclass(Simple).__pydantic_model__
然而,我发现的麻烦(与@jsbueno提供的答案相同),当直接使用BaseModel
声明pathlib.Path
(作为示例)的数据类型注释时,所提供的字符串值被强制转换为注释数据类型。但是使用我的或@jsbueno方法,数据类型保持原始(没有强制转换)。
您可以简单地调用type
,传递由SimpleModel
的__dict__
属性组成的字典-该字典将包含您的文件的默认值和__annotations__
属性,这些信息足以让Pydantic完成它的事情。
我只是会采取额外的步骤删除__weakref__
属性,该属性是在普通的"SimpleModel"中默认创建的。在此之前-避免它指向错误的类。
from pydantic import BaseModel
class Simple:
val: int = 1
new_namespace = dict(Simple.__dict__) # copies the class dictproxy into a plain dictionary
del new_namespace["__weakref__"]
SimpleModel = type("SimpleModel", (BaseModel,), new_namespace)
和
In [58]: SimpleModel.schema()
Out[58]:
{'title': 'Simple',
'type': 'object',
'properties': {'one_val': {'title': 'One Val',
'default': 1,
'type': 'integer'}}}
可以工作-但是由于Pydantic很复杂,为了使其更适合未来,最好使用Pydantic的元类提供的命名空间对象而不是普通的字典-正式的方法是使用types
模型中的辅助函数:
import types
from pydantic import BaseModel
class Simple:
val: int = 1
SimpleModel = types.new_class(
"SimpleModel",
(BaseModel,),
exec_body=lambda ns:ns.update(
{key: val for key, val in Simple.__dict__.items()
if not key.startswith("_")}
)
)
new_type
调用计算适当的元类,并将正确的命名空间对象传递给exec_body
参数中的回调。在这里,我们只需用动态类中字典的内容填充它。
这里,我选择更新名称空间并过滤所有的"_"值在一行中,但是您可以定义函数传递给"exec_body"作为一个完整的多行功能,更仔细地过滤您想要的内容。