Python 枚举可以使用命名参数吗?
示例:
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
参考链接:https://docs.python.org/3/library/enum.html#planet
我为什么想这么做呢?如果构造函数里有几个基本类型(比如整数int,布尔值bool),那么使用命名参数会更方便。
4 个回答
如果你想深入了解 namedtuple
之外的内容,可以看看 aenum
这个库。除了为 Enum
提供一些额外的功能外,它还支持 NamedConstant
和基于元类的 NamedTuple
。
使用 aenum.Enum
,上面的代码可能看起来像这样:
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
在实际使用中:
--> 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 说明:我就是 Python 标准库中的 Enum
、enum34
的回溯版本,以及 高级枚举库 (aenum
) 的作者。
对于Python 3.6.1及以上版本,可以使用typing.NamedTuple,这个工具可以让你设置默认值,从而使代码看起来更整洁。@shao.lo的例子就变成了这样:
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)
这个方法也支持序列化(pickling)。如果你不想指定具体类型,可以使用typing.Any。
感谢@monk-time,他的回答在这里启发了这个解决方案。
@zero-piraeus 提出的答案可以稍微扩展一下,让默认参数也能使用。这在你有一个很大的枚举(enum)时特别有用,因为大多数条目在某个元素上的值是相同的。
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)
需要注意的是,如果没有 __getnewargs__
,序列化(pickling)将无法正常工作。
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
虽然你不能像你描述的那样用枚举(enum)来使用命名参数,但你可以通过一个叫做 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.
我觉得这样更简洁,因为你不需要写一个 __init__
方法。
使用示例:
>>> Planet.MERCURY
<Planet.MERCURY: Body(mass=3.303e+23, radius=2439700.0)>
>>> Planet.EARTH.mass
5.976e+24
>>> Planet.VENUS.radius
6051800.0
需要注意的是,根据 文档 的说明,“混合类型必须在 Enum
本身之前出现在基类的顺序中”。