繁体   English   中英

带有嵌套字典和 attrs.define( converter=foo ) 的 cattrs.structure

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM