Python中的枚举类型无法按预期工作

11

我在Python的枚举类(Enum class)中发现了一种非常奇怪的行为。因此,枚举类型很简单:

from enum import Enum
Analysis = Enum('Analysis', 'static dynamic')

因此,我在步骤对象中使用了这个枚举类型,以便将其存储在属性分析中,如下所示:

class Step:
    def __init__(self):
        self.analysis = None
        self.bcs = []

到目前为止非常简单,所以当我在列表中有几个这些步骤时,然后我尝试查看枚举类型并且它已经被正确分配。但它们不相等:

# loop over steps
for s, step in enumerate(kwargs['steps']):
    print(kwargs)
    print(step)
    print(step.analysis)
    print("test for equality: ",(step.analysis == Analysis.static))
    quit()

打印

{'mesh': <fem.mesh.mesh.Mesh object at 0x10614d438>,
 'steps': [<hybrida.fem.step.Step object at 0x10614d278>,
           <hybrida.fem.step.Step object at 0x10616a710>,
           <hybrida.fem.step.Step object at 0x10616a390>]}
Step:
  analysis: Analysis.static
  bcs: [<hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a0f0>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a320>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a3c8>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a470>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a518>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a5c0>,
        <hybrida.fem.conditions.dirichlet.Dirichlet object at 0x10616a668>]
Analysis.static
test for equality:  False

这不正确,但我不知道如何调试。

更新

根据 @martineau 的建议,我创建了一个 IntEnum,问题得到解决。但我仍然不明白为什么正常的 Enum 不能工作。


如果我按照你建议的方式去做,会出现“AttributeError: static”错误。请参考这篇文章 - aaragon
不同的枚举包有哪些?我在帖子中提到了我使用的包。 - aaragon
那么你如何设置 step.analysis?请在你的问题中添加相关代码。 - Lukas Graf
@LukasGraf 在读取文件时,我将其设置为:self.steps[-1].analysis = Analysis.static - aaragon
所以你总是在self.steps列表的最后一步上设置属性 - 这是你想要的吗? - Lukas Graf
显示剩余3条评论
3个回答

20

在评论中,你说:

输入文件包含许多步骤,每次我添加一个新步骤时,都必须设置分析类型。

如果我理解正确的话,你是说每次添加一个新步骤时,都会创建一个新的Enum对象。这可能是为什么你看到你的“错误”的原因。尽管两个不同的Enum对象具有相同的名称和顺序,但其值不一定相等。例如:

import enum
Analysis1 = enum.Enum("Analysis", "static dynamic")
Analysis2 = enum.Enum("Analysis", "static dynamic")

但是:

>>> Analysis1.static == Analysis2.static
False
这是因为据我所知,对于Enum对象,等号运算符未定义,因此使用检查id的默认行为。正如@martineau在评论中建议的那样,避免这个问题的一种方法是使用IntEnum类型,它是int的子类,因此根据Enum的值而不是id定义了等号运算符。
import enum
Analysis1 = enum.IntEnum("Analysis", "static dynamic")
Analysis2 = enum.IntEnum("Analysis", "static dynamic")

那么:

>>> Analysis1.static == Analysis2.static
True

为什么需要EnumIntEnum

乍一看,似乎IntEnum总是我们想要的。那么Enum有什么用呢?

假设您想枚举两组项目,比如水果和颜色。现在,“橙色”既是一种水果,又是一种颜色。所以我们编写:

Fruits = enum.IntEnum("Fruits", "orange apple lemon")
Colors = enum.IntEnum("Colors", "orange red blue")

但是现在:

>>> Fruits.orange == Colors.orange
True

但是,从哲学上讲,“橙色”(水果)和“橙色”(颜色)是不同的!我们难道不能区分这两者吗?在这里,IntEnumint 进行子类化反而对我们产生了影响,因为 Fruits.orangeColors.orange 都等于 1。当然,正如我们在上面所看到的,Enum 的比较是通过比较 id 而不是值进行的。由于 Fruits.orangeColors.orange 是独特的对象,它们不会被比作相等:

Fruits = enum.Enum("Fruits", "orange apple lemon")
Colors = enum.Enum("Colors", "orange red blue")

所以:

>>> Fruits.orange == Colors.orange
False

现在我们生活的世界已不再是你可以在本地杂货店的蔬果区里找到的一些颜色。


@aargon 我敢打赌这仍然是正在发生的事情。如果您检查step.analysisAnalysis.staticid,我猜您会发现它们是不同的。也就是说,它们是两个不同的对象。您是否正在比较已解封的Enum - jme
你说得对!我没有使用未解封的枚举。我在赋值后立即检查了id:print(id(self.steps[-1].analysis)); print(id(Analysis.static)),并且它给出了相同的数字:4329054048。稍后我做了同样的事情:print(id(step.analysis)); print(id(Analysis.static)),第二个id是不同的:4393709864。为什么会发生这种情况?比较枚举的整数值的整个重点是能够执行哪些比较(来自C++世界)。 - aaragon
@aargon:你可以尝试使用从int派生的IntEnum类来避免这个问题。 - martineau
这解决了我的问题,但并没有修复这个问题。这是Enum Python类中的一个错误吗? - aaragon
目前还没有放在GitHub上。在我的definitions.py文件中,我只有Analysis = Enum('Analysis', 'static dynamic')。然后我只需在代码中调用Analysis.static来分配它(在步骤属性analysis的情况下),然后在执行step.analysis == Analysis.static进行比较时进行比较。可能会调用Analysis.static创建另一个对象吗? - aaragon
显示剩余7条评论

13

如果其他人在我们之后遇到相同的问题,请注意,我们经历过同样的问题。我们成功地将其追溯到不经意地混合使用绝对导入和相对导入,类似于下面描述的情况。

# File: package/module/analysis_types.py
Analysis = enum.Enum("Analysis", "static dynamic")
# File: package/module/static_thing.py
from .analysis_types import Analysis

class StaticThing:
    ...
    analysis = Analysis.static
    ...
# File: package/module/static_thing_test.py
from package.module.static_thing import StaticThing
from .analysis_types import Analysis

# This throws an AssertionError because as
#  id(StaticThing.analysis) != id(Analysis.static)
assert StaticThing.analysis == Analysis.static

以下更改使得预期行为得以恢复:

# File: package/module/static_thing_test.py
from .static_thing import StaticThing
from .analysis_types import Analysis

# This does NOT throw an AssertionError because as
#  id(StaticThing.analysis) == id(Analysis.static)
assert StaticThing.analysis == Analysis.static

1
刚好来到这里。为什么 Python 枚举测试是 false?它是同一个枚举!啊哈……太令人困惑了! - MajesticRa
3
肯定是导入加载/重新加载问题。我遇到过这种情况,我的特定问题不在测试中,而是在使用了%autoreload魔法的笔记本中。解决方案是停止使用该魔法。 - Bryan Dannowitz
这个解决方案是正确的。混合使用相对和绝对导入也给我带来了这个问题。 - undefined

0

对于那些像Austin Basye所描述的情况卡住了,无法通过改变导入来解决问题的人,请尝试使用枚举值。

也就是说,如果obj1.type1 == obj2.type1为False(但它应该是True),请检查是否obj1.type1.value == obj2.type2.value有效。

在这种情况下,Analysis1.static.value == Analysis2.static.value应该始终返回正确的值。


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