1

I'm trying translate some code from python 2 to python 3 and had trouble with this code:

from enum import Enum, EnumMeta


class KeyCode(Enum):
    A = 1
    B = 2
    C = 3


class KeyMeta(EnumMeta):
    def __new__(mcs, name, bases, dct):
        dct.update({e.name: e.value for e in KeyCode})
        return super(KeyMeta, mcs).__new__(mcs, name, bases, dct)


class Key(Enum, metaclass=KeyMeta):
    pass


print(repr(Key.A))

The problem is that for python2 Key.A is enum < Key.A: 1>, whereas for python3 it is < class 'int'>.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • What are you trying to accomplish? Having the same set of names in several different `Enum`s? [This answer](https://stackoverflow.com/a/42253518/208880) could help with that. Also, see [When should I subclass EnumMeta instead of Enum](https://stackoverflow.com/q/43730305/208880) for some good general advice. – Ethan Furman Nov 22 '19 at 21:25

1 Answers1

1

In Python 3, the dct object is not a regular dictionary, but a subclass dedicated to helping create enums, set via the __prepare__ attribute of the metaclass. It's update() method, however, is not altered from the base dictionary.

It normally expects members to be set via it's __setitem__ method (e.g. dct[name] = value); do so in your __new__ method too:

class KeyMeta(EnumMeta):
    def __new__(mcs, name, bases, dct):
        for e in KeyCode:
            dct[e.name] = e.value
        return super(KeyMeta, mcs).__new__(mcs, name, bases, dct)

Now each of the names A, B and C are recorded as being enum member names and the EnumMeta class takes it from there. Without using the specialised __setitem__ implementation, the names are seen as some other type of attribute instead.

With the above change you get the expected output:

>>> class Key(Enum, metaclass=KeyMeta):
...     pass
...
>>> Key.A
<Key.A: 1>

The same for e in KeyCode: loop will continue to work in Python 2 as well.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343