为什么python2中的定义顺序不可用?

wim*_*wim 3 python enums

在python3中这只是起作用

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

但是在python v2.7.6中它会引发TypeError,因为当元类库尝试对值进行排序时会有未处理的异常.

我们可以这样解决:

>>> class Animal(Enum):
...     __order__ = 'cat dog'
...     cat = [0]
...     dog = {1}
Run Code Online (Sandbox Code Playgroud)

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

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

>>> class Animal(Enum):
...     cat = {0, 1}
...     dog = {1, 2}
...     fish = {2, 0}
Run Code Online (Sandbox Code Playgroud)

订购是否安全且定义明确?或者它是不可靠的,像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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ers 6

Enum使用了一个新的元类功能:准备类名称空间,允许元类使用with __prepare__钩子指定备用名称空间实现.这在Python 2中不可用.

通过__prepare__返回自定义映射对象,您可以捕获类主体的定义顺序.请参阅_EnumDict实现EnumMeta.__prepare__定义.该_member_names属性是一个列表,一个有序结构,Enum子类中的名称在定义时添加到它中.

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

>>> {0, 1} < {1, 2}
False
>>> {0, 1} > {1, 2}
False
Run Code Online (Sandbox Code Playgroud)

所以使用了原始的类命名空间顺序,这是任意的.如果你打开哈希随机化,你会看到订单波动:

$ 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])>]
Run Code Online (Sandbox Code Playgroud)

当我使用Python 2.7.8时,我没有看到你的TypeError; 直到问题8743的修复使得可以将collections.Set()ABCset()对象一起使用,set对象确实不可订购.

该问题的修复是Python 2.7.8的一部分,我个人认为旧的行为是一个bug; NotImplemented应该返回一个哨兵,而不是提出异常.

因此,如果您必须为值添加各种类型的枚举,那么您将坚持使用该__order__属性,直到您可以升级到2.7.8.set在订购异类型时,a 不能很好用,这很可惜,但几乎没有enum34错.