31

I understand that this is NOT the standard use case, but I need to dynamically add elements to a IntEnum derived class in Python. Notice that dynamically creating the Enum using the functional API is not enough. I need to add elements to an existing enum. How can I do this?

Background: For those of you wondering why would somebody want to do this. I am wrapping a library and the values for the enum are defined within the library. I can query the names and values using the library API. But I cannot do it upon initialization as it depends on components which are dynamically loaded by the library upon user request. I could load all components at start up and use the functional API to create the enum upon import but this is time consuming and has side effects.

5
  • 1
    isn't the whole point of ENUM the immutability? Commented Jan 24, 2015 at 14:07
  • 3
    You can't. Even subclassing that extend existing enums is guarded against. Commented Jan 24, 2015 at 14:11
  • @MartijnPieters Any suggestion for this particular use case? Commented Jan 24, 2015 at 14:17
  • 2
    @MightyPork Yes, but Python is a great dynamic glue language and many times you need to interact with something that is out of your control. Commented Jan 24, 2015 at 14:28
  • @Hernan: I am curious why you didn't accept the answer that does what you need? Commented Apr 7, 2017 at 17:33

2 Answers 2

45

This is a job for the extend_enum function from the aenum library1.


A couple sample Enums:

from aenum import Enum

class Color(Enum):
    black = 0

class ColorHelp(Enum):
    _init_ = 'value __doc__'
    black = 0, 'the absence of color'

extend_enum in action:

from aenum import extend_enum

extend_enum(Color, 'white', 1)
print Color, list(Color)
print repr(Color.black), Color.black, repr(Color.white), Color.white
print

extend_enum(ColorHelp, 'white', 1, 'the presence of every color')
print ColorHelp, list(ColorHelp)
print repr(ColorHelp.black), ColorHelp.black, ColorHelp.black.__doc__, repr(ColorHelp.white), ColorHelp.white, ColorHelp.white.__doc__

Which gives us:

<enum 'Color'> [<Color.black: 0>, <Color.white: 1>]
<Color.black: 0> Color.black <Color.white: 1> Color.white

<enum 'ColorHelp'> [<ColorHelp.black: 0>, <ColorHelp.white: 1>]
<ColorHelp.black: 0> ColorHelp.black the absence of color <ColorHelp.white: (1, 'the presence of every color')> ColorHelp.white None

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

Sign up to request clarification or add additional context in comments.

5 Comments

I get "File "/home/vamalov/.local/lib/python3.5/site-packages/aenum/__init__.py", line 2166, in extend_enum for canonical_value in canonical_member._values_: AttributeError: 'MachineName' object has no attribute 'values'", where MachineName is usual python enum. Is this method still working?
@likern, if this is still an issue, please file an issue. Thanks.
To anyone coming here who is having the same error as @likern, make sure to use aenum's Enum (from aenum import Enum) instead of the regular Enum (from enum import Enum).
It also turns out to be pickleable.
@EthanFurman I would like to correct my previous comment. I could not load the pickle object with extended enum. Default enum definition doesn't include some of the items. ValueError: <enumValue> is not a valid <EnumType>
23

Enums are immutable, that's rather the point. You can create a new enum that replaces the original instead:

from enum import Enum

names = [m.name for m in ExistingEnum] + ['newname1', 'newname2']
ExistingEnum = Enum('ExistingEnum', names)

but any existing references (say, in other modules) would continue to use the old definition.

names can be:

  • A string containing member names, separated either with spaces or commas. Values are incremented by 1 from start (which can be set as a keyword argument and defaults to 1).
  • An iterable of member names (as in the code above). Values are incremented by 1 from start.
  • An iterable of (member name, value) pairs.
  • A mapping of member name -> value pairs.

6 Comments

As I mentioned, this is not an option for the exact same reasons that you describe. Thanks anyway
@Hernan: that is your only option when using the enum library, however.
Immutability is not "the point" of enumerations. Enumerations exist to guarantee mutual exclusion over a finite unordered set. Appending additional members onto an existing enumeration at runtime in no way violates this guarantee. That Python's standard enum.Enum type chose to prohibit this valid use case in no way prohibits third-party alternatives from doing so. See also: the mutable aenum.Enum and Java enum types.
@CecilCurry: Neither of those libraries mutate the original enum, they extend on an existing enum to create a new enum. If enums were mutable at runtime, that'd have a wide range of implications where you can no longer guarantee to be handling all possible values (languages like Rust even build their type safety on enums not being runtime mutable).
@MartijnPieters "Neither of those libraries mutate the original enum, they extend on an existing enum to create a new enum." This doesn't seem to be correct for aenum.Enum. References to the original enum are of the same type as references to the mutated enum.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.