为什么Python中的可变值枚举相同的对象?

时间:2016-11-16 19:59:25

标签: python python-3.x enums identity

在为Enum成员尝试不同的值类型时,我发现当值是可变的时会出现一些奇怪的行为。

如果我将Enum的值定义为不同的列表,则成员的行为仍然类似于Enum值是strint等典型不可变类型的时间,即使我可以更改成员的值,以便两个Enum成员的值相同:

>>> class Color(enum.Enum):
        black = [1,2]
        blue = [1,2,3]  

>>> Color.blue is Color.black
False
>>> Color.black == Color.blue
False
>>> Color.black.value.append(3)
>>> Color.black
<Color.black: [1, 2, 3]>
>>> Color.blue
<Color.blue: [1, 2, 3]>
>>> Color.blue == Color.black
False
>>> Color.black.value == Color.blue.value
True

但是,如果我将值定义为相同的列表,则每个成员的值似乎都是相同的对象,因此一个成员的值的任何突变都会影响所有成员:

>>> class Color(enum.Enum):
        black = [1,2,3]
        blue = [1,2,3]

>>> Color.blue is Color.black
True
>>> Color.black == Color.blue
True
>>> Color.black.value.append(4)
>>> Color.black
<Color.black: [1, 2, 3, 4]>
>>> Color.blue
<Color.black: [1, 2, 3, 4]>
>>> Color.blue == Color.black
True

Enum为何如此表现?这是预期的行为还是一个错误?

注意: 我不打算以这种方式实际使用Enums,我只是尝试使用Enum成员的非标准值

4 个答案:

答案 0 :(得分:10)

来自docs

  

给定两个成员A和B具有相同的值(并且A定义为第一个),B是A的别名.A和B的值的值按字母顺序查找将返回A.B的名字查找也将返回A:

>>> class Shape(Enum):
...     square = 2
...     diamond = 1
...     circle = 3
...     alias_for_square = 2
...
>>> Shape.square
<Shape.square: 2>
>>> Shape.alias_for_square
<Shape.square: 2>
>>> Shape(2)
<Shape.square: 2>

即使价值是可变的,这也是平等运作的。由于您为blackblue定义了相同的值,而black首先为blueblack是{{1}}的别名。

答案 1 :(得分:5)

要补充@user2357112's answer,请查看EnumMeta,所有Enum类的元类;它可以查看具有类型的每个类定义,并进行更改以更改它。

具体来说,它通过简单的分配来处理re-assign members with the same value in its __new__ method

    # If another member with the same value was already defined, the
    # new member becomes an alias to the existing one.
    for name, canonical_member in enum_class._member_map_.items():
        if canonical_member._value_ == enum_member._value_:
            enum_member = canonical_member
            break

我没有选择检查文档,而是查看了源代码。要接受的课程:始终检查文档,以及是否提出ExplanationNotFound;检查来源: - )

答案 2 :(得分:3)

Python 3 Enum类不会强制执行唯一性,除非您通过unique decorator

明确说明它

另见duplicate values。由于blueblack相同,因此它只会成为black的别名。

答案 3 :(得分:3)

From the Python documentation for Enums

  

默认情况下,枚举允许多个名称作为相同值的别名。如果不需要这种行为,可以使用以下装饰器来确保每个值在枚举中只使用一次....

这意味着blueblack的别名。当任何一个改变时,另一个也必须改变。

但是,您可以使用enum.unique装饰器强制Python使每个枚举值唯一。同样来自文档:

>>> from enum import Enum, unique
>>> @unique
 ... class Mistake(Enum):
 ...     one = 1
 ...     two = 2
 ...     three = 3
 ...     four = 3
 ...
 Traceback (most recent call last):
 ...
 ValueError: duplicate values found in <enum 'Mistake'>: four -> three