Why is the order of definition not available in python2?
In python3 this just works
>>> from enum import Enum
>>> class Animal(Enum):
... cat = [0]
... dog = {1}
But in python v2.7.6 it raises TypeError
because there is an unhandled exception when the metaclass bases try to invoke sort by value.
We can fix it like this:
>>> class Animal(Enum):
... __order__ = 'cat dog'
... cat = [0]
... dog = {1}
My question is, why is the order of definition not available in python2? I am guessing why the python2 version is not working, correct me if I am wrong here.
If we do the listing like this:
>>> class Animal(Enum):
... cat = {0, 1}
... dog = {1, 2}
... fish = {2, 0}
Will the ordering procedure be safe and clear? Or would it be unreliable, for example, dict
or set
iteration?
edit: with tracing
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
source to share
Enum uses a new metaclass feature: class namespace preparation , allowing the metaclass to specify an alternative namespace implementation with a __prepare__
hook. This is not possible in Python 2.
If a __prepare__
custom mapping object is returned, you can capture the order in which the class body is defined. See _EnumDict
implementation and EnumMeta.__prepare__
definition . An attribute _member_names
is a list, an ordered structure, and the names in the subclass Enum
are appended to it as defined.
In Python 2, you stick to the normal namespace dict
for class objects, which do not preserve the order of definition. So in your last example, the order of the attributes depends on the implementation details of the mapping object used. Without an attribute __order__
, it is enum34
sorted by value in Python 2, while in Python 2 it means that the order is arbitrary if the elements do not actually have a specific order. Your sets are not in a particular order because they are not strict subsets of each other:
>>> {0, 1} < {1, 2}
False
>>> {0, 1} > {1, 2}
False
therefore, the original ordering of the class namespace is used, which is arbitrary. If you enable hash randomization , you will see the order fluctuate:
$ 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])>]
Since I'm using Python 2.7.8, I haven't seen yours TypeError
; while the fix for issue 8743 did not allow collections.Set()
ABC to be used with objects set()
, the objects were set
not really ordered.
The fix for this issue is part of Python 2.7.8 and I personally consider the old behavior to be a bug; a NotImplemented
investigator should have been returned instead of raising an exception.
So, if you need to have an enum with a combination of types for values, you are stuck with an attribute __order__
until you can upgrade to 2.7.8. That a set
doesn't play well when the ordering of heterogeneous types is a pity, but hardly a mistake enum34
.
source to share