【问题标题】:dataclasses: how to ignore None values using asdict()?数据类:如何使用 asdict() 忽略 None 值?
【发布时间】:2019-11-12 07:24:21
【问题描述】:
@dataclass
class Car:
    brand: str
    color: str

如何获得忽略 None 值的字典?比如:

>>> car = Car(brand="Audi", color=None)
>>> asdict(car, some_option_to_ignore_none_values=True)
> {'brand': 'Audi'}

【问题讨论】:

标签: python python-dataclasses


【解决方案1】:

所有答案都很好,但对我来说它们太冗长了。这是一个单行:

# dc is dataclass
# d is dict out
d = asdict(dc, dict_factory=lambda x: {k: v for (k, v) in x if v is not None})

展示案例:

from typing import Optional, Tuple
from dataclasses import asdict, dataclass

@dataclass
class Space:
    size: Optional[int] = None
    dtype: Optional[str] = None
    shape: Optional[Tuple[int]] = None

s1 = Space(size=2)
s1_dict = asdict(s1, dict_factory=lambda x: {k: v for (k, v) in x if v is not None})
print(s1_dict)
# {"size": 2}

s2 = Space(dtype='int', shape=(2, 5))
s2_dict = asdict(s2, dict_factory=lambda x: {k: v for (k, v) in x if v is not None})
print(s2_dict)
# {"dtype": "int", "shape": (2, 5)}

【讨论】:

    【解决方案2】:

    另一种选择是编写一个 dict_factory 拒绝添加 None 值并将其传递给asdict 方法。 在这里查看代码源https://github.com/python/cpython/blob/master/Lib/dataclasses.py

    【讨论】:

      【解决方案3】:

      我需要一些递归的东西,所以我从 Ramtin 指出的数据类源代码中借用并添加了几个条件:

      from copy import deepcopy
      from dataclasses import fields
      
      def dict_minus_none_values(obj, dict_factory=dict):
          """Based on dataclasses._asdict_inner"""
          if hasattr(type(obj), "__dataclass_fields__"):
              result = []
              for field in fields(obj):
                  value = dict_minus_none_values(getattr(obj, field.name), dict_factory)
                  if value is not None:
                      result.append((field.name, value))
              return dict_factory(result)
          if isinstance(obj, tuple) and hasattr(obj, "_fields"):
              return type(obj)(*[dict_minus_none_values(v, dict_factory) for v in obj])
          if isinstance(obj, (list, tuple)):
              return type(obj)(dict_minus_none_values(v, dict_factory) for v in obj)
          if isinstance(obj, dict):
              return type(obj)(
                  (
                      dict_minus_none_values(k, dict_factory),
                      dict_minus_none_values(v, dict_factory),
                  )
                  for k, v in obj.items()
                  if v is not None
              )
          return deepcopy(obj)
      

      【讨论】:

        【解决方案4】:
        class IgnoreNoneValues(dict):
            def __setitem__(self, k, v):
                if v is not None:
                    super().__setitem__(k, v)
        
        asdict(<some dataclass object>, dict_factory=IgnoreNoneValues)
        

        【讨论】:

        • dict.__init__ 不调用dict.__setitem__。此代码不起作用。
        【解决方案5】:

        使用简单的类

        class human:
          def __init__(self, choice = False, **kwargs):
            self.details = [kwargs if choice is False else self._filterr(kwargs)][0]
        
          def _filterr(self, param):
            filtered = {k:v for k,v in param.items() if v is not None}
            return filtered
        
        jason = human(choice = True ,name = "jason", age = None, height = None, gender = None, programmer = True)
        
        print(jason.details)
        
        {'name': 'jason', 'programmer': True}
        
        [Program finished]
        

        【讨论】:

          【解决方案6】:

          给你:

          from dataclasses import dataclass
          
          
          @dataclass
          class Car:
              brand: str
              color: str
          
          
          def asdict(o, skip_empty=False):
              return {k: v
                      for k, v in o.__dict__.items()
                      if not (skip_empty and v is None)}
          
          
          if __name__ == '__main__':
              c = Car(brand='BMW', color=None)
              print(asdict(c, skip_empty=False))
              print(asdict(c, skip_empty=True))
          
          

          哪个打印:

          {'brand': 'BMW', 'color': None}
          {'brand': 'BMW'}
          

          【讨论】:

          • 谢谢!所以你建议写我自己的方法。是不是因为没有办法使用内置的asdict 来做到这一点?在这种情况下,我会建议使用其他名称以避免混淆,您怎么看?
          • asdict 使用相同的(几乎)逻辑 (source),因此您可以使用自己的 asdict 包装 as_dict 并根据需要丢弃空值。
          猜你喜欢
          • 2022-11-03
          • 1970-01-01
          • 1970-01-01
          • 2020-08-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-27
          • 1970-01-01
          相关资源
          最近更新 更多