是否可以在 Enum 中定义类常量? [英] Is it possible to define a class constant inside an Enum?
问题描述
Python 3.4 引入了一个新模块 enum
,它添加了一个 <语言的 href="http://en.wikipedia.org/wiki/Enumerated_type">枚举类型.enum.Enum
的文档提供了一个例子来演示它是如何可以扩展:
这个例子还演示了 Enum
的一个问题:在 surface_gravity()
属性方法中,定义了一个常量 G
,它通常是在类级别定义 - 但尝试在 Enum
中这样做只会将其添加为枚举的成员之一,因此它是在方法内部定义的.
如果类想在其他方法中使用这个常量,它也必须在那里定义,这显然不理想.
有没有办法在 Enum
中定义一个类常量,或者一些解决方法来达到同样的效果?
这是高级行为,在创建的 90+% 的枚举中都不需要.
根据文档:
<块引用>允许的规则如下: _sunder_
名称(以单个下划线开头和结尾)由 enum 保留,不能使用;枚举中定义的所有其他属性都将成为该枚举的成员,除了 __dunder__
名称和 descriptors
(方法也是描述符).
所以如果你想要一个类常量,你有几个选择:
- 在
__init__
中创建它 - 在创建类后添加
- 使用混合
- 创建您自己的
描述符
在 __init__
中创建常量并在创建类后添加它都会遇到无法将所有类信息收集在一个地方的问题.
在适当的时候当然可以使用 Mixins(参见 dnozay 的答案以获得一个很好的例子),但这种情况也可以通过具有内置实际常量的基本 Enum
类来简化.
首先,将在以下示例中使用的常量:
class Constant: # use Constant(object) if in Python 2def __init__(self, value):self.value = 价值def __get__(self, *args):返回 self.valuedef __repr__(self):返回 '%s(%r)' % (self.__class__.__name__, self.value)
以及一次性使用的 Enum 示例:
from enum import Enum类行星(枚举):汞 = (3.303e+23, 2.4397e6)金星 = (4.869e+24, 6.0518e6)地球 = (5.976e+24, 6.37814e6)火星 = (6.421e+23, 3.3972e6)木星 = (1.9e+27, 7.1492e7)土星 = (5.688e+26, 6.0268e7)天王星 = (8.686e+25, 2.5559e7)海王星 = (1.024e+26, 2.4746e7)# 万有引力常数G = 常数(6.67300E-11)def __init__(自身,质量,半径):self.mass = 质量 # 公斤self.radius = 半径 # 以米为单位@财产defsurface_gravity(self):返回 self.G * self.mass/(self.radius * self.radius)打印(Planet.__dict__['G'])#常量(6.673e-11)打印(行星.G)# 6.673e-11打印(Planet.NEPTUNE.G)#6.673e-11打印(Planet.SATURN.surface_gravity)#10.44978014597121
最后,多用途枚举示例:
from enum import Enum类天文物体(枚举):# 万有引力常数G = 常数(6.67300E-11)def __init__(自身,质量,半径):self.mass = 质量self.radius = 半径@财产defsurface_gravity(self):返回 self.G * self.mass/(self.radius * self.radius)类行星(天文物体):汞 = (3.303e+23, 2.4397e6)金星 = (4.869e+24, 6.0518e6)地球 = (5.976e+24, 6.37814e6)火星 = (6.421e+23, 3.3972e6)木星 = (1.9e+27, 7.1492e7)土星 = (5.688e+26, 6.0268e7)天王星 = (8.686e+25, 2.5559e7)海王星 = (1.024e+26, 2.4746e7)类小行星(天文物体):CERES = (9.4e+20, 4.75e+5)帕拉斯 = (2.068e+20, 2.72e+5)朱诺斯 = (2.82e+19, 2.29e+5)VESTA = (2.632e+20 ,2.62e+5Planet.MERCURY.surface_gravity # 3.7030267229659395Asteroid.CERES.surface_gravity # 0.27801085872576176
<小时>
注意:
Constant
G
真的不是.可以将 G
重新绑定到其他东西:
Planet.G = 1
如果你真的需要它是恒定的(也就是不可重新绑定),那么使用 新的 aenum 库 [1] 将阻止尝试重新分配 constant
以及 Enum
成员.
1 披露:我是 Python stdlib 的作者Enum
,enum34
向后移植a> 和 高级枚举 (aenum
) 库.>
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?
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:
- create it in
__init__
- add it after the class has been created
- use a mixin
- create your own
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.
这篇关于是否可以在 Enum 中定义类常量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!