Example:
class Planet(Enum):
MERCURY = (mass: 3.303e+23, radius: 2.4397e6)
def __init__(self, mass, radius):
self.mass = mass # in kilograms
self.radius = radius # in meters
Ref: https://docs.python.org/3/library/enum.html#planet
Why do I want to do this? If there are a few primitive types (int, bool) in the constructor list, it would be nice to used named arguments.
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 namedtuple
from enum import Enum
Body = 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.mass
5.976e+24
>>> Planet.VENUS.radius
6051800.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 deepcopy
from copy import deepcopy
f1 = Foo()
f2 = deepcopy(f1) # pickle error here
If going beyond the namedtuple
mix-in check out the aenum
library1. Besides having a few extra bells and whistles for Enum
it also supports NamedConstant
and a metaclass-based NamedTuple
.
Using aenum.Enum
the above code could look like:
from aenum import Enum, enum, _reduce_ex_by_name
class Planet(Enum, init='mass radius'):
MERCURY = enum(mass=3.303e+23, radius=2.4397e6)
VENUS = enum(mass=4.869e+24, radius=6.0518e6)
EARTH = enum(mass=5.976e+24, radius=3.3972e6)
# replace __reduce_ex__ so pickling works
__reduce_ex__ = _reduce_ex_by_name
and in use:
--> for p in Planet:
... print(repr(p))
<Planet.MERCURY: enum(radius=2439700.0, mass=3.3030000000000001e+23)>
<Planet.EARTH: enum(radius=3397200.0, mass=5.9760000000000004e+24)>
<Planet.VENUS: enum(radius=6051800.0, mass=4.8690000000000001e+24)>
--> print(Planet.VENUS.mass)
4.869e+24
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
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 Enum
from typing import NamedTuple
class Body(NamedTuple):
mass: float
radius: float
moons: int=0
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)
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.
来源:https://stackoverflow.com/questions/26691784/can-named-arguments-be-used-with-python-enums