2

I wanted to overload the getitem Enum method in Python to return the first element of a tuple instead of the whole value content. For that, I tried to create an Enum based in the EnumMeta with the redefined method, just like below, and then the Enum final class inheriting from it, just like it's shown below:

from enum import Enum
from enum import EnumMeta
from datetime import date

class CommandMeta (EnumMeta):
    SUCCESS = 0, "Exited successsssfully."

    def __getitem__(cls, value, *args, **kwargs):
        value = value[0]
        return super().__getitem__(value, *args, **kwargs)

class Command (Enum, metaclass=CommandMeta):
    SUCCESS = 0, "Exited successsssfully."

The expected output was:

print(Command.SUCCESS.value)
0

And instead, it is still the same:

print(Command.SUCCESS.value)
(0, 'Exited successsssfully.')

How could I manage to overload the method properly?

3 Answers3

1

The documentation on enums gives a good example of how to store multiple constants on an enum, and access them via attribute name.

class Planet(Enum):
    MERCURY = (3.303e+23, 2.4397e6)
    VENUS   = (4.869e+24, 6.0518e6)
    EARTH   = (5.976e+24, 6.37814e6)
    MARS    = (6.421e+23, 3.3972e6)
    JUPITER = (1.9e+27,   7.1492e7)
    SATURN  = (5.688e+26, 6.0268e7)
    URANUS  = (8.686e+25, 2.5559e7)
    NEPTUNE = (1.024e+26, 2.4746e7)
    def __init__(self, mass, radius):
        self.mass = mass       # in kilograms
        self.radius = radius   # in meters
    @property
    def surface_gravity(self):
        # universal gravitational constant  (m3 kg-1 s-2)
        G = 6.67300E-11
        return G * self.mass / (self.radius * self.radius)

Which looks like this when accessed:

>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.mass
5.976e+24
>>> Planet.EARTH.surface_gravity
9.802652743337129
Dunes
  • 37,291
  • 7
  • 81
  • 97
0

Command.SUCCESS and SUCCESS.value are all attribute accesses, so __getitem__ never enters the picture.

If you want the value to be 0 (or 1, etc.) then you want to split 0, "Exited successsssfully" into two parts -- maybe value and __doc__? To see something similar, only done with the stdlib Enum, try this one.

As far as subclassing EnumMeta, please read this.

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

You don't need a new metaclass for this, just ordinary inheritance. __getitem__ is only invoked when you use index syntax.

class IndexableEnum(Enum):
    def __getitem__(self, key):
        return self.value[key]

class Command(IndexableEnum):
    SUCCESS = 0, "Exited successfully"

Then

>>> Command.SUCCESS
<Command.SUCCESS: (0, 'Exited successfully')>
>>> Command.SUCCESS[0]
0

(Of course, a class inheriting from IndexableEnum should have values that are, in fact, indexable, and __getitem__ can be defined directly in your Command class if you don't plan on making multiple classes indexable.)

chepner
  • 497,756
  • 71
  • 530
  • 681