Python 3.4引入了一个新模块enum,该模块向该语言添加了枚举类型。的文档enum.Enum提供了一个示例来演示如何扩展它:
enum
enum.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) ... 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
此示例还演示了一个问题Enum:在surface_gravity()property方法中,G定义了一个常量,该常量通常在类级别定义- 但是尝试在a级别内执行此操作Enum只会将其添加为枚举的成员之一,因此,在方法内部定义。
Enum
surface_gravity()
G
如果该类想在其他方法中使用此常量,则也必须在其中定义它,这显然不是理想的选择。
有什么方法可以在中定义类常量Enum,也可以通过一些解决方法来达到相同的效果?
这是高级行为,在创建的90%以上的枚举中将不需要此行为。
根据文档:
允许的规则如下:_sunder_名称(以单个下划线开头和结尾)由枚举保留,不能使用;枚举中定义的所有其他属性都将成为该枚举的成员,但__dunder__名称和descriptors(方法也是描述符)除外。
_sunder_
__dunder__
descriptors
因此,如果您想要一个类常量,则有多种选择:
__init__
descriptor
在创建__init__类后在其中创建常量并将其添加都会使所有类信息都没有聚集在一个地方。
当然可以在适当的时候使用[Mixins]有关示例,请参见dnozay的答案,但是通过Enum内置具有实际常量的基类也可以简化这种情况。
首先,将在以下示例中使用的常量:
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)
和一次性Enum示例:
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
最后,多用途枚举示例:
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
注意事项 :
该Constant G还真不是。一个人可以重新绑定G到其他东西:
Constant
Planet.G = 1
如果确实需要将其保持不变(也就是不可重新绑定),请使用新的aenum库[1],该库将阻止尝试重新分配constants及其Enum成员。
constant
1披露:我是Python stdlibEnum,enum34backport和Advanced Enumeration(aenum) 库的作者。
enum34
aenum
最优雅的解决方案(IMHO)是使用mixins /基类提供正确的行为。
基类,以提供egSatellite和通用的所有实现所需的行为Planet。 如果您决定提供可选的行为(例如,Satellite并且Planet可能必须提供其他行为),mixin很有趣 这是一个示例,其中您首先定义行为:
egSatellite
Planet
Satellite
# # business as usual, define your class, methods, constants... # class AstronomicalObject: # universal gravitational constant G = 6.67300E-11 def __init__(self, mass, radius): self.mass = mass # in kilograms self.radius = radius # in meters class PlanetModel(AstronomicalObject): @property def surface_gravity(self): return self.G * self.mass / (self.radius * self.radius) class SatelliteModel(AstronomicalObject): FUEL_PRICE_PER_KG = 20000 @property def fuel_cost(self): return self.FUEL_PRICE_PER_KG * self.mass def falling_rate(self, destination): return complicated_formula(self.G, self.mass, destination) 然后Enum使用正确的基类/混合创建您的。
# # then create your Enum with the correct model. # class Planet(PlanetModel, 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) class Satellite(SatelliteModel, Enum): GPS1 = (12.0, 1.7) GPS2 = (22.0, 1.5)