Python'da dizeyi Enum'a dönüştürme


142

Bir dizeyi Python'un Enum sınıfına dönüştürmenin (seriyi kaldırmanın) doğru yolunun ne olduğunu merak ediyorum. getattr(YourEnumType, str)İşi yapıyor gibi görünüyor , ama yeterince güvenli olup olmadığından emin değilim.

Daha spesifik olmak gerekirse, bir 'debug'dizeyi şöyle bir Enum nesnesine dönüştürmek istiyorum:

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

Yanıtlar:


214

Bu işlevsellik zaten Enum [1] 'de yerleşiktir:

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

[1] Resmi belgeler: Enum programmatic access


6
Girdinin sterilize edilmesi gerektiğinde bir geri dönüş değerine ne dersiniz? Bu türden bir şey Build.get('illegal', Build.debug)mi?
Hetzroni

1
@Hetzroni: Enumbir .get()yöntemle gelmez , ancak gerektiğinde bir tane ekleyebilir veya sadece bir temel Enumsınıf oluşturabilir ve her zaman bundan miras alabilirsiniz.
Ethan Furman

@Hetzroni: "Af isteyin, izin istemeyin" ilkesine göre, varsayılanı geri döndürmek için erişimi her zaman bir try / exclude KeyError cümlesiyle kaplayabilirsiniz (ve Ethan'ın da bahsettiği gibi, isteğe bağlı olarak bunu kendi işlevinize / yönteminize tamamlayabilirsiniz) .
Laogeodritt

1
Mansiyon ödülü Build('debug')
Dragonborn

2
@Dragonborn Aramak işe yaramaz Build('debug'). Sınıf yapıcı almalıdır değeri , yani 200ya 400bu örnekte. İsmi geçmek için cevabın zaten söylediği gibi köşeli parantez kullanmalısınız.
Arthur Tacca

17

Başka bir alternatif (Dizeleriniz sizin numaralandırma durumlarda 1-1 eşleşmeyebileceğinden özellikle yararlı) bir eklemektir staticmethodsizin için Enum, örneğin:

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

O zaman yapabilirsin question_type = QuestionType.from_str('singleSelect')


1
Çok ilgili, eğer kendinizi sık sık bunu yaparken bulursanız: pydantic-docs.helpmanual.io
driftcatcher

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)

Veya dizeyi bilinen Enum'a dönüştürmeniz mi gerekiyor ?

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

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

Veya:

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

Demek istediğim, bir debugdizeyi aşağıdaki gibi bir python class BuildType(Enum): debug = 200 release = 400
enum'a

Harika ipuçları! İle __dict__aynı mı kullanıyor getattr? Dahili Python öznitelikleriyle ad çakışmaları konusunda endişeleniyorum ....
Vladius

Oh ... evet aynı getattr. İsim çatışmaları için bir sebep göremiyorum. Anahtar kelimeyi sınıf alanı olarak ayarlayamazsınız.
ADR

4

Soruna Java benzeri çözümüm. Umarım birine yardımcı olur ...

    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 yanıtında bir iyileştirme:

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

Bunun python 3.6'da çalışmadığını bildirmek istiyorum.

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

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

Verileri bunun gibi bir demet olarak vermeniz gerekecek

MyEnum(('aaa',))

DÜZENLEME: Bunun yanlış olduğu ortaya çıkıyor. Bir yorumcunun hatamı işaret ettiği için krediler


Python 3.6.6 kullanarak bu davranışı yeniden oluşturamadım. Sanırım test yaparken bir hata yapmış olabilirsiniz (Bunu kontrol ederken ilk defa yaptığımı biliyorum). ,Her öğenin arkasına yanlışlıkla bir (virgül) koyarsanız (öğeler bir listeymiş gibi), her öğeyi bir demet olarak ele alır. (yani a = 'aaa',aslında aynıdır a = ('aaa',))
Multihunter

Haklısın, kodumda farklı bir hata vardı. Bir şekilde ,değerleri bir şekilde
demetlere
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.