如何删除数据类属性

Gus*_*oIP 5 python python-3.x python-dataclasses

我有一个这样的数据类:

@dataclass
class Bla:
    arg1: Optional[int] = None
    arg2: Optional[str] = None
    arg3: Optional[Dict[str, str]] = None
Run Code Online (Sandbox Code Playgroud)

我想要这种行为:

>>> bla = Bla(arg1=None, arg2=None, arg3=None)
>>> asdict(bla)
{'arg1': None, 'arg2': None, 'arg3': None}

>>> bla = Bla()
{}
Run Code Online (Sandbox Code Playgroud)

在这种特定情况下,我可以使用 a dict,但我会失去类型提示的可能性(并使用 mypy)

所以我试过这个:

class none:
    ...

@dataclass
class Bla:
    arg1: Union[none, int] = none()
    arg2: Union[none, str] = none()
    arg3: Union[none, Dict[str, str]] = none()

    def __post_init__(self) -> None:
        for k, v in self.__dict__.copy().items():
            if isinstance(v, none):
                delattr(self, k)
Run Code Online (Sandbox Code Playgroud)

但结果是:

>>> asdict(Bla())
{'arg1': <__main__.none object at 0x7f71bc0159b0>, 'arg2': <__main__.none object at 0x7f71bc015a90>, 'arg3': <__main__.none object at 0x7f71bc015ac8>}
Run Code Online (Sandbox Code Playgroud)

我期待一个空的 dict

如果尝试:

>>> a = Bla(None, None, None)
>>> del a.__dict__["arg1"]
>>> asdict(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/(....)/venv/lib/python3.6/site-packages/dataclasses.py", line 1011, in asdict
    return _asdict_inner(obj, dict_factory)
  File "/home/(...)/venv/lib/python3.6/site-packages/dataclasses.py", line 1018, in _asdict_inner
    value = _asdict_inner(getattr(obj, f.name), dict_factory)
AttributeError: 'Bla' object has no attribute 'arg1'
Run Code Online (Sandbox Code Playgroud)

如何以asdict之后可以使用的方式从数据类对象中动态删除属性?

Arn*_*rne 12

如果您计划使用asdict,重要的部分不是从实例中删除属性(这是egvars将检查的位置),而是从实例的__dataclass_fields__.

from dataclasses import dataclass
from typing import Optional, Dict, cast

SENTINEL = cast(None, object())  # have a sentinel that pretends to be 'None'


@dataclass
class Bla:
    arg1: Optional[int] = SENTINEL
    arg2: Optional[str] = SENTINEL
    arg3: Optional[Dict[str, str]] = SENTINEL

    def __post_init__(self):
        self.__dataclass_fields__ = {
            k: v for k, v in self.__dataclass_fields__.items()
            if getattr(self, k) is not SENTINEL
        }
Run Code Online (Sandbox Code Playgroud)

一些测试:

>>> from dataclasses import asdict
>>> asdict(Bla())
{}
>>> asdict(Bla(None, None))
{'arg1': None, 'arg2': None}
>>> asdict(Bla(1, 'foo', None))
{'arg1': 1, 'arg2': 'foo', 'arg3': None}
Run Code Online (Sandbox Code Playgroud)

使用稍微不同的__post_init__,您还可以从实例本身删除属性的值。这将节省一点 RAM 并从 中删除属性vars,但getattr仍然会找到类变量,这可能最终会让事情变得非常混乱:

[...]
    def __post_init__(self):
        for field_name in [*self.__dataclass_fields__]:
            if getattr(self, field_name) is SENTINEL:
                del self.__dataclass_fields__[field_name]
                delattr(self, field_name)
[...]
>>> bla = Bla()
>>> asdict(bla)
{}
>>> vars(bla)
{}
>>> bla.arg1
<object object at 0x7f2c248a49e0>
Run Code Online (Sandbox Code Playgroud)