Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Create custom Enumerations
#1
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!
Reply
#2
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')>
Reply
#3
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__?
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  I want to create custom charts in Python. js1152410 1 545 Nov-13-2023, 05:45 PM
Last Post: gulshan212
  how to create my own custom logging message maiya 4 2,364 Jul-15-2020, 05:42 PM
Last Post: maiya
  How to create Custom Buttons for 3D Scatter plots in Plotly? yourboyjoe 0 2,144 Jun-01-2020, 10:58 PM
Last Post: yourboyjoe

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020