Python 3.4 introduces a new module enum
, which adds an enumerated type to the language. The documentation for enum.Enum
provides an example to demonstrate how it can be extended:
>>> class Planet(Enum): ... MERCURY = (3.303e+23, 2.4397e6) ... VENUS = (4.869e+24, 6.0518e6) ... EARTH = (5.976e+24, 6.37814e6) ... MARS = (6.421e+23, 3.3972e6) ... JUPITER = (1.9e+27, 7.1492e7) ... SATURN = (5.688e+26, 6.0268e7) ... URANUS = (8.686e+25, 2.5559e7) ... NEPTUNE = (1.024e+26, 2.4746e7) ... def __init__(self, mass, radius): ... self.mass = mass # in kilograms ... self.radius = radius # in meters ... @property ... def surface_gravity(self): ... # universal gravitational constant (m3 kg-1 s-2) ... G = 6.67300E-11 ... return G * self.mass / (self.radius * self.radius) ... >>> Planet.EARTH.value (5.976e+24, 6378140.0) >>> Planet.EARTH.surface_gravity 9.802652743337129
This example also demonstrates a problem with Enum
: in the surface_gravity()
property method, a constant G
is defined which would normally be defined at class level - but attempting to do so inside an Enum
would simply add it as one of the members of the enum, so instead it's been defined inside the method.
If the class wanted to use this constant in other methods, it'd have to be defined there as well, which obviously isn't ideal.
Is there any way to define a class constant inside an Enum
, or some workaround to achieve the same effect?
An enum is a special class that represents a group of constants. To create an enum, use the enum keyword (instead of class or interface), and separate the constants with a comma. values() method can be used to return all values present inside enum.
Yes, we can define an enumeration inside a class. You can retrieve the values in an enumeration using the values() method.
Enums are lists of constants. When you need a predefined list of values which do represent some kind of numeric or textual data, you should use an enum. You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values.
Methods and variables in an enumeration Enumerations are similar to classes and, you can have variables, methods, and constructors within them. Only concrete methods are allowed in an enumeration.
This is advanced behavior which will not be needed in 90+% of the enumerations created.
According to the docs:
The rules for what is allowed are as follows:
_sunder_
names (starting and ending with a single underscore) are reserved by enum and cannot be used; all other attributes defined within an enumeration will become members of this enumeration, with the exception of__dunder__
names anddescriptors
(methods are also descriptors).
So if you want a class constant you have several choices:
__init__
descriptor
Creating the constant in __init__
and adding it after the class has been created both suffer from not having all the class info gathered in one place.
Mixins can certainly be used when appropriate (see dnozay's answer for a good example), but that case can also be simplified by having a base Enum
class with the actual constants built in.
First, the constant that will be used in the examples below:
class Constant: # use Constant(object) if in Python 2 def __init__(self, value): self.value = value def __get__(self, *args): return self.value def __repr__(self): return '%s(%r)' % (self.__class__.__name__, self.value)
And the single-use Enum example:
from enum import Enum class Planet(Enum): MERCURY = (3.303e+23, 2.4397e6) VENUS = (4.869e+24, 6.0518e6) EARTH = (5.976e+24, 6.37814e6) MARS = (6.421e+23, 3.3972e6) JUPITER = (1.9e+27, 7.1492e7) SATURN = (5.688e+26, 6.0268e7) URANUS = (8.686e+25, 2.5559e7) NEPTUNE = (1.024e+26, 2.4746e7) # universal gravitational constant G = Constant(6.67300E-11) def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters @property def surface_gravity(self): return self.G * self.mass / (self.radius * self.radius) print(Planet.__dict__['G']) # Constant(6.673e-11) print(Planet.G) # 6.673e-11 print(Planet.NEPTUNE.G) # 6.673e-11 print(Planet.SATURN.surface_gravity) # 10.44978014597121
And, finally, the multi-use Enum example:
from enum import Enum class AstronomicalObject(Enum): # universal gravitational constant G = Constant(6.67300E-11) def __init__(self, mass, radius): self.mass = mass self.radius = radius @property def surface_gravity(self): return self.G * self.mass / (self.radius * self.radius) class Planet(AstronomicalObject): MERCURY = (3.303e+23, 2.4397e6) VENUS = (4.869e+24, 6.0518e6) EARTH = (5.976e+24, 6.37814e6) MARS = (6.421e+23, 3.3972e6) JUPITER = (1.9e+27, 7.1492e7) SATURN = (5.688e+26, 6.0268e7) URANUS = (8.686e+25, 2.5559e7) NEPTUNE = (1.024e+26, 2.4746e7) class Asteroid(AstronomicalObject): CERES = (9.4e+20 , 4.75e+5) PALLAS = (2.068e+20, 2.72e+5) JUNOS = (2.82e+19, 2.29e+5) VESTA = (2.632e+20 ,2.62e+5 Planet.MERCURY.surface_gravity # 3.7030267229659395 Asteroid.CERES.surface_gravity # 0.27801085872576176
Note:
The Constant
G
really isn't. One could rebind G
to something else:
Planet.G = 1
If you really need it to be constant (aka not rebindable), then use the new aenum library [1] which will block attempts to reassign constant
s as well as Enum
members.
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With