Summary
I have a dataclass
with 10+ fields. print()
ing them buries interesting context in a wall of defaults - let's make them friendlier by not needlessly repeating those.
Dataclasses in Python
Python's @dataclasses.dataclass()
(PEP 557) provides automatic printable representations (__repr__()
).
Assume this example, based on python.org's:
from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float = 1.00
quantity_on_hand: int = 0
The decorator, through @dataclass(repr=True)
(default) will print()
a nice output:
InventoryItem(name='Apple', unit_price='1.00', quantity_on_hand=0)
What I want: Skip printing the defaults
repr
It prints all the fields, including implied defaults you wouldn't want to show.
print(InventoryItem("Apple"))
# Outputs: InventoryItem(name='Apple', unit_price='1.00', quantity_on_hand=0)
# I want: InventoryItem(name='Apple')
print(InventoryItem("Apple", unit_price="1.05"))
# Outputs: InventoryItem(name='Apple', unit_price='1.05', quantity_on_hand=0)
# I want: InventoryItem(name='Apple', unit_price='1.05')
print(InventoryItem("Apple", quantity_on_hand=3))
# Outputs: InventoryItem(name='Apple', unit_price=1.00, quantity_on_hand=3)
# I want: InventoryItem(name='Apple', quantity_on_hand=3)
print(InventoryItem("Apple", unit_price='2.10', quantity_on_hand=3))
# Output is fine (everything's custom):
# InventoryItem(name='Apple', unit_price=2.10, quantity_on_hand=3)
Discussion
Internally, here's the machinery of dataclass
repr
-generator as of python 3.10.4
: cls.__repr__
=
_repr_fn(flds, globals))
-> _recursive_repr(fn)
It may be the case that @dataclass(repr=False)
be switched off and def __repr__(self):
be added.
If so, what would that look like? We don't want to include the optional defaults.
Context
To repeat, in practice, my dataclass
has 10+ fields.
I'm print()
ing instances via running the code and repl, and @pytest.mark.parametrize
when running pytest with -vvv
.
Big dataclass' non-defaults (sometimes the inputs) are impossible to see as they're buried in the default fields and worse, each one is disproportionately and distractingly huge: obscuring other valuable stuff bring printed.
Related questions
As of today there aren't many dataclass
questions yet (this may change):
You could do it like this:
import dataclasses
from dataclasses import dataclass
from operator import attrgetter
@dataclass(repr=False)
class InventoryItem:
name: str
unit_price: float = 1.00
quantity_on_hand: int = 0
def __repr__(self):
nodef_f_vals = (
(f.name, attrgetter(f.name)(self))
for f in dataclasses.fields(self)
if attrgetter(f.name)(self) != f.default
)
nodef_f_repr = ", ".join(f"{name}={value}" for name, value in nodef_f_vals)
return f"{self.__class__.__name__}({nodef_f_repr})"
# Prints: InventoryItem(name=Apple)
print(InventoryItem("Apple"))
# Prints: InventoryItem(name=Apple,unit_price=1.05)
print(InventoryItem("Apple", unit_price="1.05"))
# Prints: InventoryItem(name=Apple,unit_price=2.10,quantity_on_hand=3)
print(InventoryItem("Apple", unit_price='2.10', quantity_on_hand=3))