【问题标题】:Unstructuring a complex class with cattrs用 cattrs 解构一个复杂的类
【发布时间】:2019-08-03 07:03:10
【问题描述】:

我在使用 attrs/cattrs 将自定义数据类转换为 JSON 格式时遇到问题。我的班级有以下形式:

import attr
from datetime import datetime
from typing import Tuple, Set, Dict, FrozenSet

@attr.s(auto_attribs=True)
class B:
    w: Set[datetime] = attr.ib()
    x: Set[str] = attr.ib()
    y: Set['A'] = attr.ib()
    z: Set[Tuple[datetime, str]] = attr.ib(factory=set)


@attr.s(auto_attribs=True, cmp=False)
class A:
    a: str = attr.ib()
    b: FrozenSet[Team] = attr.ib()
    c: FrozenSet[Tuple[datetime, str]] = attr.ib(factory=frozenset)
    d: Dict[Tuple[str, str], float] = attr.ib(factory=dict)

我面临的问题是,当我尝试通过cattrs.unstructure 转换为dict 并通过cattrs.structure 转换为dict 时,cattrs 显示一个错误,告诉我应该使用挂钩:

import cattr

# Create instance of b
b_instance = B(...)
...
data = cattr.unstructure(b_instance)
print()
print(data)
print()
restored = cattr.structure(data, B)
assert b_instance == restored
ValueError: Unsupported type: <class 'datetime.datetime'>. Register a structure hook for it.

我注册了一个钩子,将datetime 转换为str,并使用给定的格式:

import cattr
from datetime import datetime

time_format = '%Y-%m-%d %H:%M:%S'
cattr.register_unstructure_hook(datetime, lambda dt: dt.strftime(time_format))
cattr.register_structure_hook(datetime, lambda s, _: datetime.strptime(s, time_format))

然后我收到以下错误:

TypeError: strptime() argument 1 must be str, not datetime.datetime

我需要能够使用 %Y-%m-%d %H:%M:%S 格式的日期时间从对象实例转换为 JSON 并返回。我怎样才能做到这一点?

【问题讨论】:

    标签: python json datetime python-attrs


    【解决方案1】:

    问题在于 Set 类型。如果它存在于类型中,那么cattr.unstructure 无法序列化它。用元组替换它,事情就会解决:

    from typing import Tuple, Set
    from datetime import datetime
    
    import cattr
    import attr
    import pytest
    
    
    TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
    COMPLEX_TYPE = Tuple[Tuple[str, datetime]]
    COMPLEX_TYPE_BAD = Set[Tuple[str, datetime]]
    
    cattr.register_unstructure_hook(
        datetime, lambda dt: datetime.strftime(dt, format=TIME_FORMAT)
    )
    cattr.register_structure_hook(
        datetime, lambda dt_str, _: datetime.strptime(dt_str, TIME_FORMAT)
    )
    
    
    def test_cattrs_io_for_a():
        @attr.s(auto_attribs=True)
        class A:
            ds: datetime = attr.ib(factory=datetime.utcnow)
    
        a = A()
        a_ser = cattr.unstructure(a)
        a_des = cattr.structure(a_ser, A)
        assert isinstance(a_des, A)
    
    
    def test_cattrs_io_for_b():
        @attr.s(auto_attribs=True)
        class B:
            ds: COMPLEX_TYPE = attr.ib(factory=set)
    
        b = B((("a", datetime.utcnow()), ("b", datetime.utcnow())))
        b_ser = cattr.unstructure(b)
        b_des = cattr.structure(b_ser, B)
        assert isinstance(b_des, B)
    
    
    def test_cattrs_io_for_b_bad():
        @attr.s(auto_attribs=True)
        class B:
            ds: COMPLEX_TYPE_BAD = attr.ib(factory=set)
    
        b = B({("a", datetime.utcnow()), ("b", datetime.utcnow())})
        b_ser = cattr.unstructure(b)
        with pytest.raises(TypeError):
            b_des = cattr.structure(b_ser, B)
            assert isinstance(b_des, B)
    

    看起来像是 cattrs https://github.com/Tinche/cattrs/issues/48 中的一个问题

    将在 cattrs 的下一个版本中修复。或者你可以从master 分支构建它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-11
      • 1970-01-01
      • 2021-12-15
      • 2019-07-31
      • 1970-01-01
      相关资源
      最近更新 更多