Is it possible to define a class constant inside an Enum?
python 3.4引入了一个新的模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | >>> 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 |
这个例子还演示了
如果类想要在其他方法中使用这个常量,那么它也必须在那里定义,这显然是不理想的。
在
这是高级行为,在创建的90%以上的枚举中不需要这种行为。
根据文件:
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).
因此,如果你想要一个类常量,你有几个选择:
- 在
__init__ 中创建 - 创建类后添加它
- 使用混音
- 创建自己的
descriptor
在
在适当的时候当然可以使用mixin(参见dnozay的回答中的一个好例子),但是这种情况也可以通过使用一个基本的
首先,将在下面的示例中使用的常量:
1 2 3 4 5 6 7 | 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) |
以及一次性枚举示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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 |
最后,多用途枚举示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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 |
注:
1 | Planet.G = 1 |
如果您真的需要它是常量(即不可重新绑定),那么使用新的aenum库[1]来阻止重新分配
1公开:我是python stdlib
最优雅的解决方案(imho)是使用mixins/基类来提供正确的行为。
- 提供所有实现所需的行为的基类,如
Satellite 和Planet 。 - 如果您决定提供可选的行为(例如,
Satellite 和Planet 可能必须提供不同的行为),则mixin很有趣。
下面是一个例子,您首先定义自己的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # # 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) |
然后用正确的基类/混合函数创建您的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # # 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) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | from enum import Enum class classproperty(object): """A class property decorator""" def __init__(self, getter): self.getter = getter def __get__(self, instance, owner): return self.getter(owner) class classconstant(object): """A constant property from given value, visible in class and instances""" def __init__(self, value): self.value = value def __get__(self, instance, owner): return self.value class strictclassconstant(classconstant): """A constant property that is callable only from the class""" def __get__(self, instance, owner): if instance: raise AttributeError( "Strict class constants are not available in instances") return self.value 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 G = classconstant(6.67300E-11) @property def surface_gravity(self): # universal gravitational constant (m3 kg-1 s-2) return Planet.G * self.mass / (self.radius * self.radius) print(Planet.MERCURY.surface_gravity) print(Planet.G) print(Planet.MERCURY.G) class ConstantExample(Enum): HAM = 1 SPAM = 2 @classproperty def c1(cls): return 1 c2 = classconstant(2) c3 = strictclassconstant(3) print(ConstantExample.c1, ConstantExample.HAM.c1) print(ConstantExample.c2, ConstantExample.SPAM.c2) print(ConstantExample.c3) # This should fail: print(ConstantExample.HAM.c3) |
@property不起作用,classconstant起作用的原因很简单,并在这里的答案中进行了解释。
The reason that the actual property object is returned when you access
it via a class Hello.foo lies in how the property implements the
__get__(self, instance, owner) special method. If a descriptor is accessed on an instance, then that instance is passed as the
appropriate argument, and owner is the class of that instance.On the other hand, if it is accessed through the class, then instance
is None and only owner is passed. The property object recognizes this
and returns self.
因此,
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Planet(Enum): # ... @property def G(self): return 6.67300E-11 # ... @property def surface_gravity(self): return self.G * self.mass / (self.radius * self.radius) |
如果您想定义大量常量,这会有点笨拙,因此可以在类外部定义一个助手函数:
1 2 3 | def constant(c): """Return a class property that returns `c`.""" return property(lambda self: c) |
…使用方法如下:
1 2 3 4 5 | class Planet(Enum): # ... G = constant(6.67300E-11) |
这种方法的一个局限性是,它只适用于类的实例,而不适用于类本身:
1 2 3 4 | >>> Planet.EARTH.G 6.673e-11 >>> Planet.G <property object at 0x7f665921ce58> |
tldr;不,它不能在枚举类内完成。
这也就是说,如其他答案所示,有一些方法可以获得与枚举关联的类拥有的值(即通过类继承/混合),但这些值没有"定义"。在枚举中"。