我知道Python可以做枚举(很好),但它所做的枚举类型非常原始。例如,我可以这样做:
from enum import Enum
class Color(Enum):
BLACK = 'black'
WHITE = 'white'
print(Color.BLACK.value)
这很好但是如果我希望每种颜色都有名称和十六进制值呢?我有几个选项(比如使枚举值成为字典)但我更喜欢Java的枚举方式。在Java中,允许枚举包含类和类的字段和方法。所以通常当一种语言只支持上面例子中的简单枚举时,我会重构代码,类似于以下内容:
class Color(object):
BLACK = Color('black', '#000')
WHITE = Color('white', '#fff')
def __init__(self, name, hex):
self.name = name
self.hex = hex
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
现在我可以有多个值,自定义方法,并且可以按名称引用不同的字段,因为每种颜色都是一个对象。我已经用几种语言做了这个没有问题,但Python似乎抱怨“名称'颜色'没有定义”。我可以不在该类中创建类的实例吗?我的hacky解决方案是这样做:
class Color(object):
def __init__(self, name, hex):
self.name = name
self.hex = hex
Color.BLACK = Color('black', '#000')
Color.WHITE = Color('white', '#fff')
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
哪个工作得很好。不过,我的问题是为什么课堂内不允许这些字段?我可以添加一些东西或重组它以使其允许吗?提前感谢您的任何答案!
枚举直接支持此用例。该库的文档涵盖了Planet example in the examples section:
如果定义了
__new__()
或__init__()
,则枚举成员的值将传递给这些方法:>>> class Planet(Enum): ... MERCURY = (3.303e+23, 2.4397e6) ... # ... ... ... 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
因此,对于您的具体示例,只需定义一个__init__
方法:
from enum import Enum
class Color(Enum):
BLACK = ('black', '#000')
WHITE = ('white', '#fff')
def __init__(self, color_name, hex):
self.color_name = color_name
self.hex = hex
print(Color.BLACK.color_name + ' ' + Color.BLACK.hex)
我没有使用name
作为属性,因为这是一个保留属性(用于反映枚举值名称,这里是BLACK
和WHITE
):
>>> Color.BLACK
<Color.BLACK: ('black', '#000')>
>>> Color.BLACK.name
'BLACK'
>>> Color.BLACK.color_name
'black'
>>> Color.BLACK.hex
'#000'
您仍然可以使用name
覆盖@property
属性,但我不会偏离此处的标准。
我使用这种技术在我的Advent of Code day 22 solution中定义病毒状态,定义下一个状态名称和每个条目的方向更改。
使用元组值和@property
访问器为元组元素命名:
from enum import Enum
class Color(Enum):
BLACK = ('black', '#000')
WHITE = ('white', '#fff')
@property
def name(self):
return self.value[0]
@property
def hex(self):
return self.value[1]
print(Color.BLACK.name)
print(Color.BLACK.hex)
black
#000
至于为什么你的代码不起作用,Python类定义是必不可少的。在您尝试构建Color
实例时,Color
类尚不存在。
你在这里遇到“鸡和蛋”问题。因为如果构造一个类,Python必须将属性和函数的名称与属性和函数的值相关联。如果访问Color.abc
,则会查看是否找到相应的名称,并返回值/函数定义。
但现在有一个问题。如果你写:
class Foo(object):
bar = Foo()
为什么?那么为了构造class
,它首先必须构造属性。因此,它必须构建一个'bar'
条目,映射到Foo()
的结果,但我们正在构建Foo
,那么如果Foo
依赖于该构造,我们如何构建Foo
。我们不可以。在Java中,它更简单,因为类在编译时是在概念上构造的。
不过,我们有一些选择。
Color
class我们可以先构建Color
类,然后构建“猴子补丁”类:
class Color(object):
def __init__(self, name, hex):
self.name = name
self.hex = hex
Color.black = Color('black', '#000')
Color.white = Color('white', '#fff')
这里我们首先定义Color
类,然后将属性添加到Color
类。之后我们可以这样做,因为现在定义了对象。
Enum
objects我们还可以将值附加到Enum
对象:
from enum import Enum
class Color(Enum):
white = {'name': 'white', 'hex': '#fff'}
black = {'name': 'black', 'hex': '#000'}
@property
def name(self):
return self.value['name']
@property
def hex(self):
return self.value['hex']
我们可以为每个Enum
成员附加一个值。例如,我们将{'name': 'white', 'hex': '#fff'}
附加到white
。我们以后可以通过self.value
访问该值。所以现在我们可以通过定义访问字典的Color.white
键的属性函数def name(self):
来定义'name'
上的属性。
一个命名的元组解决方案怎么样?
from collections import namedtuple
color = namedtuple('Color', ['name', 'value']) # Add attributes as you please
class Color:
BLACK = color('black', '#000')
WHITE = color('white', '#fff')
print(Color.BLACK.name, Color.BLACK.value)
输出
黑#000
添加新的就像这样容易
Color.RED = color('red', '#ff0')
print(Color.RED.name, Color.RED.value)
红色#ff0
您可以通过使用元类来帮助构建Color
类来执行您想要的操作:
class ColorMeta(type):
def __new__(cls, class_name, parents, attrs):
NUL = object() # Sentinel.
meta_args = attrs.get('meta_args', NUL)
if meta_args is NUL:
meta_args = []
else:
del attrs['meta_args'] # Clean up so isn't part of class created.
clsobj = super().__new__(cls, class_name, parents, attrs)
for meta_arg in meta_args:
name, hex = meta_arg
color = clsobj(name, hex)
setattr(clsobj, name, color)
return clsobj
class Color(metaclass=ColorMeta):
meta_args = [('WHITE', '#fff'),
('BLACK', '#000'),]
def __init__(self, name, hex):
self.name = name
self.hex = hex
print(Color.WHITE.name + ' ' + Color.WHITE.hex)
print(Color.BLACK.name + ' ' + Color.BLACK.hex)
我认为user2357112
已经找到了你想要的答案,但是看看namedtuples
以及访问属性可能也是值得的。
命名元组:
from collections import namedtuple
Color = namedtuple('Color', 'name hex')
Black = Color(name='Black', hex='#000')
print(Black.hex)
print(Black.name)
>#000
>Black