如果使用"类型"模块,则"pyyaml"无法解析"pydantic"对象



让我首先说我想在pydantic回购中打开一个问题。一旦我开始调试橡皮鸭,我得出的结论是,它实际上是pyyaml不能正常工作,但我不确定了。

from dataclasses import dataclass
from functools import partial
from typing import List, Type
import yaml
from pydantic import BaseModel
yaml_input = """
!Foo
name: foo
bar:
- !Bar
name: bar
"""

def get_loader():
loader = yaml.SafeLoader
for tag_name, tag_constructor in tag_model_map.items():
loader.add_constructor(tag_name, tag_constructor)
return loader

def dynamic_constructor_mapping(model_class: Type[BaseModel], loader: yaml.SafeLoader,
node: yaml.nodes.MappingNode) -> BaseModel:
return model_class(**loader.construct_mapping(node))

def get_constructor_for_mapping(model_class: Type[BaseModel]):
return partial(dynamic_constructor_mapping, model_class)

class Bar(BaseModel):
name: str

class Foo1(BaseModel):
name: str
bar: list

class Foo2(BaseModel):
name: str
bar: List

class Foo3(BaseModel):
name: str
bar: List[Bar]

@dataclass
class Foo4:
name: str
bar: List[Bar]

foos = [Foo1, Foo2, Foo3, Foo4]
for foo_cls in foos:
tag_model_map = {
"!Foo": get_constructor_for_mapping(foo_cls),
"!Bar": get_constructor_for_mapping(Bar),
}
print(f"{foo_cls.__qualname__} loaded {yaml.load(yaml_input, Loader=get_loader())}")

打印

Foo1 loaded name='foo' bar=[Bar(name='bar')]
Foo2 loaded name='foo' bar=[]
Foo3 loaded name='foo' bar=[]
Foo4 loaded Foo4(name='foo', bar=[Bar(name='bar')])

  • 如果在静态类型中使用list,则pydantic对象列表解析正确
  • pydantic对象列表NOT如果在静态类型
  • 中使用List,则正确解析
  • pydantic对象列表NOT如果在静态类型
  • 中使用List[Bar],则正确解析
  • dataclass对象列表总是被正确解析

构造函数似乎在所有示例中返回正确的对象,所以我不明白问题出在哪里。

pydantic==1.8.2
Python 3.8.10 

我和你有同样的问题。

为我解决这个问题的是在construct_mapping方法中将deep设置为true。

的例子:

fields = loader.construct_mapping(node, deep=True)

所以这只是我在YAML中注意到的一个问题,但在我看来,将YAML反序列化为数据类的代码总体上比需要的更复杂。

如果您不需要pydantic提供的数据验证功能,您还可以查看dataclass-wizard,它提供了一个辅助YAMLWizardMixin类,可用于处理YAML数据——注意,这也依赖于pyyaml库。

下面是一个简单的例子:

from __future__ import annotations
from dataclasses import dataclass
from dataclass_wizard import YAMLWizard

yaml_input = """
name: foo
bar:
- name: bar
"""

@dataclass
class Foo(YAMLWizard):
name: str
bar: list[Bar]

@dataclass
class Bar:
name: str

instance = Foo.from_yaml(yaml_input)
print(f'Loaded: {instance}')

安装dataclass-wizardpyyaml,你可以包括yaml额外:

pip install dataclass-wizard[yaml]