当Frozen = True时,如何在__post_init__中设置dataclass字段的值?

时间:2018-12-13 07:16:43

标签: python

我正在尝试创建一个冻结的数据类,但是在设置__post_init__中的值时遇到了问题。使用init param设置时,是否可以根据dataclassfrozen=True中的值来设置字段值?

RANKS = '2,3,4,5,6,7,8,9,10,J,Q,K,A'.split(',')
SUITS = 'H,D,C,S'.split(',')


@dataclass(order=True, frozen=True)
class Card:
    rank: str = field(compare=False)
    suit: str = field(compare=False)
    value: int = field(init=False)
    def __post_init__(self):
        self.value = RANKS.index(self.rank) + 1
    def __add__(self, other):
        if isinstance(other, Card):
            return self.value + other.value
        return self.value + other
    def __str__(self):
        return f'{self.rank} of {self.suit}'

这是跟踪

 File "C:/Users/user/.PyCharm2018.3/config/scratches/scratch_5.py", line 17, in __post_init__
    self.value = RANKS.index(self.rank) + 1
  File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'value'

2 个答案:

答案 0 :(得分:8)

Use the same thing the generated __init__ method does: object.__setattr__.

def __post_init__(self):
    object.__setattr__(self, 'value', RANKS.index(self.rank) + 1)

答案 1 :(得分:-1)

According to PyDocs, you can't create truly immutable objects.

"...dataclasses will add setattr() and delattr() methods to the class. These methods will raise a FrozenInstanceError when invoked."

To answer your question, you cannot set values after creation using a frozen dataclass.

Frozen is designed to create a read only object. Not sure why you are trying to as you didn't specify a use-case.

Below is something that you can do, that I believe accomplishes what you are looking to do.


    #!/usr/bin/env python3.7
    """Card Dataclass: Jerod Gawne, 2019.01.09 <https://github.com/jerodg>"""
    from dataclasses import dataclass, field

    RANKS = '2,3,4,5,6,7,8,9,10,J,Q,K,A'.split(',')
    SUITS = 'H,D,C,S'.split(',')


    @dataclass(order=True)
    class Card:
        """Card"""
        rank: str = field(compare=False)
        suit: str = field(compare=False)
        value: int = field(init=False)

        def __post_init__(self):
            self.value = RANKS.index(self.rank) + 1

        def __add__(self, other):
            if isinstance(other, Card):
                return self.value + other.value
            return self.value + other

        def __str__(self):
            return f'{self.rank} of {self.suit}'


    if __name__ == '__main__':
        c = Card(rank='J', suit='H')
        print(c.value)


Result: 10