2

I have a simple Enum:

class E(Enum):
    A = 'a'
    B = 'b'

To access 'a' I must type E.A.value. However, the value is the only thing I need from the Enum object.

How to write an Enum, where 'a' could be accessed just by E.A?

Eerik Sven Puudist
  • 2,098
  • 2
  • 23
  • 42
  • 1
    You can do it using `IntEnum`. Check [this](https://stackoverflow.com/a/56651009/4909087). – cs95 Jun 24 '19 at 18:02
  • @cs95 Thank you! However, using an `int` as value was just an example. It should actually be a user-defined class. I will rewrite my question, could you please remove the `duplicate` flag? – Eerik Sven Puudist Jun 24 '19 at 18:16
  • You mean defining `A` as `self.A = 'a'` and so on? – cs95 Jun 24 '19 at 18:19
  • The duplicate has been update. You need to use `self`. – cs95 Jun 24 '19 at 18:20
  • 2
    Unless you you want to use `IntEnum` (which you **really shouldn't** unless you are trying to maintain compatibility with code written before the `enum` module), then you have to use `element.value`, that is simply how *enums work*. Why are you using an `enum` here? It seems like you probably want something else. Maybe an X-Y problem – juanpa.arrivillaga Jun 24 '19 at 18:27
  • The value of an enum member should only rarely be needed; can you give us more detail on the problem you are trying to solve by using an `Enum`? – Ethan Furman Jun 24 '19 at 18:37
  • [I found this answer and, while not a duplicate, it looks relevant](https://stackoverflow.com/questions/35567724/how-to-define-custom-properties-in-enumeration-in-python-javascript-like) – Green Cloak Guy Jun 24 '19 at 18:37

2 Answers2

2

Using an int as value was just an example. It should actually be a user-defined class.

If you mixin a class/type with the Enum, then simply accessing the member itself will give you a subtype of that type:

from enum import Enum

class MyClass:
    def __init__(self, color):
        self.color = color

class MyEnum(MyClass, Enum):
    first = 'red'
    second = 'green'
    third = 'blue'

and in use:

>>> MyEnum.first
<MyEnum.first: 'red'>

>>> MyEnum.first.color
'red'

>>> type(MyEnum.first)
<enum 'MyEnum'>

>>> isinstance(MyEnum.first, MyClass)
True

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

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

I looked around a lot, and I couldn't find a good solution to this problem using the Enum class you're trying to use. However, if you're willing to eschew the idea of using the Enum as a superclass, you can kludge this together:

class Demo:
    # something with a 'value' method in it
    def __init__(self, val):
        self.value = val

def custom_enum(cls):
    # class decorator to get __getattribute__() to work properly
    # this is necessary because __getattribute__() only exists as an instance method,
    #   and there seems to be no direct equivalent for class methods
    return cls()

@custom_enum
class E:
    # first, define our enumerated variables in a dict
    _enums = {
        'A': Demo('a'),
        'B': Demo('b'),
        'chicken': Demo('cluck')
    }

    # then, override __getattribute__() to first get the key from the dict, 
    #   and return the .value property of it
    def __getattribute__(self, key):
        # because of the decorator, we can't call self._enums or else we get a RecursionError
        # therefore, we need to implicitly subclass `object`, and then
        #   deliberately invoke object.__getattribute__ on self, to access _enums
        my_enums = object.__getattribute__(self, '_enums')
        return my_enums[key].value

Actually defining the values of your enumerable is as simple as editing the _enums dict. And once you've done so, it should work roughly as you want it to:

>>> E.A
'a'
>>> E.B
'b'
>>> E.chicken
'cluck'

From here you could modify the implementation however necessary (such as returning an AttributeError instead of a KeyError, for example, or overriding __setattr__() to make enum values non-settable, or whatever).

Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53