22

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.

Hernan
  • 5,811
  • 10
  • 51
  • 86

2 Answers2

35

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.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
  • 1
    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 Jul 14 '17 at 12:18
  • @likern, if this is still an issue, please [file an issue](https://bitbucket.org/stoneleaf/aenum/issues). Thanks. – Ethan Furman Apr 18 '18 at 19:01
  • 1
    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`). – Gino Mempin Nov 19 '18 at 05:17
  • It also turns out to be pickleable. – Saren Tasciyan Nov 03 '20 at 12:53
  • @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: is not a valid – Saren Tasciyan Nov 05 '20 at 10:01
20

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.
SzieberthAdam
  • 3,999
  • 2
  • 23
  • 31
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    As I mentioned, this is not an option for the exact same reasons that you describe. Thanks anyway – Hernan Jan 24 '15 at 14:29
  • @Hernan: that is your *only* option when using the `enum` library, however. – Martijn Pieters Jan 24 '15 at 14:31
  • 9
    **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`](https://pypi.python.org/pypi/aenum) and [Java `enum`](https://stackoverflow.com/questions/13015870/java-enums-mutability-usecases-and-possibilities) types. – Cecil Curry Mar 20 '19 at 01:51
  • 3
    @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). – Martijn Pieters Mar 20 '19 at 12:20