Search code examples
pythonpython-dataclasses

How to define `__str__` for `dataclass` that omits default values?


Given a dataclass instance, I would like print() or str() to only list the non-default field values. This is useful when the dataclass has many fields and only a few are changed.

@dataclasses.dataclass
class X:
  a: int = 1
  b: bool = False
  c: float = 2.0

x = X(b=True)
print(x)  # Desired output: X(b=True)

Solution

  • The solution is to add a custom __str__() function:

    @dataclasses.dataclass
    class X:
      a: int = 1
      b: bool = False
      c: float = 2.0
    
      def __str__(self):
        """Returns a string containing only the non-default field values."""
        s = ', '.join(f'{field.name}={getattr(self, field.name)!r}'
                      for field in dataclasses.fields(self)
                      if getattr(self, field.name) != field.default)
        return f'{type(self).__name__}({s})'
    
    x = X(b=True)
    print(x)        # X(b=True)
    print(str(x))   # X(b=True)
    print(repr(x))  # X(a=1, b=True, c=2.0)
    print(f'{x}, {x!s}, {x!r}')  # X(b=True), X(b=True), X(a=1, b=True, c=2.0)
    

    This can also be achieved using a decorator:

    def terse_str(cls):  # Decorator for class.
      def __str__(self):
        """Returns a string containing only the non-default field values."""
        s = ', '.join(f'{field.name}={getattr(self, field.name)}'
                      for field in dataclasses.fields(self)
                      if getattr(self, field.name) != field.default)
        return f'{type(self).__name__}({s})'
    
      setattr(cls, '__str__', __str__)
      return cls
    
    @dataclasses.dataclass
    @terse_str
    class X:
      a: int = 1
      b: bool = False
      c: float = 2.0