![](/img/trans.png)
[英]Structure JSON to an `attrs` class with extra fields using `cattrs`?
[英]cattrs.structure with nested dictionaries and attrs.define( converter=foo )
我在将包含无效数据的字典结构化为@attrs.define
装饰类时遇到问题,但应该使用attrs.define
转换器处理。
我已经设法让它与Foo( **dct )
一起工作,它确实为每个属性调用转换器,但是当使用cattrs.structure( dct, Foo )
或cattrs.Converter().structure( dct, Foo)
调用时它失败。
在我看来,没有调用 class 属性转换器。 那是对的吗? 这是cattrs
中的错误还是我使用不正确?
#!/usr/bin/env python3
from __future__ import annotations
from typing import Optional, Any
import attrs
import cattrs
def converter_float_or_none( val : Any ) -> Optional[float] :
new_val = None
try :
new_val = float( val )
except Exception :
pass
return new_val
@attrs.define
class Foo:
xxx : Optional[float] = attrs.field( default=None, converter=converter_float_or_none )
@attrs.define
class Bar:
foo : Foo = attrs.field( factory=Foo, converter=Foo )
if __name__ == '__main__' :
converter = cattrs.Converter()
#! --- test with valid fields ---
dct_valid = { 'xxx' : None }
dct_invalid = { 'xxx' : '--' }
#! ok
foo = Foo( **dct_valid )
print( f"foo = {foo!r}" )
assert None == foo.xxx
#! ok
foo = Foo( **dct_invalid )
print( f"foo = {foo!r}" )
assert None == foo.xxx
#! ok !!
foo = converter.structure( dct_valid, Foo )
print( f"foo = {foo!r}" )
assert None == foo.xxx
#! FAIL !!
foo = converter.structure( dct_invalid, Foo )
print( f"foo = {foo!r}" )
assert None == foo.xxx
Output 是:
(venv_3) $ python3 xxx_test.py
foo = Foo(xxx=None)
foo = Foo(xxx=None)
foo = Foo(xxx=None)
+ Exception Group Traceback (most recent call last):
| File "xxx_test.py", line 54, in <module>
| foo = converter.structure( dct_invalid, Foo )
| File "/home/brendansimon/venv_3/lib/python3.7/site-packages/cattrs/converters.py", line 309, in structure
| return self._structure_func.dispatch(cl)(obj, cl)
| File "<cattrs generated structure __main__.Foo>", line 10, in structure_Foo
| if errors: raise __c_cve('While structuring ' + 'Foo', errors, __cl)
| cattrs.errors.ClassValidationError: While structuring Foo (1 sub-exception)
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "<cattrs generated structure __main__.Foo>", line 6, in structure_Foo
| res['xxx'] = __c_structure_xxx(o['xxx'], __c_type_xxx)
| File "/home/brendansimon/venv_3/lib/python3.7/site-packages/cattrs/converters.py", line 574, in _structure_optional
| return self._structure_func.dispatch(other)(obj, other)
| File "/home/brendansimon/venv_3/lib/python3.7/site-packages/cattrs/converters.py", line 415, in _structure_call
| return cl(obj)
| ValueError: could not convert string to float: '--'
| Structuring class Foo @ attribute xxx
+------------------------------------
我是猫的作者。 让我解释一下这是怎么回事。
attrs转换器总是被调用,实际上没有办法阻止这种情况。 这里的问题是cattrs尝试在attrs之前处理该字段,但失败了,因为cattrs不知道您的自定义转换器。
这是因为,在大多数其他情况下,如果存在attrs转换器,则cattrs跳过其自身的逻辑会更令人惊讶。
但是,有一种方法可以启用此功能:
from cattrs.gen import make_dict_structure_fn
converter.register_structure_hook(
Foo,
make_dict_structure_fn(Foo, converter, _cattrs_prefer_attrib_converters=True),
)
这个片段是converter
自己做的,我们只是手动做,所以我们可以自定义。 (由于make_dict_structure_fn
的其他用法,参数名称很奇怪。)
看起来我们忘了记录这个功能,我会为下一个版本做一个记录。
这是整个工作片段(我用黑色重新格式化):
from __future__ import annotations
from typing import Any, Optional
import attrs
import cattrs
def converter_float_or_none(val: Any) -> Optional[float]:
new_val = None
try:
new_val = float(val)
except Exception:
pass
return new_val
@attrs.define
class Foo:
xxx: Optional[float] = attrs.field(default=None, converter=converter_float_or_none)
@attrs.define
class Bar:
foo: Foo = attrs.field(factory=Foo, converter=Foo)
if __name__ == "__main__":
converter = cattrs.Converter()
dct_valid = {"xxx": None}
dct_invalid = {"xxx": "--"}
foo = Foo(**dct_valid)
print(f"foo = {foo!r}")
assert None is foo.xxx
foo = Foo(**dct_invalid)
print(f"foo = {foo!r}")
assert None is foo.xxx
foo = converter.structure(dct_valid, Foo)
print(f"foo = {foo!r}")
assert None is foo.xxx
from cattrs.gen import make_dict_structure_fn
converter.register_structure_hook(
Foo,
make_dict_structure_fn(Foo, converter, _cattrs_prefer_attrib_converters=True),
)
foo = converter.structure(dct_invalid, Foo)
print(f"foo = {foo!r}")
assert None is foo.xxx
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.