2014-09-25 2 views
3

В Python3 это просто работаетПочему порядок определения не доступен в python2?

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

Но питона 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 Итерация?


редактировать: с TRACEBACK

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

Какую версию 'enum' вы используете с Python 2? Пакет '' enum34' backport package (https://pypi.python.org/pypi/enum34/) не вызывает 'TypeError' в вашем первом примере. Здесь что-то не хватает? –

+0

Это как раз enum34 backport, версия 'enum34 == 1.0' в соответствии с замораживанием пипетки. И да, это поднимает исключение на python 2.7.6, вы говорите, что у вас нет ?! – wim

+0

Возможно, вы используете другую реализацию Martijn .. – wim

ответ

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, и я лично считаю, что старое поведение является ошибкой; a NotImplemented контролер должен был быть возвращен вместо того, чтобы возбуждать исключение.

Так что, если у вас есть иметь перечисление с соединением типов для значений, вы застряли с атрибутом __order__, пока вы не можете обновить до 2.7.8.То, что set не играет хорошо при заказе гетерогенных типов, очень жаль, но вряд ли enum34.

+0

Не имеем ли мы теоретически доступ к объекту кода в python2, чтобы восстановить порядок определения с помощью интроспекции? – wim

+1

@wim: даже получить исходный объект кода, который определяет класс, будет сложным, так как от него не требуется функция. Сначала вам нужно будет загружать исходный байт-код модуля, что означает, что вы должны полагаться на возможность доступа к этим данным в первую очередь. Это требует поддержки архивов с заархивированными модулями и т. Д. Это зависит от боли, которую вам придется пройти, для сопоставления порядка байт-кода 'STORE_FAST' с именами, которые они хранят, и т. Д. –

+0

@wim: а затем есть поддержка Jython, IronPython и PyPy, поэтому использование деталей реализации, таких как байт-код, сразу. –