python中的枚举
重复:python中的枚举,python,enums,Python,Enums,重复: python中公认的枚举方法是什么 例如,目前我正在编写一个游戏,希望能够移动“上”、“下”、“左”和“右”。我之所以使用字符串,是因为我还没有弄清楚枚举在python中是如何工作的,所以我的逻辑中充斥着这样的东西: def move(self, direction): if direction == "up": # Do something 我想把“up”替换为方向。up我给了Kugel+1,但另一个更精简的选项是 class Directions:
python中公认的枚举方法是什么 例如,目前我正在编写一个游戏,希望能够移动“上”、“下”、“左”和“右”。我之所以使用字符串,是因为我还没有弄清楚枚举在python中是如何工作的,所以我的逻辑中充斥着这样的东西:
def move(self, direction):
if direction == "up":
# Do something
我想把
“up”
替换为方向。up
我给了Kugel+1,但另一个更精简的选项是
class Directions:
up = 0
down = 1
left = 2
right =3
dirUp, dirDown, dirLeft, dirRight = range(4)
- (一些时间过去了)
>>> enums = ['dirUp', 'dirDown']
>>> for v, k in enumerate(enums):
... exec(k + '=' + str(v))
...
>>> print dirDown
1
>>> print dirUp
0
如果您能忍受使用exec()
进行此操作,则可以。如果没有,则使用另一种方法。无论如何,目前的讨论都是学术性的。然而,这里仍然存在一个问题。如果枚举在大量源代码中使用,而其他程序员出现并在dirUp
和dirDown
之间插入一个新值,该怎么办?这将导致痛苦,因为枚举名称和枚举本身之间的映射将是错误的。请记住,即使在最初的简单解决方案中,这仍然是一个问题
这里,我们有一个新颖的想法,即使用内置的hash()
函数将枚举值确定为int,并使用枚举本身的文本名称来确定哈希:
>>> for k in enums:
... exec(k + '=' + str(hash(k)))
...
>>> dirUp
-1147857581
>>> dirDown
453592598
>>> enums = ['dirUp', 'dirLeft', 'dirDown']
>>> for k in enums:
... exec(k + '=' + str(hash(k)))
...
>>> dirUp
-1147857581
>>> dirDown
453592598
>>> dirLeft
-300839747
>>>
请注意,我们在dirUp
和dirDown
之间插入了一个新值,即dirLeft
,前两个的原始映射值没有改变
我可能会在自己的代码中使用它。感谢OP发布的问题
- (再过一些时间)
- Python的默认值
在跨平台上不稳定(对于持久性应用程序来说是危险的)hash()
- 碰撞的可能性总是存在的
>>> class Direction():
for i in ('dirUp','dirDown','dirLeft','dirRight'):
exec('{}="{}"'.format(i,i))
>>> Direction.dirUp
'dirUp'
他提到的加密散列的长度可以在这里看到:
>>> from hashlib import md5
>>> crypthash = md5('dirDown'.encode('utf8'))
>>> crypthash.hexdigest()
'6a65fd3cd318166a1cc30b3e5e666d8f'
对象可以提供这样的命名空间:
>>> import collections
>>> dircoll=collections.namedtuple('directions', ('UP', 'DOWN', 'LEFT', 'RIGHT'))
>>> directions=dircoll(0,1,2,3)
>>> directions
directions(UP=0, DOWN=1, LEFT=2, RIGHT=3)
>>> directions.DOWN
1
>>>
如果您使用的是Python 2.6+,那么您可以使用。它们的优点是具有固定数量的属性,当您需要所有枚举值时,可以像元组一样使用它 为了更好地控制枚举值,您可以创建自己的枚举类
def enum(args, start=0):
class Enum(object):
__slots__ = args.split()
def __init__(self):
for i, key in enumerate(Enum.__slots__, start):
setattr(self, key, i)
return Enum()
>>> e_dir = enum('up down left right')
>>> e_dir.up
0
>>> e_dir = enum('up down left right', start=1)
>>> e_dir.up
1
声明\uuuuu slots\uuuuu
密封您的枚举类,无法为从具有\uuuuu slots\uuuu
属性的类创建的对象设置更多属性
您的
Enum
类也可以基于namedtuple
,在这种情况下,您还可以获得tuple的特性。请参见子类化namedtuple更新1:Python 3.4将有一个设计良好的内置接口。这些值总是知道它们的名称和类型;有一个整数兼容模式,但建议新使用的默认值为Singleton,不等于任何其他对象
更新2:写这篇文章后,我意识到枚举的关键测试是序列化。其他方面可以在以后重构,但是如果您的枚举进入文件/连接,请事先问问自己,如果它被旧/新版本(可能支持不同的值集)反序列化,会发生什么情况
如果您确定需要枚举,其他人已经回答了如何使用。
但让我们看看你为什么想要它们?了解动机将有助于选择解决方案
- 原子值-在C语言中,小数字很容易传递,字符串则不然。
在Python中,像“up”这样的字符串非常适合多种用途。
此外,任何最终只有一个数字的解决方案对调试来说都是糟糕的
- 有意义的值-在C语言中,您经常需要处理现有的魔法
数字,只是想要一些语法糖。这里不是这样。
但是,您可能还需要关联其他有意义的信息
方向,例如(dx,dy)矢量-更多信息如下
- 类型检查-在C语言中,枚举有助于在编译时捕获无效值。
但是Python通常更喜欢牺牲编译器检查来减少类型
- 内省(在C枚举中不存在)-您想知道所有有效值
- 完成-编辑器可以显示可能的值并帮助您键入
已赎回的字符串(又名符号)
因此,在Pythonic解决方案的光明面上,只需使用字符串,并且可能有一个包含所有有效值的列表/集合:
DIRECTIONS = set(['up', 'down', 'left', 'right'])
def move(self, direction):
# only if you feel like checking
assert direction in DIRECTIONS
# you can still just use the strings!
if direction == 'up':
# Do something
请注意,调试器会告诉您,函数是以“up”作为参数调用的。任何direction
实际上是0
的解决方案都比这糟糕得多
在LISP语言家族中,这种用法被称为符号——原子对象与数字一样容易使用,但带有文本值。(准确地说,符号类似字符串,只是一种单独的类型。然而,Python通常使用常规字符串,而LISP则使用符号。)
名称空间字符串
您可以将
DIRECTIONS = set(['up', 'down', 'left', 'right'])
def move(self, direction):
# only if you feel like checking
assert direction in DIRECTIONS
# you can still just use the strings!
if direction == 'up':
# Do something
UP = 'up'
...
RIGHT = 'right'
class Directions:
UP = "up"
...
RIGHT = "right"
def move(self, direction):
if direction == 'up':
self.y += STEP
elif direction == 'down':
self.y -= STEP
elif direction == 'left':
self.x -= STEP
elif direction == 'right':
self.x += STEP
def move(self, direction):
self.x += direction.dx * STEP
self.y += direction.dy * STEP
# Written in full to give the idea.
# Consider using collections.namedtuple
class Direction(object):
def __init__(self, dx, dy, name):
self.dx = dx
self.dy = dy
self.name = name
def __str__(self):
return self.name
UP = Direction(0, 1, "up")
DOWN = Direction(0, -1, "down")
LEFT = Direction(-1, 0, "left")
RIGHT = Direction(1, 0, "right")
class Direction(object):
pass
class Up(Direction):
dx = 0
dy = 1
...
class Right(Direction):
dx = 1
dy = 0
class Enum(object):
def __init__(self, *keys):
self.__dict__.update(zip(keys, range(len(keys))))
>>> x = Enum('foo', 'bar', 'baz', 'bat')
>>> x.baz
2
>>> x.bat
3