Python Forum

Full Version: Create custom Enumerations
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi,

I use the Enum class to have a nicer code when I need to deal with old fortran programs that expect just a number in their inputs. Otherwise my code would be full of magic numbers.
The problem is that in many occasions is necessary to add extra information to the Enum, and doing it in the way is done in Planet Example has the drawback of leaving a Enum.value impractical.

So my first approximation to do something better was:
from enum import Enum

class Coord(Enum):
    def __init__(self, code, label):
        self._value_ = code
        self.label = label

    X = (0, 'Position.X [km]')
    Y = (1, 'Position.Y [km]')
This almost works as expected, except that is not possible to map from the value to the Enum:
# This works
x = Coord((0, 'Position.X [km]'))
# This fails
x = Coord(0)
I know that to make it work I can use the Coord class with a custom __new__ method:

class Coord(Enum):
    def __new__(cls, code, label):
        obj = object.__new__(cls)
        obj._value_ = code
        return obj

    def __init__(self, code, label):
        self.label = label

    X = (0, 'Position.X [km]')
    Y = (1, 'Position.Y [km]')
But looks much complex and prone to error... And to solve it it will be enough to put the Line 221 of emum.py after the line 224:
            enum_member._name_ = member_name
            enum_member.__objclass__ = enum_class
            enum_member.__init__(*args)
            # Allow to customise the value in the init
            value = enum_member._value_
There is something conceptually erroneous in the first proposal (change _value_ in the init) apart from that it breaks the current Enum?
It is quite easy that I am missing something important and it is better to change the design.

If the idea is not bad what shall I do to propose the change?

Thanks!
What about a builder classmethod that returns the right enum? Something like...
>>> class mynum(enum.Enum):
...   Eggs = (0, "green")
...   Ham = (1, "also green")
...   @classmethod
...   def build(cls, enum_kind):
...     items = {0: cls.Eggs, 1: cls.Ham}
...     return items[enum_kind]
...
>>> mynum.build(0)
<mynum.Eggs: (0, 'green')>
>>> mynum.Ham
<mynum.Ham: (1, 'also green')>
The classmethod option looks good to avoid using the __new__ method that is always something easy to break.

The only problem is that it still breaks the Enum syntax as mynum(0) is going to fail.
Maybe is enough using a base class for all my enums like:
class BuildEnum(enum.Enum):
    @classmethod
    def build(cls, enum_kind):
        for entry in cls:
            if entry.value[0] == enum_kind:
                return entry
        raise ValueError("{} is not a valid {}".format(enum_kind, cls.__name__))
And derive all the enums for my library from it. Currently I am doing this, but only for the Enums that need additional info, like the one of the example.

So you think is not a good idea to have Enum classes that set the _value_ field during __init__ instead of __new__?