Pythonで文字列を列挙に変換する


141

文字列をPythonのEnumクラスに変換(逆シリアル化)する正しい方法は何でしょうか。getattr(YourEnumType, str)仕事はしているようですが、それが十分に安全かどうかはわかりません。

より具体的には、次のように'debug'文字列をEnumオブジェクトに変換したいと思います。

class BuildType(Enum):
    debug = 200
    release = 400

回答:


213

この機能はすでにEnum [1]に組み込まれています。

>>> from enum import Enum
>>> class Build(Enum):
...   debug = 200
...   build = 400
... 
>>> Build['debug']
<Build.debug: 200>

[1]公式ドキュメント: Enum programmatic access


6
入力をサニタイズする必要がある場合のフォールバック値はどうですか?ある種のBuild.get('illegal', Build.debug)何か?
Hetzroni、2018

1
@Hetzroni: メソッドEnumは付属していませんが、.get()必要に応じて追加したり、基本Enumクラスを作成して常にそれから継承したりできます。
イーサンファーマン2018

@Hetzroni:「許可ではなく許しを求める」という原則に従って、常にtry / except KeyError句でアクセスを囲んでデフォルトを返すことができます(Ethanが述べたように、オプションでこれを独自の関数/メソッドでラップする) 。
Laogeodritt

1
名誉な言及 Build('debug')
Dragonborn

2
@Dragonbornを呼び出すことはできませんBuild('debug')。クラスのコンストラクタは取らなければならない値を、すなわち、200または400この例では。名前を渡すには、答えがすでに述べているように、角括弧を使用する必要があります。
アーサータッカ

17

別の代替方法(特に、文字列が列挙型のケースに1-1をマップしない場合に便利です)は、にを追加するstaticmethodことです。Enum例:

class QuestionType(enum.Enum):
    MULTI_SELECT = "multi"
    SINGLE_SELECT = "single"

    @staticmethod
    def from_str(label):
        if label in ('single', 'singleSelect'):
            return QuestionType.SINGLE_SELECT
        elif label in ('multi', 'multiSelect'):
            return QuestionType.MULTI_SELECT
        else:
            raise NotImplementedError

その後、あなたは行うことができます question_type = QuestionType.from_str('singleSelect')


1
非常に関連しています。これを頻繁に実行している場合:pydantic-docs.helpmanual.io
ドリフトキャッチャー

6
def custom_enum(typename, items_dict):
    class_definition = """
from enum import Enum

class {}(Enum):
    {}""".format(typename, '\n    '.join(['{} = {}'.format(k, v) for k, v in items_dict.items()]))

    namespace = dict(__name__='enum_%s' % typename)
    exec(class_definition, namespace)
    result = namespace[typename]
    result._source = class_definition
    return result

MyEnum = custom_enum('MyEnum', {'a': 123, 'b': 321})
print(MyEnum.a, MyEnum.b)

または、文字列を既知の列挙型に変換する必要がありますか?

class MyEnum(Enum):
    a = 'aaa'
    b = 123

print(MyEnum('aaa'), MyEnum(123))

または:

class BuildType(Enum):
    debug = 200
    release = 400

print(BuildType.__dict__['debug'])

print(eval('BuildType.debug'))
print(type(eval('BuildType.debug')))    
print(eval(BuildType.__name__ + '.debug'))  # for work with code refactoring

私は私が変換したいわけdebugなの列挙型に文字列を: python class BuildType(Enum): debug = 200 release = 400
Vladius

素晴らしいヒント!__dict__と同じを使用していgetattrますか?内部Python属性との名前の衝突について心配しています...
Vladius

ああ...はい、同じgetattrです。名前の衝突の理由はわかりません。クラスのフィールドとしてキーワードを設定することはできません。
ADR

4

問題に対する私のJavaのような解決策。それが誰かを助けることを願っています...

    from enum import Enum, auto


    class SignInMethod(Enum):
        EMAIL = auto(),
        GOOGLE = auto()

        @staticmethod
        def value_of(value) -> Enum:
            for m, mm in SignInMethod.__members__.items():
                if m == value.upper():
                    return mm


    sim = SignInMethod.value_of('EMAIL')
    print("""TEST
    1). {0}
    2). {1}
    3). {2}
    """.format(sim, sim.name, isinstance(sim, SignInMethod)))

2

@rogueleaderrの回答の改善:

class QuestionType(enum.Enum):
    MULTI_SELECT = "multi"
    SINGLE_SELECT = "single"

    @classmethod
    def from_str(cls, label):
        if label in ('single', 'singleSelect'):
            return cls.SINGLE_SELECT
        elif label in ('multi', 'multiSelect'):
            return cls.MULTI_SELECT
        else:
            raise NotImplementedError

-2

私はこれがpython 3.6では機能しないことを通知したいだけです

class MyEnum(Enum):
    a = 'aaa'
    b = 123

print(MyEnum('aaa'), MyEnum(123))

このようなタプルとしてデータを与える必要があります

MyEnum(('aaa',))

編集:これは誤りであることが判明しました。私の間違いを指摘したコメント投稿者の功績


Python 3.6.6を使用すると、この動作を再現できませんでした。テスト中に間違いを犯した可能性があります(これを確認したのは初めてだったと思います)。,(要素がリストであるかのように)誤って各要素の後に(カンマ)を置いた場合、それは各要素をタプルとして扱います。(つまりa = 'aaa',、実際にはと同じですa = ('aaa',)
Multihunter 2018年

あなたは正しい、それは私のコードの別のバグでした。どういうわけか,、値を何らかの方法でタプルに変換する列挙型を定義するときに、すべての行を
後回しに
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.