Why do I get an AttributeError when adding a string field to a dataclass instantiated by an Enum?

Question:

I’m confused by this behavior: I have a frozen dataclass of which only 10 are ever needed, so I wanted to put them into an Enum, and did so successfully. Later, I realized I wanted to be able to put a name on them, and all of a sudden, the Enum can’t instantiate the dataclass.

import enum
import dataclasses as dc

class Stats(int, enum.Enum):
    HP = 0
    STA = 1
    SPD = 2
    ATK = 3
    DEF = 4
    SPATK = 5
    SPDEF = 6

@dc.dataclass(frozen=True)
class Strand:
    keeps: tuple[Stats, ...]
    costs: int

class Strands(Strand, enum.Enum):
    VIT =   ((Stats.HP,),              1000)
    END =   ((Stats.STA,),             1000)
    LTH =   ((Stats.SPD,),             1000)
    AGG =   ((Stats.ATK,),             1000)
    HRD =   ((Stats.DEF,),             1000)
    GFT =   ((Stats.SPATK,),           1000)
    DOM =   ((Stats.SPDEF,),           1000)
    VIGOR = ((Stats.HP, Stats.STA),    5000)
    MIGHT = ((Stats.ATK, Stats.SPATK), 5000)
    IMMUN = ((Stats.DEF, Stats.SPDEF), 5000)

The above, what I had before adding the string field, runs successfully – it doesn’t do anything. I’m adding a string to Strand and altering the Enum as follows:

@dc.dataclass(frozen=True)
class Strand:
    keeps: tuple[Stats, ...]
    costs: int
    name: str

class Strands(Strand, enum.Enum):
    VIT =   ((Stats.HP,),              1000, "Vitality")
    END =   ((Stats.STA,),             1000, "Endurance")
    LTH =   ((Stats.SPD,),             1000, "Lithe")
    AGG =   ((Stats.ATK,),             1000, "Aggressive")
    HRD =   ((Stats.DEF,),             1000, "Hardening")
    GFT =   ((Stats.SPATK,),           1000, "Gifted")
    DOM =   ((Stats.SPDEF,),           1000, "Dominant")
    VIGOR = ((Stats.HP, Stats.STA),    5000, "Vigor")
    MIGHT = ((Stats.ATK, Stats.SPATK), 5000, "Mighty")
    IMMUN = ((Stats.DEF, Stats.SPDEF), 5000, "Immunity")

When I run it with those changes, it no longer exits successfully and instead gives me the following error:

Traceback (most recent call last):
  File "test.py", line 20, in <module>
    class Strands(Strand, enum.Enum):
  File "C:Program FilesPython310libenum.py", line 298, in __new__
  File "<string>", line 5, in __init__
  File "C:Program FilesPython310libtypes.py", line 187, in __set__
    raise AttributeError("can't set attribute")
AttributeError: can't set attribute

I’m confused as to why that’s happening, and as to why it’s only happening with strings and not the tuple or int. From what I can gather, python strs are immutable just like the other two types in Strand – though I’m not sure why it would matter, since you can have mutable types in a frozen dataclass.

Is there something weird that happens when initializing a frozen dataclass with a string, when the initialization is done by an Enum?

I’m not really looking for a solution since the issue is pretty easy to get around, I’m just confused by this behavior and wondered if there was an explanation. I’m using Python 3.10.2 – I haven’t tested this with other Python versions.

Asked By: lapraswastaken

||

Answers:

After some time I figured this out. Apparently, Enum has a built-in un-settable field called name which collides with your identically named field name. If you change the name of name to something else, this will work as expected.

Answered By: Yevhen Kuzmovych