3

I can easily create a class like

class MyEnum(enum.Enum):
  BOB = "bob"
  RALPH = "ralph"
  ETC = "etc"

Then I can assign variables by enum value:

a = MyEnum('bob')

However -- I want to assign variables by things that could be the correct value. I.e., I'd like to do

a = MyEnum('bob')
b = MyEnum('Bob')
c = MyEnum('BOB')

and have them all work, and all map to the same enum value.

Is there a way of doing this without making a factory method? I've currently defined a create method, so a = MyEnum.create('Bob') works, but I'd like things to be seamless.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Tim Wescott
  • 478
  • 4
  • 14

2 Answers2

2

The thing you are looking for is called _missing_ and is available in the stdlib as of Python3.6, and in aenum1 as of 2.0.

class MyEnum(Enum):

    BOB = "bob"
    RALPH = "ralph"
    ETC = "etc"

    @classmethod
    def _missing_(cls, value):
        for member in cls:
            if member.value == value.lower():
                return member

If _missing_ fails to return a MyEnum member then EnumMeta will raise an exception (so _missing_ doesn't have to worry about that part)2.


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

2 Thanks, Aran-Fey, for bringing that up.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
0

This can be achieved by subclassing EnumMeta, which is the metaclass responsible for defining the __call__ method that's invoked by MyEnum('Bob').

import enum

class CaseInsensitiveEnum(enum.EnumMeta):
    def __call__(self, string):
        string = string.lower()  # convert the string to lowercase
        return super().__call__(string)

class MyEnum(enum.Enum, metaclass=CaseInsensitiveEnum):
  BOB = "bob"
  RALPH = "ralph"
  ETC = "etc"

(Keep in mind that all the enum values have to be lowercase; i.e. BOB = 'Bob' would not work.)

Demonstration:

>>> MyEnum('Bob')
<MyEnum.BOB: 'bob'>
>>> MyEnum('Bob') is MyEnum.BOB
True
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149