Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Exclude default fields from python `dataclass` `__repr__`

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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):

>Solution :

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))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading