簡體   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