是否可以在 Enum 中定义类常量? [英] Is it possible to define a class constant inside an Enum?

查看:26
本文介绍了是否可以在 Enum 中定义类常量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python 3.4 引入了一个新模块 enum,它添加了一个 <语言的 href="http://en.wikipedia.org/wiki/Enumerated_type">枚举类型.enum.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)... def __init__(self, mass, radius):... self.mass = 质量 # 公斤... self.radius = 半径 # 以米为单位... @财产...定义表面重力(自我):... # 万有引力常数 (m3 kg-1 s-2)... G = 6.67300E-11...返回 G * self.mass/(self.radius * self.radius)...>>>行星.地球.价值(5.976e+24, 6378140.0)>>>Planet.EARTH.surface_gravity9.802652743337129

这个例子还演示了 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 的作者Enumenum34 向后移植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 and descriptors (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 constants 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆