Create an Enum using a custom mix-in type

Question:

In the relevant section of the Python Enum documentation, it is mentioned that mix-in types can be used to ensure items belong to that type and act as objects of that type, like in the example:

class IntEnum(int, Enum):
    pass

However, the documentation does not give any examples on how to use a custom class as a mix-in type, and I’ve failed miserably at that. Is it even possible? And if so, how do I do it?


The code I’m trying to write is

@dataclass
class Field:
    dtype: str
    name: str

@dataclass
class FwfField(Field):
    width: int

    def __post_init__(self):
        if self.width <= 0:
            raise ValueError("Field width must be positive.")

class CnefeSchema(FwfField, Enum):
    state_code = ("int", "Código da UF", 2)
    ...

When I import CnefeSchema from another file or run it, I get

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [1], in <cell line: 1>()
----> 1 from src.conf.schemas import CnefeSchema

File src/conf/schemas.py:25, in <module>
     21     pass
     24 # TODO automatically read schema from Layout
---> 25 class CnefeSchema(EnumFwfField, Enum):
     27     @classmethod
     28     def parse(cls) -> tuple[list, dict, dict]:
     29         """
     30         Get field widths, dtypes, and columns from schema.
     31
   (...)
     34         as `pandas.read_fwf`.
     35         """

File /usr/lib/python3.10/enum.py:298, in EnumMeta.__new__(metacls, cls, bases, classdict, **kwds)
    296 enum_member._name_ = member_name
    297 enum_member.__objclass__ = enum_class
--> 298 enum_member.__init__(*args)
    299 # If another member with the same value was already defined, the
    300 # new member becomes an alias to the existing one.
    301 for name, canonical_member in enum_class._member_map_.items():

File <string>:4, in __init__(self, dtype, name, width)

File /usr/lib/python3.10/types.py:187, in DynamicClassAttribute.__set__(self, instance, value)
    185 def __set__(self, instance, value):
    186     if self.fset is None:
--> 187         raise AttributeError("can't set attribute")
    188     self.fset(instance, value)

AttributeError: can't set attribute

I’ve already tried:

  • Defining another class, EnumFwfField, and using it as a mix-in, i.e.
    class EnumFwfField(FwfField, Enum):
        pass
    
    class CnefeSchema(EnumFwfField, Enum):
        ...
    

    but I get the same error as above;

  • Setting the Enum fields (in this example, state_code) to FwfField, i.e.
    class CnefeSchema(FwfField, Enum):
        state_code = FwfField("int", "Código da UF", 2)
        ...
    

    but then I get

    Traceback (most recent call last):
      File "src/conf/schemas.py", line 25, in <module>
        class CnefeSchema(EnumFwfField, Enum):
      File "/usr/lib/python3.10/enum.py", line 298, in __new__
        enum_member.__init__(*args)
    TypeError: FwfField.__init__() missing 2 required positional arguments: 'name' and 'width'
    
Asked By: tomaz-suller

||

Answers:

The issue you are having is because in your above code you have a name attribute in your Field dataclass, but Enum will not let you set a name attribute.

Rename that field to something else, for example 'dname', and it should work.

(The error message in 3.11 is more informative.)

Answered By: Ethan Furman
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.