Обновление: 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
).
Вы имеете в виду 'Animal.dog.value == 'dog''? –
related: http://www.acooke.org/cute/Pythonssad0.html – wim
Я думаю, что это можно сделать, изменив 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'. –