Dataclass字段可以格式化其为reter的值吗?

发布于 2025-02-04 07:44:31 字数 2370 浏览 4 评论 0原文

我有一个以HEX和HSV形式持有RGB数据的节点类。我将使用它以各种方式对颜色进行排序,并且希望HSV元组保持浮动形式进行比较,而不是从字符串中转换为每种用途。是否有一种方法可以指定Dataclass字段,它应该以类似于默认值的特定方式格式化值,而default_factory,即A repr_factory

def RGB2HSV(r, g, b):
    '''Returns HSV values in the range H = [0, 360], S = [0, 100], V = [0, 100]'''
    r, g, b = r / 255, g / 255, b / 255
    maxRGB = max(r, g, b)
    minRGB = min(r, g, b)
    delta = maxRGB - minRGB

    V = maxRGB
    if V == 0:
        return 0, 0, V
    
    S = delta / V * 100
    if S == 0:
        return 0, S, V * 100
    
    if V == r:
        H = (g - b) / delta
    elif V == g:
        H = 2 + (b - r) / delta
    else:
        H = 4 + (r - g) / delta
    H *= 60
    if H < 0:
        H += 360
    
    return H, S, V * 100

@dataclass
class Node:
    r: int = field(repr=False)
    g: int = field(repr=False)
    b: int = field(repr=False)
    hex: tuple[int, int, int] = field(init=False)
    hsv: tuple[float, float, float] = field(init=False)

    def __post_init__(self):
        self.hex = self.r, self.g, self.b # Generating random r, g, b numbers
        self.hsv = RGB2HSV(self.hex) # Converts the r, g, b to a tuple of floats

当我研究各种各样的情况时,我正在打印节点,看到10个不必要的浮点数是分散注意力的。据我所知,我会更好地实施自己的__ epr __,而不是依靠生成的数据级别?

我要查看__ repl __值的原因是因为它是由数据级别自动生成的,并且可以使几乎相同的颜色更容易区分几乎相同的颜色,而不是仅查看视觉输出。如果我知道颜色是什么是实际数字,则更容易找出要更改或下一步的事情。输出末尾的一部分:

Node(hex=(238, 0, 0), hsv=(0.0, 100.0, 93.33333333333333))
Node(hex=(238, 17, 0), hsv=(4.285714285714286, 100.0, 93.33333333333333))
Node(hex=(238, 34, 0), hsv=(8.571428571428571, 100.0, 93.33333333333333))
Node(hex=(238, 51, 0), hsv=(12.857142857142858, 100.0, 93.33333333333333))
Node(hex=(255, 0, 0), hsv=(0.0, 100.0, 100.0))
Node(hex=(255, 17, 0), hsv=(4.0, 100.0, 100.0))
Node(hex=(255, 34, 0), hsv=(8.0, 100.0, 100.0))
Node(hex=(255, 51, 0), hsv=(12.0, 100.0, 100.0))

基本上,可以将格式指定到数据级字段,类似于如何将函数指定到default_factory,以便生成的__repr __要为我格式化字段,所以我不必写自己的?

...
    hsv: tuple[float, float, float] = field(init=False, repr_factory=lambda x: "{:.3f"}.format(x) for x in self.hsv)
...
Node(hex=(238, 51, 0), hsv=(12.857, 100.000, 93.333))

I have a Node class holding RGB data in both hex and HSV form. I'll be using this to sort colors in various ways and would prefer the HSV tuple to remain in float form for comparisons instead of converting from a string for every use. Is there a way to specify to the dataclass field that it should format the value in a specific way similar to default values with the default_factory, i.e. a repr_factory?

def RGB2HSV(r, g, b):
    '''Returns HSV values in the range H = [0, 360], S = [0, 100], V = [0, 100]'''
    r, g, b = r / 255, g / 255, b / 255
    maxRGB = max(r, g, b)
    minRGB = min(r, g, b)
    delta = maxRGB - minRGB

    V = maxRGB
    if V == 0:
        return 0, 0, V
    
    S = delta / V * 100
    if S == 0:
        return 0, S, V * 100
    
    if V == r:
        H = (g - b) / delta
    elif V == g:
        H = 2 + (b - r) / delta
    else:
        H = 4 + (r - g) / delta
    H *= 60
    if H < 0:
        H += 360
    
    return H, S, V * 100

@dataclass
class Node:
    r: int = field(repr=False)
    g: int = field(repr=False)
    b: int = field(repr=False)
    hex: tuple[int, int, int] = field(init=False)
    hsv: tuple[float, float, float] = field(init=False)

    def __post_init__(self):
        self.hex = self.r, self.g, self.b # Generating random r, g, b numbers
        self.hsv = RGB2HSV(self.hex) # Converts the r, g, b to a tuple of floats

While I'm working out the different sorts, I'm printing out the Nodes and seeing 10 unnecessary digits of a float is distracting. As far as I can think of, would I just be better off implementing my own __repr__ for the class instead of relying on the dataclass generated one?

The reason I'm looking at the __repr__ value is because it's automatically generated by the dataclass and can make distinguishing between nearly identical colors easier than just looking at the visual output. It'll be easier to find out what to change or do next if I know what the actual numbers a color are. A portion of the end of the output:

Node(hex=(238, 0, 0), hsv=(0.0, 100.0, 93.33333333333333))
Node(hex=(238, 17, 0), hsv=(4.285714285714286, 100.0, 93.33333333333333))
Node(hex=(238, 34, 0), hsv=(8.571428571428571, 100.0, 93.33333333333333))
Node(hex=(238, 51, 0), hsv=(12.857142857142858, 100.0, 93.33333333333333))
Node(hex=(255, 0, 0), hsv=(0.0, 100.0, 100.0))
Node(hex=(255, 17, 0), hsv=(4.0, 100.0, 100.0))
Node(hex=(255, 34, 0), hsv=(8.0, 100.0, 100.0))
Node(hex=(255, 51, 0), hsv=(12.0, 100.0, 100.0))

Basically, can a format be specified to a dataclass field, similar to how a function can be specified to default_factory, in order for the generated __repr__ to format the field for me so I don't have to write my own?

...
    hsv: tuple[float, float, float] = field(init=False, repr_factory=lambda x: "{:.3f"}.format(x) for x in self.hsv)
...
Node(hex=(238, 51, 0), hsv=(12.857, 100.000, 93.333))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

可爱暴击 2025-02-11 07:44:31

Dataclasses库当前不支持这样的格式字段。默认__ epr __在每个随附的字段中生成的代码始终位于f'field = {self.field!r}'中。您将必须编写自己的__ epr __

The dataclasses library currently does not support formatting fields like that. The code generated in the default __repr__ for each included field is always in the formf'field={self.field!r}'. You will have to write your own __repr__.

冷血 2025-02-11 07:44:31

这是基于 jasmijn的答案我们必须编写我们自己的__ epr__ 。我敢肯定,我可能会以最糟糕的方式处理字段,但这是一个开始。用更好的方法替换尴尬的字段名称和值访问。

from dataclasses import dataclass
import struct


@dataclass
class ElfHeader:
    """ELF Header 32-bit"""
    magic: bytes
    bitwidth: int
    endianess: int
    version: int
    osabi: int
    abi: int
    # padding: int
    filetype: int
    machine: int
    version2: int
    entry_address: int
    phoff: int
    shoff: int
    flags: int
    header_size: int
    ph_entry_size: int
    ph_num: int
    sh_entry_size: int
    sh_num: int
    sh_string_index: int

    @staticmethod
    def from_bytes(data_bytes):
        """Construct an instance from binary data."""
        # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
        return ElfHeader(*struct.unpack('<4s5B7x2H5L6H', data_bytes[:52]))
        
    def __post_init__(self):
        self.reformat_hex_fields = {"machine", "entry_address"}

    def __repr__(self):
        """Just like the default __repr__ but supports reformatting some values."""
        def hexConvert(name, value):
            return hex(value) if name in self.reformat_hex_fields else f'{value!r}'
        
        fields = (
            f'{name}={hexConvert(name, value)}'
            for field in self.__dataclass_fields__.values() if field.repr
            # This just assigns shorter names to code to improve readability above.
            # It's like the new assignment operator.
            for name, value in ((field.name, self.__getattribute__(field.name)),)
            )
        return f'{self.__class__.__name__}({", ".join(fields)})'


data_header = b'\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00i\x00\x01\x00\x00\x00X\xb3\x00\x00\x0c\xe4\x1a\x00L\xe5\x1a\x00\x00\x00\x00\x004\x00 \x00\n\x00(\x00l\x00k\x00'
print(ElfHeader.from_bytes(data_header))

输出(修改为原始):

ElfHeader(magic=b'\x7fELF', bitwidth=1, endianess=1, version=1, osabi=0, abi=0, filetype=2, machine=0x69, version2=1, entry_address=0xb358, phoff=1762316, shoff=1762636, flags=0, header_size=52, ph_entry_size=32, ph_num=10, sh_entry_size=40, sh_num=108, sh_string_index=107)
ElfHeader(magic=b'\x7fELF', bitwidth=1, endianess=1, version=1, osabi=0, abi=0, filetype=2, machine=105, version2=1, entry_address=45912, phoff=1762316, shoff=1762636, flags=0, header_size=52, ph_entry_size=32, ph_num=10, sh_entry_size=40, sh_num=108, sh_string_index=107)

Here's a working proof-of-concept implementation based on Jasmijn's answer that we must write our own __repr__. I'm sure that I probably processed the fields in the worst way possible, but this is a start. Replace the awkward field name and value access with a better method.

from dataclasses import dataclass
import struct


@dataclass
class ElfHeader:
    """ELF Header 32-bit"""
    magic: bytes
    bitwidth: int
    endianess: int
    version: int
    osabi: int
    abi: int
    # padding: int
    filetype: int
    machine: int
    version2: int
    entry_address: int
    phoff: int
    shoff: int
    flags: int
    header_size: int
    ph_entry_size: int
    ph_num: int
    sh_entry_size: int
    sh_num: int
    sh_string_index: int

    @staticmethod
    def from_bytes(data_bytes):
        """Construct an instance from binary data."""
        # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
        return ElfHeader(*struct.unpack('<4s5B7x2H5L6H', data_bytes[:52]))
        
    def __post_init__(self):
        self.reformat_hex_fields = {"machine", "entry_address"}

    def __repr__(self):
        """Just like the default __repr__ but supports reformatting some values."""
        def hexConvert(name, value):
            return hex(value) if name in self.reformat_hex_fields else f'{value!r}'
        
        fields = (
            f'{name}={hexConvert(name, value)}'
            for field in self.__dataclass_fields__.values() if field.repr
            # This just assigns shorter names to code to improve readability above.
            # It's like the new assignment operator.
            for name, value in ((field.name, self.__getattribute__(field.name)),)
            )
        return f'{self.__class__.__name__}({", ".join(fields)})'


data_header = b'\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00i\x00\x01\x00\x00\x00X\xb3\x00\x00\x0c\xe4\x1a\x00L\xe5\x1a\x00\x00\x00\x00\x004\x00 \x00\n\x00(\x00l\x00k\x00'
print(ElfHeader.from_bytes(data_header))

Output (Modified then Original):

ElfHeader(magic=b'\x7fELF', bitwidth=1, endianess=1, version=1, osabi=0, abi=0, filetype=2, machine=0x69, version2=1, entry_address=0xb358, phoff=1762316, shoff=1762636, flags=0, header_size=52, ph_entry_size=32, ph_num=10, sh_entry_size=40, sh_num=108, sh_string_index=107)
ElfHeader(magic=b'\x7fELF', bitwidth=1, endianess=1, version=1, osabi=0, abi=0, filetype=2, machine=105, version2=1, entry_address=45912, phoff=1762316, shoff=1762636, flags=0, header_size=52, ph_entry_size=32, ph_num=10, sh_entry_size=40, sh_num=108, sh_string_index=107)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文