Câu trả lời:
Enums đã được thêm vào Python 3.4 như được mô tả trong PEP 435 . Nó cũng đã được nhập vào 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 và 2.4 trên pypi.
Để biết 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 ).
enum34
, làm$ pip install enum34
aenum
, 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 để thực hiện enum 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ợ liệt kê tự động với một cái gì đó như thế này:
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
Có thể thêm hỗ trợ để chuyển đổi các giá trị thành tên 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 cứ thứ gì có tên đó, nhưng nó rất hữu ích để hiển thị enum của bạn ở đầ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'
**named
) trong hàm enum cho các phiên bản cũ hơn là để hỗ trợ các giá trị tùy chỉnh:enum("blue", "red", "green", black=0)
Trước PEP 435, Python không có tương đương nhưng bạn có thể tự thực hiện.
Bản thân tôi, tôi thích giữ nó đơn giản (tôi đã thấy một số ví dụ phức tạp khủng khiếp trên mạng), đại loại như thế này ...
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
Trong Python 3.4 ( PEP 435 ), bạn có thể biến Enum thành lớp cơ sở. Điều này giúp bạn có thêm 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 bao gồm a name
và 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)
Enum
triể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 các 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
object()
.
Đây là một cách thực hiện:
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)
__setattr__(self, name, value)
và có thể __delattr__(self, name)
vì vậy nếu bạn vô tình viết Animals.DOG = CAT
, nó sẽ không âm thầm thành công.
Animals.DOG
; Ngoài ra, các giá trị của các hằng số là các chuỗi, do đó việc so sánh với các hằng số này chậm hơn so với nếu các số nguyên được cho phép là các giá trị.
setattr()
hàm bên trong __init__()
phương thức thay vì __getattr__()
phương thức overidding . Tôi giả sử điều này được cho là hoạt động theo cùng một cách: class Enum (object): def __init __ (self, enum_opes_list): if type (enum_opes_list) == list: for enum_opes in enum_opes_list: setattr (self, enum_opes, enum_opes AttributionError
try-except
khối?
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 một trình giữ chỗ được gắn dấu sao ở cuối, điều 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 lãng phí bộ nhớ và không thể đếm:
dog, cat, rabbit, horse, *_ = range(100)
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ừ hàng giả của bạn enum
.
Enum đơn giản:
Nếu bạn enum
chỉ cần một danh sách các tên xác định các mục khác nhau , thì giải pháp của Mark Harrison (ở trên) là tuyệt vời:
Pen, Pencil, Eraser = range(0, 3)
Sử dụng range
cũng cho phép bạn đặt bất kỳ giá trị bắt đầu :
Pen, Pencil, Eraser = range(9, 12)
Ngoài việc trên, nếu bạn cũng yêu cầu các mặt hàng thuộc về một thùng chứa của một số loại, sau đó nhúng chúng trong một lớp học:
class Stationery:
Pen, Pencil, Eraser = range(0, 3)
Để sử dụng mục enum, bây giờ bạn sẽ cần sử dụng tên container và tên mục:
stype = Stationery.Pen
Enum phức tạp:
Đối với danh sách dài enum hoặc 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ể tìm đến công thức của Will Ware để mô phỏng liệt kê trong Python được xuất bản trong Python Cookbook . Một phiên bản trực tuyến có sẵn ở đây .
Thêm thông tin:
PEP 354: Các 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.
range
bạn có thể bỏ qua đối số đầu tiên nếu nó là 0
my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
. Sau đó, my_enum
có thể được sử dụng trong tra cứu, ví dụ, my_enum['Item0']
có thể là một chỉ mục thành một chuỗi. Bạn có thể muốn bọc kết quả của str.split
một hàm ném ngoại lệ nếu có bất kỳ bản sao nào.
Flag1, Flag2, Flag3 = [2**i for i in range(3)]
Mẫu enum an toàn được sử dụng trong Java trước JDK 5 có một số lợi thế. Giống như trong câu trả lời của Alexandru, bạn tạo ra các trường cấp lớp và cấ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 thay vì 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à đưa ra các 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 vài thư viện enum trong tự nhiên, bao gồm:
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 (tra cứu tiến và lùi, khóa, giá trị, vật phẩm, 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)]
in
từ khóa để tìm kiếm các thành viên gọn gàng. Ví dụ sử dụng:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Vì vậy, tôi đồng ý. Chúng ta đừng thực thi loại an toàn trong Python, nhưng tôi muốn bảo vệ bản thân 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ữ cho tôi khỏi xung đột giá trị trong việc xác định enum của tôi.
>>> Animal.Cat
2
Có một lợi thế tiện dụng khác: tra cứu ngược rất nhanh:
def name_of(self, i):
return self.values[i]
Animal = Enum('horse', 'dog', 'cat')
. Tôi cũng bắt gặp ValueError trong getattr trong trường hợp có một mục bị thiếu trong self.values - có vẻ tốt hơn để tăng AttributionError bằng chuỗi tên được cung cấp thay thế. Tôi không thể làm cho siêu dữ liệu hoạt động trong Python 2.7 dựa trên kiến thức hạn chế của mình trong khu vực đó, nhưng lớp Enum tùy chỉnh của tôi hoạt động tốt với các phương thức cá thể thẳng.
Python không có sẵn tích hợp tương đương enum
và các câu trả lời khác có ý tưởng để tự thực hiện (bạn cũng có thể quan tâm đến phiên bản cao nhất trong sách dạy nấu ăn Python).
Tuy nhiên, trong các tình huống enum
sẽ được gọi bằng C, tôi thường chỉ sử dụng các chuỗi đơn giản : do cách thức thực hiện các đối tượng / thuộc tính, (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ự là bất kỳ lợi ích hiệu suất để sử dụng số nguyên. Để bảo vệ chống lại lỗi chính tả / giá trị không hợp lệ, bạn có thể chèn séc vào các vị trí đã chọn.
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(Một nhược điểm so với việc sử dụng một lớp là bạn mất lợi ích của tự động hoàn thành)
Vào ngày 2013-05-10, 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ợ dựng sẵn cho bảng 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. Đó là trên Pypi như 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 theo chương trình:
>>> Color(1)
Color.red
>>> Color['blue']
Color.blue
Để biết thêm thông tin, tham khảo các đề xuất . Tài liệu chính thức có thể sẽ theo sau.
Tôi thích định nghĩa enums trong Python như vậy:
class Animal:
class Dog: pass
class Cat: pass
x = Animal.Dog
Nó chống lỗi nhiều 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 số nguyên là duy nhất (ví dụ: nếu bạn nói Dog = 1 và Cat = 1 bạn sẽ bị lừa).
Nó chống lỗi nhiều 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" không thành công, nhưng x == Animal.Catt là ngoại lệ thời gian chạy).
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 biểu tượng độc đáo 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)
enum(names)
thành enum(*names)
- sau đó bạn có thể bỏ dấu ngoặc đơn bổ sung khi gọi nó.
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 liệt kê được tạo bằ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 phép liệt kê, phân lớp Enum như sau:
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
Hmmm ... Tôi cho rằng thứ gần gũi nhất với enum sẽ là một cuốn từ điển, được định nghĩa như thế này:
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ư thế này:
mymonth = months['January']
Có các tùy chọn khác, như danh sách các bộ dữ liệu hoặc bộ dữ liệu, nhưng từ điển là lựa chọn duy nhất cung cấp cho bạn cách "biểu tượ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!
Một cách khác, rất đơn giản, thực hiện một enum trong Python, 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())
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ố liên tiếp.
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.PUBOUNDED và hai bộ dữ liệu để sử dụng làm lựa chọn trong các mô hình Django.
davidg khuyên bạn nên sử dụng dicts. Tôi sẽ tiến thêm một bước 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 không:
if m in months:
Tuy nhiên, như dF, tôi thường chỉ sử dụng các hằng chuỗi thay cho enum.
Giữ cho nó đơn giả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
Đây là cái tốt nhất 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 làm giá trị nguyên. . .) 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ó "trong" enum 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 của tôi với Python 3:
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)
.__int__()
phương thức sẽ đưa ra một ngoại lệ cho một enum; nhưng nên có một cách để có được giá trị. Và có thể đặt các giá trị nguyên cụ thể theo thời gian xác định lớp, vì vậy bạn có thể sử dụng một enum cho những thứ như các hằng số trong stat
mô-đun.
Tôi đã có dịp cần một lớp Enum, với mục đích giải mã một định dạng tệp nhị phân. Các tính năng tôi tình cờ muốn là định nghĩa enum súc tích, khả năng tự do tạo ra các thể hiện của enum theo giá trị nguyên hoặc chuỗi và một ý nghĩa 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ụ hay thay đổi khi 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()
Và 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ê.is
__instancecheck__
phương thức. Các lớp học không phải là tập hợp các thể hiện, vì vậy 1 in Fruit
là vô lý. Tuy nhiên, phiên bản được liên kết hỗ trợ isinstance(1, Fruit)
sẽ chính xác hơn về mặt khái niệm các lớp và thể hiện.
Tiêu chuẩn mới trong Python là PEP 435 , do đó, một lớp Enum sẽ có sẵn trong các phiên bản tương lai của Python:
>>> from enum import Enum
Tuy nhiên, để bắt đầu sử dụng ngay bây giờ, bạn có thể cài đặt thư viện gốc tạo động lực cho 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
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 đối tượng thay vì giá trị cho phép bạn thực hiện việc 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 được đặt ở đây (cũng như khi sử dụng các thể hiệ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. Đối với đây là một cạm bẫy có thể:
>>> 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!
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 chức năng tạo ra một lớp với các thuộc tính được chỉ định.
Với một chút sửa đổi cho chức năng, chúng ta có thể khiến nó hoạt động nhiều hơn một chút 'ghen 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 '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 xác đị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 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:
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'
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ện http://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'
Đề xuất của Alexandru về việc sử dụng hằng số lớp cho enums hoạt động khá tốt.
Tôi cũng muốn thêm một từ điển cho mỗi bộ hằng số để tra cứu một đại diện chuỗ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 enum của bạn và b) từ điển nhóm các hằng số để 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())
Đâ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ị:
và quan trọng nhất là ngăn chặn sự so sánh giữa các loại khác nhau !
Dựa trên http://code.activestate.com/recipes/413486-first- class-enums-in-python .
Nhiều tài liệu bao gồm ở đây để minh họa những gì khác nhau về phương pháp 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
Đây là một biến thể của giải pháp 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
Giải pháp này là một cách đơn giản để nhận một lớp cho phép 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 khó chịu hơn):
liệt kê:
import new
def create(class_name, names):
return new.classobj(
class_name, (object,), dict((y, x) for x, y in enumerate(names))
)
ví dụ:
import enumeration
Colors = enumeration.create('Colors', (
'red',
'orange',
'yellow',
'green',
'blue',
'violet',
))
type(class_name, (object,), dict(...))
thay thế?
Trong khi đề xuất enum ban đầu, PEP 354 , đã bị từ chối nhiều năm trước, 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 lùi về 3.3 và sau đó bị lãng quên. Và bây giờ có PEP 435 dự định đưa vào Python 3.4. Việc thực hiện 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ó khăn. Xem các chủ đề bắt đầu ở đây và ở đây , và một 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 PyPI .
Sử dụng như sau.
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 pythonic. Nó không thực sự là một Enum, nhưng nó thực hiện công việc.