15
@dataclass
class Car:
    brand: str
    color: str

How can I get a dict that ignore None values? Something like:

>>> car = Car(brand="Audi", color=None)
>>> asdict(car, some_option_to_ignore_none_values=True)
> {'brand': 'Audi'}
martineau
  • 119,623
  • 25
  • 170
  • 301
David Dahan
  • 10,576
  • 11
  • 64
  • 137
  • 1
    Possible duplicate of [How I can get rid of None values in dictionary?](https://stackoverflow.com/questions/2544710/how-i-can-get-rid-of-none-values-in-dictionary) – Arne Jul 06 '19 at 12:07

6 Answers6

23

All answers are good but to me they are too verbose. Here's a one-liner:

# 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})

Show case:

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)}
Dawid Laszuk
  • 1,773
  • 21
  • 39
4

Old topic but I hope this can help: The best way I've found so far to do this is to use the metadata/config exclude option:

from dataclasses import dataclass, field
from typing import Optional, Union
from dataclasses_json import LetterCase, dataclass_json, config
import pprint


def ExcludeIfNone(value):
    """Do not include field for None values"""
    return value is None

@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Op:
    required: str
    optional: Optional[int] = field(metadata=config(exclude=ExcludeIfNone), default=None)


pp = pprint.PrettyPrinter(indent=4)
pp.pprint(clearsale.Op(required="test").to_dict())
pp.pprint(clearsale.Op(required="test", optional=10).to_dict())

Output:
{'required': 'test'}
{'optional': 10, 'required': 'test'}
skylazart
  • 69
  • 4
3

Another option is to write a dict_factory that will reject adding None values and pass it to asdict method. checkout the code source here https://github.com/python/cpython/blob/master/Lib/dataclasses.py

Ramtin M. Seraj
  • 686
  • 7
  • 17
1

I needed something recursive so I borrowed from the dataclasses source code that Ramtin pointed to and added a couple of conditionals:

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)
billkw
  • 3,350
  • 3
  • 28
  • 32
-1

Using simple class

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]

Edit 1:

from dataclasses import dataclass, asdict

@dataclass
class Human:
    name: str
    age: int = None
    height: float = None
    gender: str = None
    programmer: bool = False

jason = Human(name="jason", programmer=True)

jason_details = asdict(jason)
print(jason_details)

By using the asdict() function from the dataclasses module, you can directly convert the instance of the data class to a dictionary, excluding any fields with None values.

Subham
  • 397
  • 1
  • 6
  • 14
-2
class IgnoreNoneValues(dict):
    def __setitem__(self, k, v):
        if v is not None:
            super().__setitem__(k, v)

asdict(<some dataclass object>, dict_factory=IgnoreNoneValues)
Vasniktel
  • 307
  • 1
  • 4
  • 12