Can named arguments be used with Python enums?
While you can't use named arguments the way you describe with enums, you can get a similar effect with a namedtuple
mixin:
from collections import namedtuplefrom enum import EnumBody = namedtuple("Body", ["mass", "radius"])class Planet(Body, Enum): MERCURY = Body(mass=3.303e+23, radius=2.4397e6) VENUS = Body(mass=4.869e+24, radius=6.0518e6) EARTH = Body(mass=5.976e+24, radius=3.3972e6) # ... etc.
... which to my mind is cleaner, since you don't have to write an __init__
method.
Example use:
>>> Planet.MERCURY<Planet.MERCURY: Body(mass=3.303e+23, radius=2439700.0)>>>> Planet.EARTH.mass5.976e+24>>> Planet.VENUS.radius6051800.0
Note that, as per the docs, "mix-in types must appear before Enum
itself in the sequence of bases".
The accepted answer by @zero-piraeus can be slightly extended to allow default arguments as well. This is very handy when you have a large enum with most entries having the same value for an element.
class Body(namedtuple('Body', "mass radius moons")): def __new__(cls, mass, radius, moons=0): return super().__new__(cls, mass, radius, moons) def __getnewargs__(self): return (self.mass, self.radius, self.moons)class Planet(Body, Enum): MERCURY = Body(mass=3.303e+23, radius=2.4397e6) VENUS = Body(mass=4.869e+24, radius=6.0518e6) EARTH = Body(5.976e+24, 3.3972e6, moons=1)
Beware pickling will not work without the __getnewargs__
.
class Foo: def __init__(self): self.planet = Planet.EARTH # pickle error in deepcopyfrom copy import deepcopyf1 = Foo()f2 = deepcopy(f1) # pickle error here
For Python 3.6.1+ the typing.NamedTuple can be used, which also allows for setting default values, which leads to prettier code. The example by @shao.lo then looks like this:
from enum import Enumfrom typing import NamedTupleclass Body(NamedTuple): mass: float radius: float moons: int=0class Planet(Body, Enum): MERCURY = Body(mass=3.303e+23, radius=2.4397e6) VENUS = Body(mass=4.869e+24, radius=6.0518e6) EARTH = Body(5.976e+24, 3.3972e6, moons=1)
This also supports pickling. The typing.Any can be used if you don't want to specify the type.
Credit to @monk-time, who's answer here inspired this solution.