枚举在python中

重复:
什么是在Python中实现'枚举'的最佳方式?

什么是在python中进行枚举的公认方式?

例如,现在我正在写一个游戏,并希望能够“向上”,“向下”,“向左”和“向右”移动。 我使用的是字符串,因为我还没有弄清楚枚举在python中是如何工作的,所以我的逻辑充斥着这样的东西:

def move(self, direction):
    if direction == "up":
        # Do something

我想用诸如Directions.up东西替换"up"


class Directions:
    up = 0
    down = 1
    left = 2
    right =3

更新1 :Python 3.4将有一个内置的精心设计的枚举库。 这些值总是知道他们的名字和类型; 有一个整数兼容的模式,但推荐的新用途默认值是singleton,不等于任何其他对象。

更新2 :由于写这篇文章,我意识到对枚举的关键测试是序列化。 其他方面可以在以后重构,但是如果你的枚举进入文件/进入网络,问问自己,如果它被一个较旧/较新版本(可能支持不同的值集合)反序列化,会发生什么?...


如果你确定你需要枚举,其他人已经回答了如何去做。 但让我们看看你为什么要他们? 了解动机将有助于选择解决方案。

  • 原子值 - 在C中,小数字很容易传递,字符串不是。 在Python中,像“up”这样的字符串对于许多用途来说是非常好的。 而且,任何以数字结尾的解决方案对于调试都会更糟!

  • 有意义的价值观 - 在C语言中,你经常需要处理现有的幻数,并且只需要一些语法糖。 情况并非如此。 然而,还有其他有意义的信息可能需要与方向相关联,例如(dx,dy)矢量 - 更多关于下面的信息。

  • 类型检查 - 在C中,枚举有助于在编译时捕获无效值。 但是Python通常更喜欢牺牲编译器检查来减少输入。

  • 自省 (在C枚举中不存在) - 你想知道所有的有效值。

  • 完成 - 编辑可以显示可能的值并帮助您键入它们。
  • 字符串Redeemed(又名符号)

    因此,在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将使用符号。)

    命名空间字符串

    您可以将'up'优于0的想法与其他解决方案结合使用。

    如果你想纠正错误(在运行时):

    UP = 'up'
    ...
    RIGHT = 'right'
    

    如果你想坚持输入一个前缀来完成,把上面的内容放在一个类中:

    class Directions:
        UP = "up"
        ...
        RIGHT = "right"
    

    或者只是在一个单独的文件中,使其成为一个模块。

    一个模块允许懒惰的用户通过from directions import *来跳过前缀 - 无论你认为这是一个加号还是减号...(我个人会不愿意被迫输入Directions.UP如果我经常使用它)。

    具有功能的对象

    如果有与每个值相关的有用信息/功能会怎么样? “右”不只是4个任意值中的一个,它是X轴上的正方向!

    如果你在做什么, if是这样的:

    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
    

    请记住,在Python中,类也是对象(不同于任何其他对象),您可以比较它们: direction == Up

    通常情况下,实例可能更清晰,但如果枚举的概念具有某种层次关系,有时直接使用类对它们进行建模是非常好的。


    我给库格尔+1,但另一个更精简的选择是

    dirUp, dirDown, dirLeft, dirRight = range(4)
    
  • (一段时间过去了)
  • 所以我在想......我们在这里有一个明显的DRY违规行为,因为我们已经在LHS中指定了四个项目,然后再在RHS中指定四个项目。 如果我们将来添加项目会发生什么? 当其他人添加它们时会发生什么,也许他们比我们更sl?? 删除DRY违规的一个显而易见的方法是使用枚举列表来分配它们的值:

    >>> enums = ['dirUp', 'dirDown']
    >>> for v, k in enumerate(enums):
    ...     exec(k + '=' + str(v))
    ...     
    >>> print dirDown
    1
    >>> print dirUp
    0
    

    如果你可以用exec()这件事,那很好。 如果不是,则使用其他方法。 无论如何,这个当前的讨论都是学术的。 但是,这里仍然存在问题。 如果在整个源代码体系中使用枚举,并且其他程序员出现并在dirUpdirDown之间插入一个新值,该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
    >>> 
    

    请注意,我们在dirUpdirDown之间插入了一个新值,即dirLeft ,并且前两个的原始映射值没有变化。

    我可能会在我自己的代码中使用它。 感谢发布该问题的OP。

  • (更多的时间流逝)
  • 贝尼切尔尼亚夫斯基 - 帕斯金提出了一些非常好的评论:

  • Python的默认hash()在跨平台不稳定(对持久性应用程序很危险)
  • 碰撞的可能性始终存在。
  • 我倾向于同意这两个观察。 他的建议是使用字符串本身(我真的很喜欢使用这些值的自我记录行为)作为哈希,因此代码变为以下(注意我们使用集合而不是列表来强制唯一性):

    >>> items=('dirUp','dirDown','dirLeft','dirRight')
    >>> for i in items:
            exec('{}="{}"'.format(i,i))
    >>> dirDown
    'dirDown'
    

    将它们放在一个名称空间中也是微不足道的,以避免与其他代码发生冲突:

    >>> 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'
    
    链接地址: http://www.djcxy.com/p/91731.html

    上一篇: Enumerations in python

    下一篇: What's the common practice for enums in Python?