แปลงสตริงเป็น Enum ใน Python


143

ฉันสงสัยว่าวิธีที่ถูกต้องในการแปลง (deserializing) สตริงเป็นคลาส Enum ของ Python คืออะไร ดูเหมือนgetattr(YourEnumType, str)จะได้ผล แต่ฉันไม่แน่ใจว่าปลอดภัยเพียงพอหรือไม่

เพื่อให้เจาะจงมากขึ้นฉันต้องการแปลง'debug'สตริงเป็นอ็อบเจ็กต์ Enum ดังนี้:

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

คำตอบ:


215

ฟังก์ชันนี้มีอยู่แล้วใน 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

1
@Hetzroni: Enumไม่ได้มาพร้อมกับ.get()วิธีการ แต่คุณสามารถเพิ่มได้ตามต้องการหรือเพียงแค่สร้างEnumคลาสพื้นฐานและสืบทอดจากสิ่งนั้นเสมอ
Ethan Furman

@Hetzroni: ตามหลักการ "ขอการให้อภัยไม่ใช่เพื่อขออนุญาต" คุณสามารถห่อการเข้าถึงได้ตลอดเวลาโดยใช้คำสั่ง try / ยกเว้น KeyError เพื่อส่งคืนค่าเริ่มต้น (และตามที่ Ethan กล่าวไว้คุณอาจรวมสิ่งนี้ไว้ในฟังก์ชัน / วิธีการของคุณเอง) .
Laogeodritt

1
รางวัลชมเชย Build('debug')
Dragonborn

2
@Dragonborn Build('debug')มันจะไม่ทำงานเพื่อโทร ตัวสร้างคลาสต้องรับค่าเช่น200หรือ400ในตัวอย่างนี้ ในการส่งชื่อคุณต้องใช้วงเล็บเหลี่ยมตามที่คำตอบได้บอกไว้แล้ว
Arthur Tacca

17

อีกทางเลือกหนึ่ง (มีประโยชน์อย่างยิ่งหากสตริงของคุณไม่จับคู่ 1-1 กับกรณี enum ของคุณ) คือการเพิ่ม a 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
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)

หรือคุณต้องการแปลงสตริงเป็น Enum ที่รู้จัก ?

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สตริงเป็น enum: 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

คุณพูดถูกมันเป็นข้อผิดพลาดที่แตกต่างกันในรหัสของฉัน ฉันคิดว่าคุณต้องวางไว้,ข้างหลังทุกบรรทัดในขณะที่กำหนด enum ซึ่งเปลี่ยนค่าเป็น tuples อย่างใด
Sstuber
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.