在python3中,这只是工作
>>> from enum import Enum
>>> class Animal(Enum):
... cat = [0]
... dog = {1}
但是在python v2.7.6中,它会引发TypeError
,因为当元类库尝试调用对值进行排序时会有未处理的异常。
我们可以这样解决:
>>> class Animal(Enum):
... __order__ = 'cat dog'
... cat = [0]
... dog = {1}
我的问题:为什么python2中的定义顺序不可用?我假设这就是python2版本不起作用的原因,如果我在这里错了,请纠正我。
如果我们进行这样的枚举:
>>> class Animal(Enum):
... cat = {0, 1}
... dog = {1, 2}
... fish = {2, 0}
订购是否安全且定义明确?或者它是否不可靠,如dict
或set
迭代?
编辑:使用追溯
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
答案 0 :(得分:6)
Enum使用新的元类功能:preparing the class namespace,允许元类指定带有__prepare__
钩子的备用名称空间实现。这在Python 2中不可用。
通过让__prepare__
返回自定义映射对象,您可以捕获类主体的定义顺序。请参阅_EnumDict
implementation和EnumMeta.__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的一部分,我个人认为旧的行为是一个bug;应该返回一个NotImplemented
哨兵,而不是提出异常。
因此,如果你让有一个包含值的混合类型的枚举,你就会被__order__
属性所困,直到你可以升级到2.7.8。在排序异类型时,set
不能很好用,这很可惜,但很难enum34
的错误。