私は整数値を持つ列挙型を作成しようとしていますが、各値に対して表示に適した文字列を返すこともできます。文字列に値をマッピングするdictを定義してから、__str__
と文字列引数を持つ静的コンストラクタを実装できると思っていましたが、それに問題があります...
(異なる状況下では、このEnumの基になるデータ型を整数ではなく文字列にすることができましたが、これはenumデータベーステーブルのマッピングとして使用されているため、整数値と文字列の両方が意味があります。前者主キーである。)
from enum import Enum
class Fingers(Enum):
THUMB = 1
INDEX = 2
MIDDLE = 3
RING = 4
PINKY = 5
_display_strings = {
THUMB: "thumb",
INDEX: "index",
MIDDLE: "middle",
RING: "ring",
PINKY: "pinky"
}
def __str__(self):
return self._display_strings[self.value]
@classmethod
def from_string(cls, str1):
for val, str2 in cls._display_strings.items():
if str1 == str2:
return cls(val)
raise ValueError(cls.__name__ + ' has no value matching "' + str1 + '"')
文字列に変換すると、次のエラーが発生します。
>>> str(Fingers.RING)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
str(Fingers.RING)
File "D:/src/Hacks/PythonEnums/fingers1.py", line 19, in __str__
return self._display_strings[self.value]
TypeError: 'Fingers' object is not subscriptable
問題は、Enumがすべてのクラス変数をenum値として使用することで、基になる型ではなくEnum型のオブジェクトを返すように見えることです。
考えられるいくつかの回避策は次のとおりです。
Fingers._display_strings.value
として参照します。 (ただし、Fingers.__display_strings
は有効な列挙値になります!)__str__
関数とfrom_string
関数で、dictを複製します(おそらく、一連のif
ステートメントに分解します)。_get_display_strings
を定義してdictを返すようにします。これにより、列挙型の値にはなりません。上記の初期コードと回避策1.
は、基になる整数値をdictキーとして使用することに注意してください。他のすべてのオプションでは、dict(またはif
テスト)がクラス自体の直接以外の場所で定義されている必要があるため、これらの値をクラス名で修飾する必要があります。したがって、たとえば、Fingers.THUMB
を使用してenumオブジェクトを取得するか、Fingers.THUMB.value
を使用して基礎となる整数値を取得するだけで、THUMB
を使用することはできません。基になる整数値をdictキーとして使用する場合は、その値を使用してdictを検索し、[Fingers.THUMB.value]
だけでなく、たとえば[Fingers.THUMB]
でインデックス付けする必要があります。
したがって、問題は、基になる整数値を保持しながら、Enumの文字列マッピングを実装するための最良または最もPython的な方法は何ですか?
これはstdlib Enum
で実行できますが、 aenum
ではるかに簡単です1:
from aenum import Enum
class Fingers(Enum):
_init_ = 'value string'
THUMB = 1, 'two thumbs'
INDEX = 2, 'offset location'
MIDDLE = 3, 'average is not median'
RING = 4, 'round or finger'
PINKY = 5, 'wee wee wee'
def __str__(self):
return self.string
文字列値を介して検索できるようにしたい場合は、新しいクラスメソッド_missing_value_
を実装します(stdlibで_missing_
のみ):
from aenum import Enum
class Fingers(Enum):
_init_ = 'value string'
THUMB = 1, 'two thumbs'
INDEX = 2, 'offset location'
MIDDLE = 3, 'average is not median'
RING = 4, 'round or finger'
PINKY = 5, 'wee wee wee'
def __str__(self):
return self.string
@classmethod
def _missing_value_(cls, value):
for member in cls:
if member.string == value:
return member
1 開示:私は Python stdlib Enum
、 enum34
backport 、および Advanced Enumeration(aenum
) ライブラリ。
多分私はここでポイントを逃していますが、あなたが定義した場合
class Fingers(Enum):
THUMB = 1
INDEX = 2
MIDDLE = 3
RING = 4
PINKY = 5
次にPython 3.6で行うことができます
print (Fingers.THUMB.name.lower())
あなたが望んでいるものだと思います。
私が思いついた別の解決策は、整数と文字列の両方が意味があるため、次のようにEnum値を_(int, str)
_タプルにすることです。
_from enum import Enum
class Fingers(Enum):
THUMB = (1, 'thumb')
INDEX = (2, 'index')
MIDDLE = (3, 'middle')
RING = (4, 'ring')
PINKY = (5, 'pinky')
def __str__(self):
return self.value[1]
@classmethod
def from_string(cls, s):
for finger in cls:
if finger.value[1] == s:
return finger
raise ValueError(cls.__+ ' has no value matching "' + s + '"')
_
ただし、これは、Fingers
オブジェクトのreprがintだけではなくTupleを表示することを意味します。Fingers
オブジェクトを作成するには、intだけでなく、完全なTupleを使用する必要があります。つまりf = Fingers((1, 'thumb'))
はできますが、f = Fingers(1)
はできません。
_>>> Fingers.THUMB
<Fingers.THUMB: (1, 'thumb')>
>>> Fingers((1,'thumb'))
<Fingers.THUMB: (1, 'thumb')>
>>> Fingers(1)
Traceback (most recent call last):
File "<pyshell#25>", line 1, in <module>
Fingers(1)
File "C:\Python\Python35\lib\enum.py", line 241, in __call__
return cls.__new__(cls, value)
File "C:\Python\Python35\lib\enum.py", line 476, in __new__
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 1 is not a valid Fingers
_
そのためのさらに複雑な回避策は、Enum
のメタクラスをサブクラス化してカスタム___call__
_を実装することです。 (少なくとも___repr__
_のオーバーライドははるかに簡単です!)
_from enum import Enum, EnumMeta
class IntStrTupleEnumMeta(EnumMeta):
def __call__(cls, value, names=None, *args, **kwargs):
if names is None and isinstance(value, int):
for e in cls:
if e.value[0] == value:
return e
return super().__call__(value, names, **kwargs)
class IntStrTupleEnum(Enum, metaclass=IntStrTupleEnumMeta):
pass
class Fingers(IntStrTupleEnum):
THUMB = (1, 'thumb')
INDEX = (2, 'index')
MIDDLE = (3, 'middle')
RING = (4, 'ring')
PINKY = (5, 'pinky')
def __str__(self):
return self.value[1]
@classmethod
def from_string(cls, s):
for finger in cls:
if finger.value[1] == s:
return finger
raise ValueError(cls.__+ ' has no value matching "' + s + '"')
def __repr__(self):
return '<%s.%s %s>' % (self.__class__.__name__, self.name, self.value[0])
_
この実装と通常のint Enumの違いの1つは、整数値は同じだが文字列が異なる値(たとえば、INDEX = (2, 'index')
とPOINTER = (2, 'pointer')
)は、同じFinger
オブジェクト、プレーンなEnumの場合、_Finger.POINTER is Finger.INDEX
_はTrue
と評価されます。
これはOPが要求したものではありませんが、値がintかどうかを気にしない場合の1つの優れたオプションです。人間が読める文字列として値を使用できます。
ソース: https://docs.python.org/3/library/enum.html
値の省略多くのユースケースでは、列挙の実際の値が何であるかを気にしません。このタイプの単純な列挙を定義するには、いくつかの方法があります。
値としてautoのインスタンスを使用する値としてオブジェクトのインスタンスを使用する値として説明文字列を使用する値としてタプルを使用するカスタムnew( )タプルをint値で置き換えるこれらのメソッドのいずれかを使用すると、これらの値が重要ではないことをユーザーに示し、残りのメンバーに番号を付け直さなくてもメンバーを追加、削除、または並べ替えることができます。
どちらの方法を選択する場合でも、(重要でない)値を非表示にするrepr()を提供する必要があります。
class NoValue(Enum):
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
説明的な文字列の使用値として文字列を使用すると、次のようになります。
class Color(NoValue):
RED = 'stop'
GREEN = 'go'
BLUE = 'too fast!'
Color.BLUE
<Color.BLUE>
Color.BLUE.value
'too fast!'
GUIコンボボックス(PyGTK内)の表示文字列が必要なのと同じ問題がありました。私はソリューションのPythonicity(それは一言ですか?)について知りませんが、これを使用しました:
from enum import IntEnum
class Finger(IntEnum):
THUMB = 1
INDEX = 2
MIDDLE = 3
RING = 4
PINKY = 5
@classmethod
def names(cls):
return ["Thumb", "Index", "Middle", "Ring", "Pinky"]
@classmethod
def tostr(cls, value):
return cls.names()[value - cls.THUMB]
@classmethod
def toint(cls, s):
return cls.names().index(s) + cls.THUMB
あなたのコードからそれらを使用する:
>>> print(Finger.INDEX)
Finger.INDEX
>>> print(Finger.tostr(Finger.THUMB))
Thumb
>>> print(Finger.toint("Ring"))
4
良い質問しかし、これはFingersオブジェクトのreprがintだけでなくTupleを表示することを意味し、intだけでなく、完全なTupleを使用してFingersオブジェクトを作成する必要があります。つまりできるよ
f = Fingers((1, 'thumb'))
だがしかし
f = Fingers(1)
列挙型が整数でなければならないのはなぜですか?表示する文字列を含め、何でもかまいません。
THUMB = 'Thumb'
INDEX = 'Index'
MIDDLE = 'Middle'
RING = 'Ring'
PINKY = 'Pinky'
タイプに関係なく、比較などを行うことができます。それらについて算術演算を行わないでください。