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

      

+3


source to share


1 answer


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

.

+6


source







All Articles