![](/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.