2

I would like to have an Enum with internal property for each enum value, this property can't be set or initialized by the user, but once the user has created Enum from a value, he would be able to read (get) this internal property based on the value.

e.g.

class Channels(Enum): Email = 1, True # True is the value of the internal property for value 1 Sms = 2, True # True is the value of the internal property for value 2 Log = 3, False # False is the value of the internal property for value 3 

Problem is that currently in order to create an instance of this class I need to: x = Channels((1, True)) and that's bad. I want to be able to still create instances like: x = Channels(1) (i.e. True is the internal property of 1, it shouldn't be specified by the user).

My second try was:

class Channels2(Enum): @DynamicClassAttribute def internal_property(self): if self.value == 1: return True elif self.value == 2: return True elif self.value == 3: return False Email = 1 Sms = 2 Log = 3 

And this seems to be working (x = Channels(1) is working and x.internal_property returns True, as it should).

The problem here is that it feels not so efficient, executing those if statements every time the internal_property is accessed.. Is there any other way making the Enum treat internal_property as a an extra field of the instance (on top of Enum regular name and value fields)?

2 Answers 2

3

You can use __new__() as documented here to customize the value of the enum object. For example you can use your original definitions like:

from enum import Enum class Channels(Enum): def __new__(cls, value, internal_property): obj = object.__new__(cls) obj._value_ = value obj.internal_property = internal_property return obj email = 1, True sms = 2, True log = 3, False print(Channels.email) # Coordinate.email print(Channels.email.value) # 1 print(Channels(3)) # Coordinate.log print(Channels(3).internal_property) # False print(Channels(1).internal_property) #True 
Sign up to request clarification or add additional context in comments.

4 Comments

Looks good! You don't need to inherit from int, though -- just do obj = object.__new__(cls)
Thanks @EthanFurman -- I updated the answer. aenum looks great!
Seems like you should also explicitly add internal_property as an attribute of the enum by adding the line internal_property: bool at the class level. This will remove Mypy "undefined attribute" errors whenever internal_property is referenced and add code completion, typing, and docstrings to your custom properties; the current solution doesn't solve those problems.
Another point: while the code Channels(value) will run, linters will flag an error because it's expecting internal_property to also be passed to the __new__ constructor. You can get around this by providing an arbitrary default value for those extra params in __new__, making them optional.
1

@MarkMeyer has the stdlib solution down well, but if you're looking for easier ways to create Enums you should try aenum1:

from aenum import Enum class Channels(Enum): # _init_ = 'value internal_property' # email = 1, True sms = 2, True log = 3, False 

and in use:

>>> list(Channels) [<Channels.email: 1>, <Channels.sms: 2>, <Channels.log: 3>] >>> Channels(2) <Channels.sms: 2> >>> Channels(2).name 'sms' >>> Channels(2).internal_property True 

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.