python - 为什么定义顺序在 python2 中不可用?

标签 python enums

在 python3 中这很有效

>>> from enum import Enum
>>> class Animal(Enum):
...     cat = [0]
...     dog = {1}

但在 python v2.7.6 中,它引发了 TypeError,因为当元类基尝试对值调用 sorted 时存在未处理的异常。

我们可以这样修复:

>>> class Animal(Enum):
...     __order__ = 'cat dog'
...     cat = [0]
...     dog = {1}

我的问题:为什么定义顺序在 python2 中不可用?我假设这就是 python2 版本不起作用的原因,如果我在这里错了,请纠正我。

如果我们像这样进行枚举:

>>> class Animal(Enum):
...     cat = {0, 1}
...     dog = {1, 2}
...     fish = {2, 0}

排序是否安全且定义明确?或者它会不会像 dictset 迭代那样不可靠?


编辑:带回溯

In [1]: from enum import Enum

In [2]: class Animal(Enum):
    dog = [0]
    cat = {1}
   ...:     
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-d14b1041d5bc> in <module>()
----> 1 class Animal(Enum):
      2     dog = [0]
      3     cat = {1}
      4 

/usr/local/lib/python2.7/dist-packages/enum/__init__.pyc in __new__(metacls, cls, bases, classdict)
    164         if __order__ is None:
    165             if pyver < 3.0:
--> 166                 __order__ = [name for (name, value) in sorted(members.items(), key=lambda item: item[1])]
    167             else:
    168                 __order__ = classdict._member_names

TypeError: Error when calling the metaclass bases
    can only compare to a set

最佳答案

Enum 使用了一个新的元类特性:preparing the class namespace ,允许元类使用 with __prepare__ Hook 指定替代命名空间实现。这在 Python 2 中不可用。

通过让 __prepare__ 返回自定义映射对象,您可以捕获类主体的定义顺序。查看_EnumDict implementationEnumMeta.__prepare__ definition . _member_names 属性是一个列表,一个有序结构,Enum 子类中的名称在定义时添加到其中。

在 Python 2 中,您只能使用类主体的普通 dict 命名空间,它不保留定义顺序。因此,您的最后一个示例的属性顺序取决于所使用的映射对象的实现细节。如果没有 __order__ 属性,在 Python 2 enum34 中按值排序,在 Python 2 中这意味着如果项目实际上没有定义的顺序,则顺序是任意的。您的集合没有定义的顺序,因为它们不是彼此的严格子集:

>>> {0, 1} < {1, 2}
False
>>> {0, 1} > {1, 2}
False

所以使用原来的类命名空间顺序,这是任意的。如果你打开 hash randomisation你会看到顺序波动:

$ bin/python -R -c $'from enum import Enum\nclass Animal(Enum):\n    cat = {0, 1}\n    dog = {1, 2}\n    fish = {2, 0}\n\nprint list(Animal)\n'
[<Animal.dog: set([1, 2])>, <Animal.cat: set([0, 1])>, <Animal.fish: set([0, 2])>]
$ bin/python -R -c $'from enum import Enum\nclass Animal(Enum):\n    cat = {0, 1}\n    dog = {1, 2}\n    fish = {2, 0}\n\nprint list(Animal)\n'
[<Animal.fish: set([0, 2])>, <Animal.cat: set([0, 1])>, <Animal.dog: set([1, 2])>]
$ bin/python -R -c $'from enum import Enum\nclass Animal(Enum):\n    cat = {0, 1}\n    dog = {1, 2}\n    fish = {2, 0}\n\nprint list(Animal)\n'
[<Animal.fish: set([0, 2])>, <Animal.dog: set([1, 2])>, <Animal.cat: set([0, 1])>]

因为我使用的是 Python 2.7.8,所以我没有看到你的 TypeError;直到修复 issue 8743使使用 collections.Set() ABC 成为可能对于 set() 对象,set 对象确实不可订购。

该问题的修复是 Python 2.7.8 的一部分,我个人认为旧行为是一个错误;应该返回一个 NotImplemented 标记而不是引发异常。

因此,如果您必须拥有一个包含值的混合类型的枚举,那么在升级到 2.7.8 之前,您将一直使用 __order__ 属性.很遗憾 set 在对异构类型进行排序时表现不佳,但这不是 enum34 的错。

关于python - 为什么定义顺序在 python2 中不可用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26037123/

相关文章:

c# - 向枚举类型变量添加多个值

python - 为什么从 Sublime Text 2 调用 Matplotlib 不行?

python - Pandas 将 float 转换为没有小数的字符串

python - 查找两个数据帧的具有最大匹配条目/值数量的列

swift - 将 Objective-C (#define) 宏转换为 Swift

asp.net-core - 使用 AspNet [FromQuery] 模型绑定(bind)中的 EnumMember 值反序列化枚举

c# - 如何从枚举中获取字符串值

python 萨克斯错误 "junk after document element"

Python:如何提取 "data-bind"html 元素?

postgresql - 我如何在 postgresql 中查询枚举的值