从 python `dataclass` `__repr__` 中排除默认字段

Exclude default fields from python `dataclass` `__repr__`

总结

我有一个 dataclass10+ 个字段print()使用它们会将有趣的背景隐藏在一堵默认值墙中 - 让我们不要不必要地重复这些内容,让它们变得更友好。

Python

中的数据类

Python的@dataclasses.dataclass() (PEP 557) provides automatic printable representations (__repr__()).

假设这个例子,based on python.org's:

from dataclasses import dataclass


@dataclass
class InventoryItem:
    name: str
    unit_price: float = 1.00
    quantity_on_hand: int = 0

装饰器,通过@dataclass(repr=True) (default) will print()一个不错的输出:

InventoryItem(name='Apple', unit_price='1.00', quantity_on_hand=0)

我想要的:跳过打印默认值

repr 它打印 所有 字段,包括您不想显示的隐含默认值。

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)

讨论

在内部,这是 dataclass repr 生成器的机制 python 3.10.4cls.__repr__=_repr_fn(flds, globals)) -> _recursive_repr(fn)

可能是关了@dataclass(repr=False),加了def __repr__(self):

如果是这样,那会是什么样子?我们不想包含可选的默认值。

上下文

重复一下,实际上,我的 dataclass10+ 个字段

我正在通过 运行 代码和 repl print()ing 实例,@pytest.mark.parametrize when running pytest 使用 -vvv

大数据类的非默认值(有时是输入)不可能被看到,因为它们被埋在默认字段中,更糟糕的是,每一个都非常大且令人分心:掩盖了打印出来的其他有价值的东西。

相关问题

截至今天,dataclass 个问题还不多(这可能会改变):

你可以这样做:

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