2017-05-09 13 views
3

整数値を持つ列挙型を作成しようとしていますが、値ごとに表示しやすい文字列を返すこともできます。私はちょうど文字列にdictマッピング値を定義し、次に__str__と文字列引数を使って静的なコンストラクタを実装することができると思っていましたが、そこに問題があります...整数値を使用するPython 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型のオブジェクトを返すようになっているようです。私は考えることができる

いくつかの回避策は、次のとおりです。

  1. Fingers._display_strings.valueとして辞書を参照します。 (ただし、Fingers.__display_stringsは有効なenum値になります)
  2. dictをクラス変数ではなくモジュール変数にする。
  3. __str__from_stringの関数でdictを複製します(恐らくそれを一連のif文に分割することもできます)。
  4. dictをクラス変数にするのではなく、静的メソッド_get_display_stringsを定義してdictを返すので、enum値にはなりません。

上記の初期コードと回避策1.は、基本となる整数値をdictキーとして使用することに注意してください。他のオプションはすべて、dict(またはifテスト)がクラス自体に直接ではなく別の場所に定義されている必要があるため、これらの値をクラス名で修飾する必要があります。たとえば、Fingers.THUMBなどを使用して列挙型オブジェクトを取得するか、またはFingers.THUMB.valueを使用して基礎となる整数値を取得できます(THUMBのみではありません)。下位の整数値をdictキーとして使用する場合は、それを使用してdictを検索し、ではなく[Fingers.THUMB.value]などのインデックスを付ける必要があります。

したがって、基本的な整数値を保持しながら、列挙型の文字列マッピングを実装するための最善の方法または最もPython的な方法は何ですか?

答えて

3

これはSTDLIB Enumで行われますが、aenum とはるかに簡単であることができます。

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_ STDLIB中)メソッド_missing_value_

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 

開示:私はPython stdlib Enumenum34 backport、およびAdvanced Enumeration (aenum)の著者です。

+0

ありがとうございました:)私は不思議だった、あなたはどのようにこの欠損値法を使用することになっていますか?私は 'MyEnum._missing_value _(" value ")'を行うことができますが、それが意図した使用方法であるかどうかはわかりません。 'MyEnum ['value']'を使うと例外が発生する – TomDT

+0

@TomDT:未知の値を調べると、 'Enum'機構は自動的に' _missing_value_'を呼び出します。追加の検索機能を提供するには、独自の '_missing_value_'メソッドを作成する必要があります。上記の例では、OPは 'Fingers( 'thumb')'を使って 'Fingers.THUMB'を取得したかったので、' Fingers.THUMB'は '1'なので、通常は動作しませんでした。 –

+0

Oohhh私は、括弧[]を使ってアクセスしていましたが、私は括弧を使うことになっていました。ありがとうございました ! – TomDT

0

整数と文字列の両方が意味があるので、私が思いついたもう一つの解決策は、次のように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.__name__ + ' has no value matching "' + s + '"') 

しかし、これはFingersオブジェクトののreprがタプルだけではなく、int型が表示され、完全なタプルはFingersオブジェクトだけでなく、int型を作成するために使用されなければならないことを意味しています。私。 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 

アンも、より複雑な問題を回避するには、カスタム__call__を実装するEnumのメタクラスをサブクラスが含まれます。 (少なくとも__repr__をオーバーライドすることは非常に簡単です!)この実装や単なるint列挙型との間に

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.__name__ + ' has no value matching "' + s + '"') 

    def __repr__(self): 
     return '<%s.%s %s>' % (self.__class__.__name__, self.name, self.value[0]) 

一つの違いは、同じ整数値を使用して値が異なる文字列(例えばINDEX = (2, 'index')POINTER = (2, 'pointer')が)として評価しないだろうということです同じFingerオブジェクトですが、プレーンなint EnumではFinger.POINTER is Finger.INDEXTrueと評価されます。

0

良い質問しかし、これは、Fingersオブジェクトのreprがintではなくタプルを表示し、intだけでなくFingersオブジェクトを作成するために完全なタプルを使用する必要があることを意味します。私。 f =指((1、 '親指'))はできますが、f =指(1)はできません。

1

多分私はここでのポイントをしないのですが、あなたは、Python 3.6で、その後

class Fingers(Enum): 
    THUMB = 1 
    INDEX = 2 
    MIDDLE = 3 
    RING = 4 
    PINKY = 5 

を定義した場合、あなたは私が何をしたいと思い

print (Fingers.THUMB.name.lower()) 

を行うことができます。

+0

私はここにある例では事故に過ぎません。私の意図した使用では、文字列は長いだけでなく、列挙型の名前の小文字バージョンです! –

+0

私はその点を逃した。 – BoarGules

関連する問題