Chuyển đổi chuỗi thành Enum trong Python


141

Tôi tự hỏi đâu là cách chính xác để chuyển đổi (giải tuần tự hóa) một chuỗi thành lớp Enum của Python. Có vẻ như getattr(YourEnumType, str)công việc, nhưng tôi không chắc nó có đủ an toàn không.

Để cụ thể hơn, tôi muốn chuyển đổi một 'debug'chuỗi thành một đối tượng Enum như thế này:

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

Câu trả lời:


213

Chức năng này đã được tích hợp sẵn trong Enum [1]:

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

[1] Tài liệu chính thức: Enum programmatic access


6
Điều gì về một giá trị dự phòng trong trường hợp đầu vào cần phải được vệ sinh? Một cái gì đó trong loại Build.get('illegal', Build.debug)?
Hetzroni

1
@Hetzroni: Enumkhông đi kèm với một .get()phương thức, nhưng bạn có thể thêm một phương thức khi cần hoặc chỉ tạo một Enumlớp cơ sở và luôn kế thừa từ đó.
Ethan Furman

@Hetzroni: Theo nguyên tắc "yêu cầu sự tha thứ, không cho phép", bạn luôn có thể bao bọc quyền truy cập trong một mệnh đề thử / ngoại trừ KeyError để trả về mặc định (và như Ethan đã đề cập, tùy ý gói nó trong chức năng / phương thức của riêng bạn) .
Laogeodritt

1
Đề cập đáng Build('debug')
trân trọng

2
@Dragonborn Nó không hoạt động để gọi Build('debug'). Trình xây dựng lớp phải lấy giá trị , nghĩa là 200hoặc 400trong ví dụ này. Để vượt qua tên, bạn phải sử dụng dấu ngoặc vuông, như câu trả lời đã nói.
Arthur Tacca

17

Một cách khác (đặc biệt hữu ích nếu chuỗi của bạn không ánh xạ 1-1 đến các trường hợp enum của bạn) là thêm một chuỗi staticmethodvào Enum, ví dụ:

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

Sau đó bạn có thể làm question_type = QuestionType.from_str('singleSelect')


1
Rất liên quan, nếu bạn thấy mình làm điều này thường xuyên: 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)

Hoặc bạn cần chuyển đổi chuỗi thành Enum đã biết ?

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

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

Hoặc là:

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

Ý tôi là tôi muốn chuyển đổi một debugchuỗi thành một enum như sau: python class BuildType(Enum): debug = 200 release = 400
Vladius

Lời khuyên tuyệt vời! Là sử dụng __dict__giống như getattr? Tôi lo lắng về sự va chạm tên với các thuộc tính Python bên trong ....
Vladius

Ồ ... vâng, nó giống như getattr. Tôi thấy không có lý do cho sự va chạm tên. Bạn không thể đặt từ khóa là trường của lớp.
ADR

4

Giải pháp giống như Java của tôi cho vấn đề này. Hy vọng nó sẽ giúp được ai đó ...

    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

Một cải tiến cho câu trả lời của @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

Tôi chỉ muốn thông báo rằng nó không hoạt động trong python 3.6

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

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

Bạn sẽ phải cung cấp dữ liệu như một tuple như thế này

MyEnum(('aaa',))

EDIT: Điều này hóa ra là sai. Tín dụng cho một người bình luận để chỉ ra sai lầm của tôi


Sử dụng Python 3.6.6, tôi không thể tái tạo hành vi này. Tôi nghĩ rằng bạn có thể đã mắc lỗi trong khi thử nghiệm (tôi biết tôi đã làm lần đầu tiên khi kiểm tra điều này). Nếu bạn vô tình đặt một ,dấu phẩy sau mỗi phần tử (như thể các phần tử là một danh sách) thì nó sẽ coi mỗi phần tử là một tuple. (tức a = 'aaa',là thực tế giống như a = ('aaa',))
Multihunter

Bạn nói đúng, đó là lỗi khác nhau trong mã của tôi. Tôi bằng cách nào đó nghĩ rằng bạn cần phải đặt ,đằng sau mỗi dòng trong khi xác định enum đã biến các giá trị thành bộ dữ liệu bằng cách nào đó
Sstuber
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.