如何将字符串与Python枚举进行比较?

70

我刚刚发现了Python中存在一个Enum基类,并且正在试图想象如何将其用于我的项目。

假设我定义了一个交通灯状态:

from enum import Enum, auto

class Signal(Enum):
    red = auto()
    green = auto()
    orange = auto()

假设我从程序中的某个子系统接收到信息,以表示颜色名称的字符串的形式,例如brain_detected_colour = "red"

如何将此字符串与我的红绿灯信号进行比较?

显然,brain_detected_colour is Signal.redFalse,因为Signal.red不是一个字符串。

Signal(brain_detected_colour) is Signal.red会失败并显示ValueError: 'red'不是有效的Signal

4个回答

106

不能创建一个枚举实例。 使用Signal(foo)语法通过值来访问枚举成员,这种语法不适用于auto()

但是可以使用字符串像在dict中访问值一样,使用方括号访问枚举成员:

Signal[brain_detected_colour] is Signal.red

另一个可能性是将字符串与枚举成员的name进行比较:

# Bad practice:
brain_detected_colour is Signal.red.name

但在这里,我们并不是测试枚举成员之间的身份,而是比较字符串,所以最好使用等式测试:

# Better practice:
brain_detected_colour == Signal.red.name

(通过字符串驻留,字符串之间的身份比较得以实现,但最好不要依赖它。感谢@mwchase和@Chris_Rands让我意识到这一点。)

另一个可能性是在创建枚举时将成员值显式设置为它们的名称:

class Signal(Enum):
    red = "red"
    green = "green"
    orange = "orange"

(查看此答案以获得自动化此操作的方法。)

然后,Signal(brain_detected_colour) is Signal.red 将是有效的。


18
brain_detected_colour is Signal.red.name中使用is是有风险的;最好使用== - mwchase
2
@mwchase 你可以解释一下为什么吗?这样我就可以编辑我的答案并加上说明了。 - bli
11
你正在依赖字符串驻留,这是一种模糊的实现细节http://guilload.com/python-string-interning/,除非你真正需要比较对象的标识,否则永远不要使用`is`。 - Chris_Rands
2
解决了我的问题。虽然我无法使用 Signal(brain_detected_colour) is Signal.red(答案末尾提到,用于构建带有字符串值的枚举时)...但是 Signal[brain_detected_colour] == Signal.red 是有效的。请注意,我考虑了使用“==”进行比较更少风险的评论。 - Sander Vanden Hautte
是的,当我将枚举值与字符串进行比较时,遇到了同样的问题,尽管它们具有相同的字符串值,但使用 "is" 无法解决问题,使用 == 解决了我的问题。 - MissSergeivna
为了避免其他人搜索,注意a is bid(a) == id(b)的语法糖。 - Hugh Perkins

79

更好的做法是继承str类来定义Signal

class Signal(str, Enum):
    red = 'red'
    green = 'green'
    orange = 'orange'

brain_detected_colour = 'red'
brain_detected_colour == Signal.red  # direct comparison

2
你能在答案中解释一下,为什么这是更好的做法吗? - Martin Grey
10
如果您仍然对为什么最好使用mixin str感兴趣,可以参考这个链接:https://docs.python.org/3/library/enum.html#others - Jorge Limas
2
这对我有用。@MartinGrey,可能是因为你正在比较字符串类型和枚举类型。当str在枚举类中不存在时,我无法将字符串与枚举进行比较。(我猜仅仅分配字符串值是不够的。) - omer
在比较时,难道不应该使用.value(例如Signal.red.value)吗? - Olimjon Ibragimov
4
当您使用普通的 Enum 时,需要这样做。但是当您混合使用 str 时,例如:class Signal(str, Enum):,则不需要这样做。 - Mushif Ali Nawaz

34

可以通过将auto()返回枚举成员的名称作为其值(在自动值部分1中有介绍)来实现:

class AutoName(Enum):
    def _generate_next_value_(name, start, count, last_values):
        return name

class Ordinal(AutoName):
    NORTH = auto()
    SOUTH = auto()
    EAST = auto()
    WEST = auto()

并且正在使用中:

>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

1 这需要 Python 3.6 版本或 aenum 2.0 版本2aenum 可以与旧版本的 Python,如 2.7 一起使用)。

2 声明:我是 Python 标准库中 Enumenum34 后移版高级枚举 (aenum) 库的作者。


5
不幸的是,以下断言失败:assert Ordinal.NORTH == "NORTH",因此您无法直接与字符串比较。 编辑:哦,可以通过使用 class Ordinal(str, AutoName) 来实现。 - tiho
你介意把代码编辑一下,让它可以复制粘贴吗? - Guimoute

2
class Signal(Enum):
    red = auto()
    green = auto()
    orange = auto()

    def equals(self, string):
       return self.name == string

brain_detected_colour = "red"

if Signal.red.equals(brain_detected_colour):
   #something awesome

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接