简体   繁体   中英

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM