Làm cách nào để biểu diễn một 'Enum' bằng Python?

1143
sectrean 2008-09-01 05:55.

Tôi chủ yếu là một nhà phát triển C #, nhưng tôi hiện đang làm việc trong một dự án bằng Python.

Làm cách nào để biểu diễn tương đương với Enum trong Python?

30 answers

2756
Alec Thomas 2009-11-08 17:15.

Enums đã được thêm vào Python 3.4 như được mô tả trong PEP 435 . Nó cũng đã được hỗ trợ lại thành 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 và 2.4 trên pypi.

Đối với các kỹ thuật Enum nâng cao hơn, hãy thử thư viện aenum (2.7, 3.3+, cùng tác giả với enum34. Mã không tương thích hoàn hảo giữa py2 và py3, ví dụ bạn sẽ cần __order__trong python 2 ).

  • Để sử dụng enum34, hãy làm$ pip install enum34
  • Để sử dụng aenum, hãy làm$ pip install aenum

Cài đặt enum(không có số) sẽ cài đặt một phiên bản hoàn toàn khác và không tương thích.


from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

hoặc tương đương:

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

Trong các phiên bản trước, một cách để hoàn thành enums là:

def enum(**enums):
    return type('Enum', (), enums)

được sử dụng như vậy:

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

Bạn cũng có thể dễ dàng hỗ trợ tính năng liệt kê tự động với một cái gì đó như sau:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

và được sử dụng như vậy:

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

Hỗ trợ chuyển đổi các giá trị trở lại tên có thể được thêm vào theo cách này:

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

Điều này ghi đè lên bất kỳ thứ gì có tên đó, nhưng nó rất hữu ích để hiển thị các enums của bạn trong đầu ra. Nó sẽ ném KeyError nếu ánh xạ ngược không tồn tại. Với ví dụ đầu tiên:

>>> Numbers.reverse_mapping['three']
'THREE'
850
Jundiaius 2008-09-01 06:06.

Trước PEP 435, Python không có phần mềm tương đương nhưng bạn có thể triển khai phần mềm của riêng mình.

Bản thân tôi, tôi thích giữ cho nó đơn giản (tôi đã thấy một số ví dụ phức tạp kinh khủng trên mạng), một cái gì đó như thế này ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Trong Python 3.4 ( PEP 435 ), bạn có thể đặt Enum trở thành lớp cơ sở. Điều này mang lại cho bạn một chút chức năng bổ sung, được mô tả trong PEP. Ví dụ, các thành viên enum khác với các số nguyên và chúng được cấu tạo bởi a namevà a value.

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

Nếu bạn không muốn nhập các giá trị, hãy sử dụng phím tắt sau:

class Animal(Enum):
    DOG, CAT = range(2)

Enumtriển khai có thể được chuyển đổi thành danh sách và có thể lặp lại . Thứ tự của các thành viên là thứ tự khai báo và không liên quan gì đến giá trị của chúng. Ví dụ:

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True
334
shahjapan 2010-02-02 21:21.

Đây là một cách triển khai:

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

Đây là cách sử dụng của nó:

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)
214
Mark Harrison 2008-09-01 10:31.

Nếu bạn cần các giá trị số, đây là cách nhanh nhất:

dog, cat, rabbit = range(3)

Trong Python 3.x, bạn cũng có thể thêm trình giữ chỗ được gắn dấu sao ở cuối, trình giữ chỗ này sẽ hấp thụ tất cả các giá trị còn lại của phạm vi trong trường hợp bạn không ngại lãng phí bộ nhớ và không thể đếm:

dog, cat, rabbit, horse, *_ = range(100)
134
Ashwin 2009-10-07 16:47.

Giải pháp tốt nhất cho bạn sẽ phụ thuộc vào những gì bạn yêu cầu từ giả của bạn enum.

Enum đơn giản:

Nếu bạn enumchỉ cần một danh sách các tên xác định các mục khác nhau , giải pháp của Mark Harrison (ở trên) là tuyệt vời:

Pen, Pencil, Eraser = range(0, 3)

Sử dụng một rangecũng cho phép bạn đặt bất kỳ giá trị bắt đầu nào :

Pen, Pencil, Eraser = range(9, 12)

Ngoài những điều trên, nếu bạn cũng yêu cầu các mục thuộc một vùng chứa nào đó, thì hãy nhúng chúng vào một lớp:

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

Để sử dụng mục enum, bây giờ bạn cần sử dụng tên vùng chứa và tên mục:

stype = Stationery.Pen

Enum phức tạp:

Đối với danh sách dài các enum hoặc các cách sử dụng enum phức tạp hơn, các giải pháp này sẽ không đủ. Bạn có thể xem công thức của Will Ware để Mô phỏng các phép tính bằng Python được xuất bản trong Python Cookbook . Một phiên bản trực tuyến của điều đó có sẵn tại đây .

Thêm thông tin:

PEP 354: Các phép liệt kê trong Python có các chi tiết thú vị về một đề xuất cho enum trong Python và tại sao nó bị từ chối.

82
Aaron Maenpaa 2008-09-02 06:05.

Mẫu enum an toàn kiểu chữ được sử dụng trong Java trước JDK 5 có một số ưu điểm. Giống như trong câu trả lời của Alexandru, bạn tạo một lớp và các trường cấp lớp là các giá trị enum; tuy nhiên, các giá trị enum là các thể hiện của lớp chứ không phải là các số nguyên nhỏ. Điều này có lợi thế là các giá trị enum của bạn không vô tình so sánh bằng các số nguyên nhỏ, bạn có thể kiểm soát cách chúng được in, thêm các phương thức tùy ý nếu điều đó hữu ích và thực hiện xác nhận bằng cách sử dụng isinstance:

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

Một chủ đề gần đây trên python-dev đã chỉ ra rằng có một số thư viện enum trong tự nhiên, bao gồm:

  • flufl.enum
  • lazr.enum
  • ... và enum được đặt tên theo trí tưởng tượng
65
Zoetic 2012-02-09 10:59.

Một lớp Enum có thể là một lớp lót.

class Enum(tuple): __getattr__ = tuple.index

Cách sử dụng nó (tra cứu chuyển tiếp và đảo ngược, khóa, giá trị, mục, v.v.)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]
53
royal 2010-11-04 13:02.

Vì vậy, tôi đồng ý. Hãy không thực thi an toàn kiểu trong Python, nhưng tôi muốn bảo vệ mình khỏi những sai lầm ngớ ngẩn. Vậy chúng ta nghĩ gì về điều này?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

Nó giúp tôi không bị va chạm giá trị trong việc xác định enums của mình.

>>> Animal.Cat
2

Có một lợi thế tiện dụng khác: tra cứu ngược thực sự nhanh chóng:

def name_of(self, i):
    return self.values[i]
51
dF. 2008-09-01 08:10.

Python không được tích hợp sẵn tương đương với enumvà các câu trả lời khác có các ý tưởng để triển khai của riêng bạn (bạn cũng có thể quan tâm đến phiên bản trên cùng trong sách dạy nấu ăn Python).

Tuy nhiên, trong các tình huống mà một enumsẽ được gọi trong C, tôi thường chỉ sử dụng các chuỗi đơn giản : vì cách các đối tượng / thuộc tính được triển khai, (C) Python được tối ưu hóa để hoạt động rất nhanh với các chuỗi ngắn, vì vậy sẽ không thực sự không mang lại lợi ích hiệu suất nào khi sử dụng số nguyên. Để đề phòng lỗi chính tả / giá trị không hợp lệ, bạn có thể chèn séc vào những vị trí đã chọn.

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(Một bất lợi so với việc sử dụng một lớp là bạn mất lợi ích của tính năng tự động hoàn thành)

41
Danilo Bargen 2013-05-11 06:09.

Vào ngày 5 tháng 5 năm 2013, Guido đã đồng ý chấp nhận PEP 435 vào thư viện chuẩn Python 3.4. Điều này có nghĩa là Python cuối cùng đã hỗ trợ nội dung cho các phép liệt kê!

Có một backport có sẵn cho Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 và 2.4. Nó trên Pypi dưới dạng enum34 .

Tờ khai:

>>> from enum import Enum
>>> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

Đại diện:

>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>

Lặp lại:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue

Truy cập có lập trình:

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

Để biết thêm thông tin, hãy tham khảo đề xuất . Tài liệu chính thức có thể sẽ sớm ra mắt.

35
mbac32768 2010-11-29 16:05.

Tôi thích định nghĩa enum trong Python như vậy:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Nó chống lỗi tốt hơn so với sử dụng số nguyên vì bạn không phải lo lắng về việc đảm bảo rằng các số nguyên là duy nhất (ví dụ: nếu bạn nói Dog = 1 và Cat = 1 thì bạn sẽ bị hỏng).

Nó chống lỗi tốt hơn so với sử dụng chuỗi vì bạn không phải lo lắng về lỗi chính tả (ví dụ: x == "catt" bị lỗi âm thầm, nhưng x == Animal.Catt là một ngoại lệ thời gian chạy).

34
user18695 2008-09-21 01:49.
def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

Sử dụng nó như thế này:

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

nếu bạn chỉ muốn các ký hiệu duy nhất và không quan tâm đến các giá trị, hãy thay thế dòng này:

__metaclass__ = M_add_class_attribs(enumerate(names))

Với cái này:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)
28
agf 2011-08-07 19:51.

Một cách triển khai khác, rất đơn giản, của một enum trong Python, bằng cách sử dụng namedtuple:

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

Hay cách khác,

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())




# Example for dictionary param:
values = {"Salad": 20, "Carrot": 99, "Tomato": "No i'm not"} 
Vegetables= enum(**values)

# >>> print(Vegetables.Tomato)        'No i'm not'


# Example for keyworded params: 
Fruits = enum(Apple="Steve Jobs", Peach=1, Banana=2)

# >>> print(Fruits.Apple)             'Steve Jobs'

Giống như phương thức ở trên các lớp con set, điều này cho phép:

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

Nhưng có tính linh hoạt hơn vì nó có thể có các khóa và giá trị khác nhau. Điều này cho phép

MyEnum.FOO < MyEnum.BAR

để hoạt động như mong đợi nếu bạn sử dụng phiên bản điền vào các giá trị số tuần tự.

27
Cyril Gandon 2013-12-12 03:49.

Từ Python 3.4 sẽ có hỗ trợ chính thức cho enums. Bạn có thể tìm thấy tài liệu và ví dụ ở đây trên trang tài liệu Python 3.4 .

Các bảng kê được tạo bằng cách sử dụng cú pháp lớp, giúp chúng dễ đọc và viết. Một phương pháp tạo thay thế được mô tả trong API chức năng. Để xác định một kiểu liệt kê, hãy phân lớp Enum như sau:

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3
24
dguaraglia 2008-09-01 06:09.

Hmmm ... Tôi cho rằng thứ gần nhất với enum sẽ là một từ điển, được định nghĩa như sau:

months = {
    'January': 1,
    'February': 2,
    ...
}

hoặc là

months = dict(
    January=1,
    February=2,
    ...
)

Sau đó, bạn có thể sử dụng tên tượng trưng cho các hằng số như sau:

mymonth = months['January']

Có các tùy chọn khác, chẳng hạn như danh sách các bộ giá trị hoặc nhiều bộ giá trị, nhưng từ điển là cách duy nhất cung cấp cho bạn cách "tượng trưng" (chuỗi không đổi) để truy cập giá trị.

Chỉnh sửa: Tôi cũng thích câu trả lời của Alexandru!

Những gì tôi sử dụng:

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

Cách sử dụng:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

Vì vậy, điều này cung cấp cho bạn các hằng số nguyên như state.PUBLISHED và hai bộ giá trị để sử dụng làm lựa chọn trong các mô hình Django.

17
tuxedo 2008-09-02 17:20.

davidg khuyên bạn nên sử dụng dicts. Tôi sẽ tiến thêm một bước nữa và sử dụng các bộ:

months = set('January', 'February', ..., 'December')

Bây giờ bạn có thể kiểm tra xem một giá trị có khớp với một trong các giá trị trong tập hợp như sau hay không:

if m in months:

như dF, tuy nhiên, tôi thường chỉ sử dụng hằng số chuỗi thay cho enum.

17
danger89 2014-03-29 11:44.

Hãy đơn giản hóa nó:

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

Sau đó:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1
16
cfi 2009-11-18 16:51.

Đây là cái hay nhất mà tôi từng thấy: "First Class Enums in Python"

http://code.activestate.com/recipes/413486/

Nó cung cấp cho bạn một lớp và lớp chứa tất cả các enum. Các enum có thể được so sánh với nhau, nhưng không có bất kỳ giá trị cụ thể nào; bạn không thể sử dụng chúng như một giá trị số nguyên. (Tôi đã chống lại điều này lúc đầu vì tôi đã quen với C enums, đó là các giá trị số nguyên. Nhưng nếu bạn không thể sử dụng nó như một số nguyên, bạn không thể sử dụng nó như một số nguyên do nhầm lẫn nên nhìn chung tôi nghĩ đó là một chiến thắng .) Mỗi ​​enum là một giá trị duy nhất. Bạn có thể in enum, bạn có thể lặp lại chúng, bạn có thể kiểm tra xem giá trị enum có nằm "trong" enum hay không. Nó khá hoàn chỉnh và bóng bẩy.

Chỉnh sửa (cfi): Liên kết trên không tương thích với Python 3. Đây là cổng enum.py sang Python 3 của tôi:

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __slots__ = ('__value')
      def __init__(self, value): self.__value = value
      Value = property(lambda self: self.__value)
      EnumType = property(lambda self: EnumType)
      def __hash__(self):        return hash(self.__value)
      def __cmp__(self, other):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      def __repr__(self):        return str(names[self.__value])

   maximum = len(names) - 1
   constants = [None] * len(names)
   for i, each in enumerate(names):
      val = EnumValue(i)
      setattr(EnumClass, each, val)
      constants[i] = val
   constants = tuple(constants)
   EnumType = EnumClass()
   return EnumType


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)
15
Riaz Rizvi 2013-05-11 06:22.

Tiêu chuẩn mới trong Python là PEP 435 , vì vậy một lớp Enum sẽ có sẵn trong các phiên bản Python trong tương lai:

>>> from enum import Enum

Tuy nhiên, để bắt đầu sử dụng nó ngay bây giờ, bạn có thể cài đặt thư viện gốc đã thúc đẩy PEP:

$ pip install flufl.enum

Sau đó, bạn có thể sử dụng nó theo hướng dẫn trực tuyến của nó :

>>> from flufl.enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue
14
TokenMacGuy 2011-12-22 16:16.

Tôi đã có dịp cần một lớp Enum, nhằm mục đích giải mã một định dạng tệp nhị phân. Các tính năng mà tôi tình cờ muốn là định nghĩa enum ngắn gọn, khả năng tự do tạo các phiên bản của enum theo giá trị số nguyên hoặc chuỗi, và một cách viết tắt hữu ích repr. Đây là những gì tôi đã kết thúc với:

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

Một ví dụ bất thường về việc sử dụng nó:

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

Các tính năng chính:

  • str(), int()repr()tất cả các sản phẩm đầu ra hữu dụng nhất có thể, lần lượt tên của enumartion, giá trị số nguyên của nó, và một biểu Python đánh giá lại sao cho kiểu liệt kê.
  • Các giá trị liệt kê được trả về bởi phương thức khởi tạo được giới hạn nghiêm ngặt đối với các giá trị được xác định trước, không có giá trị enum ngẫu nhiên.
  • Các giá trị được liệt kê là các giá trị đơn; chúng có thể được so sánh chặt chẽ vớiis
10
bj0 2012-01-18 20:09.

Tôi thực sự thích giải pháp của Alec Thomas (http://stackoverflow.com/a/1695250):

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

Nó trông thanh lịch và sạch sẽ, nhưng nó chỉ là một hàm tạo một lớp với các thuộc tính được chỉ định.

Với một chút sửa đổi đối với hàm, chúng ta có thể làm cho nó hoạt động 'enumy' hơn một chút:

LƯU Ý: Tôi đã tạo các ví dụ sau bằng cách cố gắng tái tạo hành vi của kiểu mới 'enums' của pygtk (như Gtk.MessageType.WARNING)

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

Điều này tạo ra một enum dựa trên một loại được chỉ định. Ngoài việc cấp quyền truy cập thuộc tính như hàm trước, nó hoạt động như bạn mong đợi đối với một Enum đối với các loại. Nó cũng kế thừa lớp cơ sở.

Ví dụ, enums số nguyên:

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

Một điều thú vị khác có thể được thực hiện với phương pháp này là tùy chỉnh hành vi cụ thể bằng cách ghi đè các phương thức tích hợp sẵn:

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'
10
estani 2014-11-11 23:26.
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

Nếu bạn đặt tên cho nó, đó là vấn đề của bạn, nhưng nếu không tạo các đối tượng thay vì các giá trị cho phép bạn làm điều này:

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

Khi sử dụng các triển khai khác ở đây (cũng như khi sử dụng các phiên bản được đặt tên trong ví dụ của tôi), bạn phải chắc chắn rằng bạn không bao giờ cố gắng so sánh các đối tượng từ các enum khác nhau. Vì đây là một cạm bẫy có thể xảy ra:

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

Rất tiếc!

8
Ashwin 2010-03-17 12:36.

Gói enum từ PyPI cung cấp một triển khai mạnh mẽ của enum . Một câu trả lời trước đó đã đề cập đến PEP 354; điều này đã bị từ chối nhưng đề xuất đã được thực hiệnhttp://pypi.python.org/pypi/enum.

Cách sử dụng rất dễ dàng và thanh lịch:

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'
5
Rick Harris 2008-09-19 17:37.

Đề xuất của Alexandru về việc sử dụng hằng số lớp cho enum hoạt động khá tốt.

Tôi cũng muốn thêm một từ điển cho mỗi tập hợp hằng số để tra cứu một biểu diễn chuỗi mà con người có thể đọc được.

Điều này phục vụ hai mục đích: a) nó cung cấp một cách đơn giản để in đẹp mắt enum của bạn và b) từ điển nhóm các hằng số một cách hợp lý để bạn có thể kiểm tra tư cách thành viên.

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())
5
Chris Johnson 2013-06-20 11:30.

Đây là một cách tiếp cận với một số đặc điểm khác nhau mà tôi thấy có giá trị:

  • cho phép> và <so sánh dựa trên thứ tự trong enum, không phải thứ tự từ vựng
  • có thể giải quyết mục theo tên, thuộc tính hoặc chỉ mục: xa, x ['a'] hoặc x [0]
  • hỗ trợ các hoạt động cắt như [:] hoặc [-1]

và quan trọng nhất là ngăn ngừa sự so sánh giữa các loại enum khác nhau !

Dựa chặt chẽ vào http://code.activestate.com/recipes/413486-first-class-enums-in-python.

Nhiều học thuyết được đưa vào đây để minh họa những gì khác biệt về cách tiếp cận này.

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype
4
abarnert 2013-04-19 15:16.

Mặc dù đề xuất enum ban đầu, PEP 354 , đã bị từ chối nhiều năm trước, nhưng nó vẫn tiếp tục trở lại. Một số loại enum đã được dự định thêm vào 3.2, nhưng nó đã bị đẩy trở lại 3.3 và sau đó bị lãng quên. Và bây giờ có một PEP 435 được thiết kế để đưa vào Python 3.4. Việc triển khai tham chiếu của PEP 435 là flufl.enum.

Kể từ tháng 4 năm 2013, dường như có một sự đồng thuận chung rằng một cái gì đó nên được thêm vào thư viện tiêu chuẩn trong 3.4 — miễn là mọi người có thể đồng ý về "cái gì đó" nên là gì. Đó là phần khó. Xem các chủ đề bắt đầu tại đây và tại đây , và nửa tá các chủ đề khác trong những tháng đầu năm 2013.

Trong khi đó, mỗi khi điều này xuất hiện, một loạt các thiết kế và triển khai mới xuất hiện trên PyPI, ActiveState, v.v., vì vậy nếu bạn không thích thiết kế FLUFL, hãy thử tìm kiếm trên PyPI .

4
Natim 2009-10-20 00:21.

Sử dụng những điều sau đây.

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

Tôi đã sử dụng nó cho các lựa chọn mô hình Django , và nó trông rất bắt mắt. Nó không thực sự là một Enum, nhưng nó thực hiện công việc.

3
Roy Hyunjin Han 2011-06-15 07:29.

Đây là một biến thể về giải pháp của Alec Thomas :

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1
3
Michael Truog 2011-09-18 15:26.

Giải pháp này là một cách đơn giản để lấy một lớp cho kiểu liệt kê được định nghĩa là một danh sách (không có các phép gán số nguyên gây phiền nhiễu nữa):

enumeration.py:

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py:

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))

Related questions

MORE COOL STUFF

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett chia tay chồng sau 3 ngày bên nhau và vẫn kết hôn với anh ấy 25 năm sau

Cate Blanchett đã bất chấp những lời khuyên hẹn hò điển hình khi cô gặp chồng mình.

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Tại sao Michael Sheen là một diễn viên phi lợi nhuận

Michael Sheen là một diễn viên phi lợi nhuận nhưng chính xác thì điều đó có nghĩa là gì?

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Hallmark Star Colin Egglesfield Các món ăn gây xúc động mạnh đối với người hâm mộ tại RomaDrama Live! [Loại trừ]

Ngôi sao của Hallmark Colin Egglesfield chia sẻ về những cuộc gặp gỡ với người hâm mộ ly kỳ tại RomaDrama Live! cộng với chương trình INSPIRE của anh ấy tại đại hội.

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Tại sao bạn không thể phát trực tuyến 'chương trình truyền hình phía Bắc'

Bạn sẽ phải phủi sạch đầu đĩa Blu-ray hoặc DVD để xem tại sao Northern Exposure trở thành một trong những chương trình nổi tiếng nhất của thập niên 90.

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

8 công dụng tuyệt vời của Baking Soda và Giấm

8 công dụng tuyệt vời của Baking Soda và Giấm

Bạn biết đấy, hai sản phẩm này là nguồn điện để làm sạch, riêng chúng. Nhưng cùng với nhau, chúng có một loạt công dụng hoàn toàn khác.

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Hạn hán, biến đổi khí hậu đe dọa tương lai của thủy điện Hoa Kỳ

Thủy điện rất cần thiết cho lưới điện của Hoa Kỳ, nhưng nó chỉ tạo ra năng lượng khi có nước di chuyển. Bao nhiêu nhà máy thủy điện có thể gặp nguy hiểm khi các hồ và sông cạn kiệt?

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Quyên góp tóc của bạn để giúp giữ nước sạch của chúng tôi

Tóc tỉa từ các tiệm và các khoản quyên góp cá nhân có thể được tái sử dụng như những tấm thảm thấm dầu và giúp bảo vệ môi trường.

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman vẫn gan ruột và gan dạ hơn bao giờ hết trong phần 3

BoJack Horseman (Ảnh: Netflix) BoJack Horseman bắt đầu mùa thứ ba với sự xuất hiện của nhân vật tiêu biểu, người vẫn đang theo đuổi những lời khen ngợi từ giới phê bình trong một nỗ lực tuyệt vọng để tìm ra ý nghĩa trong cuộc sống của mình. Mùa thứ hai về số phận bi kịch của Raphael Bob-Waksberg dày đặc những trò đùa cũng như tuyệt vọng.

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Xem cách tính năng lái tự động cập nhật của Tesla bùng phát như thế nào khi bạn bỏ qua các cảnh báo của nó

Bản nâng cấp Phiên bản 8.0 gần đây của Tesla đối với hệ thống Lái xe tự động được thiết kế để cải thiện hệ thống và mối quan hệ của nó với người lái.

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

UnREAL, Chương trình Truyền hình Về Truyền hình Thực tế Chúng tôi Không biết Chúng tôi Cần

Chắc chắn khi con cái chúng ta xem lại các chương trình chúng ta đã xem vào khoảng năm 2015, chúng sẽ thấy UnREAL, một chương trình truyền hình mới chiếu vào tối Thứ Hai của Lifetime — bạn sẽ thấy đúng lúc, sẽ phát sóng ngay sau khi The Bachelor kết thúc — như một chương trình không thể được thực hiện vào bất kỳ thời điểm nào khác trong lịch sử truyền hình, nhưng một chương trình cảm thấy cần thiết để xem bây giờ. UnREAL có sự tham gia của Shiri Appleby trong vai Rachel, một nhà sản xuất truyền hình cho một chương trình về cơ bản là The Bachelor, mặc dù trong thế giới này, nó được gọi là Vĩnh viễn.

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Sau tất cả, Josh Trank sẽ không đạo diễn bộ phim tuyển tập Star Wars thứ hai

Thông báo chính thức đến trực tiếp từ trang web của Star Wars: Fantastic Four, đạo diễn Josh Trank, một người bí ẩn không xuất hiện tại đại hội Star Wars Celebration gần đây, sẽ không chỉ đạo phần hai Star Wars Anthology. Đây là tuyên bố được đăng ngày hôm qua: Tất cả thực sự là ngôn ngữ rất lịch sự, nhưng một bài báo của Hollywood Reporter cho thấy tất cả đều không tốt ở hậu trường, gọi sự ra đi là "một vụ sa thải" một phần dựa trên "hành vi bất thường" của Trank trong khi quay Fantastic Four: The Bài báo trích dẫn các nguồn gọi là Trank “đôi khi thiếu quyết đoán và thiếu sáng tạo,” và lưu ý rằng Fantastic Four, khởi chiếu vào ngày 7 tháng 8, đã được khởi động lại vào cuối tháng 4.

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

Edwin McCain ra mắt Grand Ole Opry: Quay cảnh hậu trường với nhạc sĩ 'I'll Be'

McCain, người đang làm việc cho một album mới, lần đầu tiên bước vào vòng kết nối vào tối thứ Sáu ở Nashville

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Forced to Borrow Paris' 'I Love Paris' Sweatshirt After 'Airline Loses All [My] Luggage'

Nicky Hilton Rothschild's luggage got lost, but luckily she has an incredible closet to shop: Sister Paris Hilton's!

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa

Kate Middleton dành một ngày bên bờ nước ở London, cùng với Jennifer Lopez, Julianne Hough và hơn thế nữa. Từ Hollywood đến New York và mọi nơi ở giữa, hãy xem các ngôi sao yêu thích của bạn đang làm gì!

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

17 tuổi bị đâm chết trong khi 4 người khác bị thương trong một cuộc tấn công bằng dao trên sông Wisconsin

Các nhà điều tra đang xem xét liệu nhóm và nghi phạm có biết nhau trước vụ tấn công hay không

Tôi viết như thế nào

Tôi viết như thế nào

Đối với tôi, mọi thứ là về dòng đầu tiên đó và nó sẽ đưa bạn đến đâu. Một số nhà văn bị điều khiển bởi cốt truyện, sự sắp xếp tinh tế của các quân cờ, trong khi những người khác bị lôi cuốn bởi một nhân vật và khả năng thực hiện một cuộc hành trình với một người bạn hư cấu mới.

Đường băng hạ cánh

Đường băng hạ cánh

Cuối hè đầu thu là mùa hoài niệm. Những chiếc đèn đường chiếu ánh sáng của chúng qua những con đường đẫm mưa, và những chiếc lá dưới chân - màu đỏ cam tắt trong bóng chạng vạng - là lời nhắc nhở về những ngày đã qua.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Hãy tưởng tượng tạo ra một chiến lược nội dung thực sự CHUYỂN ĐỔI. Nó có thể.

Vào năm 2021, tôi khuyến khích bạn suy nghĩ lại mọi thứ bạn biết về khách hàng mà bạn phục vụ và những câu chuyện bạn kể cho họ. Lùi lại.

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Sự mất mát của voi ma mút đã mở ra trái tim tôi để yêu

Vào ngày sinh nhật thứ 9 của Felix The Cat, tôi nhớ về một trong những mất mát lớn nhất trong cuộc đời trưởng thành của tôi - Sophie của tôi vào năm 2013. Tôi đã viết bài luận này và chia sẻ nó trên nền tảng này một thời gian ngắn vào năm 2013.

Language