The changes to enums in Python 3.11 have resulted in some bizarre, undocumented, and (in my case) unwanted behavior.
One of the things that disturbs me most about the new behavior is that the data type of the enum values now depends on whether the constructor of the mixin type raises an exception. I view this as a bug.
Consider the following code:
import enum
class Base:
def __init__(self, x):
print('In Base init')
raise ValueError("I don't like", x)
class MyEnum(Base, enum.Enum):
A = 'a'
def __init__(self, y):
print('In MyEnum init')
self.y = y
print(type(MyEnum.A))
print(type(MyEnum.A.value))
class Base2:
def __init__(self, x):
print('In Base2 init')
class MyEnum2(Base2, enum.Enum):
A = 'a'
def __init__(self, y):
print('In MyEnum2 init')
self.y = y
print(type(MyEnum2.A))
print(type(MyEnum2.A.value))
In Python 3.10, this produces:
In MyEnum init
<enum 'MyEnum'>
<class 'str'>
In MyEnum2 init
<enum 'MyEnum2'>
<class 'str'>
In Python 3.11, this produces
In Base init
In MyEnum init
<enum 'MyEnum'>
<class 'str'>
In Base2 init
In MyEnum2 init
<enum 'MyEnum2'>
<class '__main__.Base2'>
The code responsible is here.
Additionally as a consequence of this, MyEnum('a')
works in both versions, but MyEnum2('a')
works in Python 3.10, but not in Python 3.11.
In my specific case, I am creating a tokenizer. I want to use enums to represent symbol tokens. I have a base Token class, and I want my enum members to inherit from the Token class (ie isinstance(BoolLiteral.TRUE, Token)
in the code below). The enum members should have a value
field which is the string for the token. The enum members should not inherit from str, only from Token (so I do not want to use StrEnum).
In Python 3.10, this is trivial, eg:
class BoolLiteral(Token, enum.Enum):
TRUE = 'true'
FALSE = 'false'
In Python 3.11, I’m not sure what the proper way to do this is. The code above happens to work so long as Token lacks an __init__
method taking one parameter (because without such a method you get an exception, producing the old behavior). However it makes me uncomfortable to rely on that.
The documentation also references subclassing of the form
class EnumName([mix-in, ...,] [data-type,] base-enum):
It’s not clear to me how this is supposed to work. It doesn’t seem to work at all in Python 3.11 if the mix-in type has an __init__
method which doesn’t produce an exception. In Python 3.10, using str as the data-type makes the enum members be instances of str, which is not what I want. This behavior should be clarified in the documentation.