2015-08-25 5 views
10

Я уже возился с библиотекой enum python и столкнулся с головоломкой. В документации, они показывают пример auto-numbering enum, в котором что-то определяется:Автоматическая установка значения элемента перечисления на его имя

class Color(AutoNumber): 
    red =() 
    green =() 
    ... 

Я хочу сделать подобный класс, но значение будет автоматически устанавливается от имени элемента и сохранить функциональность, вы получите от выполнения str и enummixin stuff

Так что-то вроде:

class Animal(MagicStrEnum): 
    horse =() 
    dog =() 

Animal.dog == 'dog' # True 

Я смотрел на исходный код модуля перечислений и пробовал много вариаций м эссинга вокруг с __new__ и EnumMeta класс

+1

Вы имеете в виду 'Animal.dog.value == 'dog''? –

+0

related: http://www.acooke.org/cute/Pythonssad0.html – wim

+0

Я думаю, что это можно сделать, изменив EnumMeta._create_() в enum.py (https://hg.python.org/cpython/file /3.4/Lib/enum.py#l295, однако это не может быть немедленно отменено из-за ValueError на отложенных именах, поступающих от _is_sunder() для защиты кода. Однако, если имена = ['red', 'green', 'blue' ] и Color = Enum ('Color', names = zip (имена, имена)), затем Color.red.value == 'red', Color.green.value == 'green' и Color.blue.value == 'blue'. –

ответ

0

Возможно, вы ищете атрибут name который автоматически предоставляемой Enum класса

>>> class Animal(Enum): 
...  ant = 1 
...  bee = 2 
...  cat = 3 
...  dog = 4 
... 

>>> Animal.ant.name == "ant" 
True 

Хотя, если вы действительно хотите, чтобы выстрелить себе в ногу. И я уверен, что это представит целый мир gotchas (я устранил самый очевидный).

from enum import Enum, EnumMeta, _EnumDict 

class AutoStrEnumDict(_EnumDict): 
    def __setitem__(self, key, value): 
     super().__setitem__(key, key) 

class AutoStrEnumMeta(EnumMeta): 
    @classmethod 
    def __prepare__(metacls, cls, bases): 
     return AutoStrEnumDict() 
    def __init__(self, name, bases, attrs): 
     super().__init__(name, bases, attrs) 
     # override Enum.__str__ 
     # can't put these on the class directly otherwise EnumMeta overwrites them 
     # should also consider resetting __repr__, __format__ and __reduce_ex__ 
     if self.__str__ is not str.__str__: 
      self.__str__ = str.__str__ 

class AutoStrNameEnum(str, Enum, metaclass=AutoStrEnumMeta): 
    pass 

class Animal(AutoStrNameEnum): 
    horse =() 
    dog =() 

print(Animal.horse) 
assert Animal.horse == "horse" 
assert str(Animal.horse) == "horse" 
# and not equal to "Animal.horse" (the gotcha mentioned earlier) 
+0

Ваша модификация '_EnumDict' не позволяет добавлять методы, так как они также будут преобразованы в' str'. –

+1

Легко обойти. Либо проверьте, Значение - это функция. Или, чтобы сделать ее более надежной, создайте дозорное значение и делайте только преобразование, когда значение является дозорным. – Dunes

6

Обновление: 2017-03-01

В Python 3.6 (и Aenum 2.0) Flag и IntFlag классы были добавлены; часть того, что было новым auto() helper, что делает это тривиально проста:

>>> class AutoName(Enum): 
...  def _generate_next_value_(name, start, count, last_values): 
...   return name 
... 
>>> class Ordinal(AutoName): 
...  NORTH = auto() 
...  SOUTH = auto() 
...  EAST = auto() 
...  WEST = auto() 
... 
>>> list(Ordinal) 
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>] 

Оригинальный ответ

Трудность с AutoStr класса является то, что имя члена перечисления не передается в код, который его создает, поэтому он недоступен для использования. Другая морщина заключается в том, что str является неизменяемым, поэтому мы не можем изменить эти типы перечислений после их создания (например, используя class decorator).

Проще всего сделать, это использовать Functional API:

Animal = Enum('Animal', [(a, a) for a in ('horse', 'dog')], type=str) 

, который дает нам:

>>> list(Animal) 
[<Animal.horse: 'horse'>, <Animal.dog: 'dog'>] 

>>> Animal.dog == 'dog' 
True 

Следующая проще всего сделать, если вы хотите, чтобы сделать базовый класс для вашего будущего использования перечислений, будет что-то вроде моего DocEnem:

class DocEnum(Enum): 
    """ 
    compares equal to all cased versions of its name 
    accepts a doctring for each member 
    """ 
    def __new__(cls, *args): 
     """Ignores arguments (will be handled in __init__)""" 
     obj = object.__new__(cls) 
     obj._value_ = None 
     return obj 

    def __init__(self, doc=None): 
     # first, fix _value_ 
     self._value_ = self._name_.lower() 
     self.__doc__ = doc 

    def __eq__(self, other): 
     if isinstance(other, basestring): 
      return self._value_ == other.lower() 
     elif not isinstance(other, self.__class__): 
      return NotImplemented 
     return self is other 

    def __ne__(self, other): 
     return not self == other 

и в использовании:

class SpecKind(DocEnum): 
    REQUIRED = "required value" 
    OPTION = "single value per name" 
    MULTI = "multiple values per name (list form)" 
    FLAG = "boolean value per name" 
    KEYWORD = 'unknown options' 

Обратите внимание, что в отличие от первого варианта, DocEnum члены неstr s.


Если вы хотите сделать это трудный путь: подкласс EnumMeta и скрипку с новым Enum «ы класс словаря созданы до члены:

from enum import EnumMeta, Enum, _EnumDict 

class StrEnumMeta(EnumMeta): 
    def __new__(metacls, cls, bases, oldclassdict): 
     """ 
     Scan through `oldclassdict` and convert any value that is a plain tuple 
     into a `str` of the name instead 
     """ 
     newclassdict = _EnumDict() 
     for k, v in oldclassdict.items(): 
      if v ==(): 
       v = k 
      newclassdict[k] = v 
     return super().__new__(metacls, cls, bases, newclassdict) 

class AutoStrEnum(str, Enum, metaclass=StrEnumMeta): 
    "base class for name=value str enums" 

class Animal(AutoStrEnum): 
    horse =() 
    dog =() 
    whale =() 

print(Animal.horse) 
print(Animal.horse == 'horse') 
print(Animal.horse.name, Animal.horse.value) 

Который дает нам:

Animal.horse 
True 
horse horse 

Раскрытие информации: Я являюсь автором Python stdlib Enum, enum34 backport и библиотеки Advanced Enumeration (aenum).

+0

@VillasV: Thank s для обнаружения этой ошибки! Мне жаль, что рецензенты не видели, что это было правильное редактирование. –