私はpythonのenumライブラリをいじり回していて、難問に遭遇しました。ドキュメントでは、何かが定義されている auto-numbering enum の例を示しています。
class Color(AutoNumber):
red = ()
green = ()
...
同様のクラスを作成したいのですが、値はメンバーの名前から自動的に設定され、str
とenum
を実行することで得られる機能を維持します mixin stuff
だから次のようなもの:
class Animal(MagicStrEnum):
horse = ()
dog = ()
Animal.dog == 'dog' # True
Enumモジュールのソースコードを見て、__new__
とEnumMeta
クラスをいじくり回して多くのバリエーションを試しました
更新:2017-03-01
Python 3.6(および _
Aenum 2.0
_1)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
クラスの問題は、列挙型メンバーの名前がそれを作成するコードに渡されないため、使用できないことです。もう1つのしわがあるのは、str
が不変であるため、作成された列挙型を(たとえば クラスデコレータ を使用して)変更することはできません。
最も簡単な方法は 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 __hash__(self):
# keep DocEnum hashable
return hash(self._value_)
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
のクラス辞書beforeをいじるメンバーが作成されます:
_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
_
1 開示:私は Python stdlib Enum
、 _enum34
_ backport 、および Advanced Enumeration(aenum
) ライブラリ。
おそらくあなたは name
クラスによって自動的に提供されるEnum
属性を探しています
>>> class Animal(Enum):
... ant = 1
... bee = 2
... cat = 3
... dog = 4
...
>>> Animal.ant.name == "ant"
True
あなたが本当に自分を足で撃ちたいならば。そして、これはすべての落とし穴の世界をもたらすと確信しています(最も明白なものを排除しました)。
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)