How does one get an Enum's members into the global namespace?
Asked Answered
S

3

8

Python now has an Enum type (new in 3.4 with PEP 435, and alse backported), and while namespaces are a good thing, sometimes Enums are used more like constants, and the enum members should live in the global (er, module) namespace.

So instead of:

Constant(Enum):
    PI = 3.14

...

area = Constant.PI * r * r

I can just say:

area = PI * r * r

Is there an easy way to get from Constant.PI to just PI?

Sporocyte answered 24/1, 2015 at 21:54 Comment(0)
S
13

The officially supported method is something like this:

globals().update(Constant.__members__)

This works because __members__ is the dict-like object that holds the names and members of the Enum class.

I personally find that ugly enough that I usually add the following method to my Enum classes:

@classmethod
def export_to(cls, namespace):
    namespace.update(cls.__members__)

and then in my top level code I can say:

Constant.export_to(globals())

Note: exporting an Enum to the global namespace only works well when the module only has one such exported Enum. If you have several it is better to have a shorter alias for the Enum itself, and use that instead of polluting the global namespace:

class Constant(Enum):
    PI = ....
C = Constant

area = C.PI * r * r
Sporocyte answered 24/1, 2015 at 21:54 Comment(2)
So it does work well as long as you don't have duplicate names in those exported enums?Astir
@JürgenA.Erhard: Yes.Sporocyte
W
0

FWIW — this is more of a comment rather than an answer — below is the beginning of a function from some old code I wrote which implements it's own named int-like enumerated-value objects which were added to the global namespace by default (there's no named container Enum class involved). However it's doing something similar what's shown in your own answer, so I think it a good overall approach because it's worked well for me.

def Enum(names, values=None, namespace=None):
    """Function to assign values to names given and add them to a
    namespace. Default values are a sequence of integers starting at
    zero. Default namespace is the caller's globals."""
    if namespace is None:
        namespace = sys._getframe(1).f_globals  # caller's globals

    pairs = _paired(names, values)
    namespace.update(pairs)  # bind names to cooresponding named numbers
        . . .

The point being, as far as implementing something for the current Enum class module goes, I'd suggest adding something like it or the def export_to() method shown in your own answer to the Enum base class in the next Python release so it's available automatically.

Woebegone answered 24/1, 2015 at 23:36 Comment(0)
K
0

Since python 3.11, enum module has a new class decorator enum.global_enum that puts enum member names in global namespace, and also modifies there repr such that it appears as if it was defined in global scope, rather than the class scope. Here's an example:

from enum import global_enum, Enum
import cmath

@global_enum
class GloballyFamousConstant(Enum):
    # OF COURSE, IN PRACTICE, MEMBER NAMES SHOULD BE IN UPPERCASE
    PI = cmath.pi
    E = cmath.e


class UnderAppreciatedConstant(Enum):
    TAU = cmath.tau


print(f"Hi! I'm the famous {GloballyFamousConstant.E}!")
print(f"Hi! My name is {UnderAppreciatedConstant.TAU}")

print(f'{"e" in globals() = }')

Prints:

Hi! I'm the famous E!
Hi! My name is UnderAppreciatedConstant.TAU
"E" in globals() = True

Although the repr of the members show their names, they behave like their mixin parent's value if derived from a mixin under almost all circumstances. They are essentially equal to their value. If needed, the exact underlying value can be queried using the value attribute, like normal enums.

## # IGNORE: start #
## class Complex:
##     def __init__(self, real=0, imag=1):
##         self.real, self.imag = real, imag
## 
##     def __rpow__(self, other, /):
##         val = other ** complex(self.real, self.imag)
##         return Complex(val.real, val.imag)
## 
##     def __mul__(self, other, /):
##         val = complex(self.real, self.imag) ** other
##         return int(val.real)
## 
## 
## i = Complex(real=0, imag=1)
## # IGNORE: end #

from enum import global_enum, Enum
import cmath


@global_enum
class GloballyFamousConstant(float, Enum):
    # OF COURSE, IN PRACTICE, MEMBER NAMES SHOULD BE IN UPPERCASE
    π = cmath.pi
    e = cmath.e


print(f"{e ** i*π + 1 = }")
print(f"Where {e}={e.value}, {π}={π.value}")

# prints:
# e ** i*π + 1 = 0
# Where e=2.718281828459045, π=3.141592653589793

NOTE:

At the time of writing this (23rd Jul, 2024), no type checkers/linters support this feature as per my knowledge.

Koumis answered 23/7 at 10:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.