Jawaban:
Enum telah ditambahkan ke Python 3.4 seperti yang dijelaskan dalam PEP 435 . Ini juga telah di- backport ke 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, dan 2.4 di pypi.
Untuk teknik Enum yang lebih lanjut, coba perpustakaan aenum (2.7, 3.3+, penulis yang sama dengan enum34
. Code tidak sepenuhnya kompatibel antara py2 dan py3, mis. Anda akan membutuhkan __order__
python 2 ).
enum34
, lakukan$ pip install enum34
aenum
, lakukan$ pip install aenum
Menginstal enum
(tidak ada angka) akan menginstal versi yang sama sekali berbeda dan tidak kompatibel.
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)
atau yang setara:
class Animal(Enum):
ant = 1
bee = 2
cat = 3
dog = 4
Dalam versi sebelumnya, satu cara untuk mencapai enum adalah:
def enum(**enums):
return type('Enum', (), enums)
yang digunakan seperti ini:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
Anda juga dapat dengan mudah mendukung pencacahan otomatis dengan sesuatu seperti ini:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
dan digunakan seperti ini:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
Dukungan untuk mengonversi nilai kembali ke nama dapat ditambahkan dengan cara ini:
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)
Ini menimpa apa pun dengan nama itu, tetapi ini berguna untuk menampilkan enum Anda menjadi keluaran. Ini akan melempar KeyError jika pemetaan terbalik tidak ada. Dengan contoh pertama:
>>> Numbers.reverse_mapping['three']
'THREE'
**named
) dalam fungsi enum untuk versi yang lebih lama adalah untuk mendukung nilai-nilai khusus:enum("blue", "red", "green", black=0)
Sebelum PEP 435, Python tidak memiliki padanan tapi Anda bisa menerapkannya sendiri.
Saya sendiri, saya suka menjaganya tetap sederhana (saya telah melihat beberapa contoh rumit yang mengerikan di internet), sesuatu seperti ini ...
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
Di Python 3.4 ( PEP 435 ), Anda bisa membuat Enum kelas dasar. Ini memberi Anda sedikit fungsi tambahan, dijelaskan dalam PEP. Sebagai contoh, anggota enum berbeda dari bilangan bulat, dan mereka terdiri dari a name
dan 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"
Jika Anda tidak ingin mengetik nilai, gunakan pintasan berikut:
class Animal(Enum):
DOG, CAT = range(2)
Enum
implementasi dapat dikonversi menjadi daftar dan dapat diubah . Urutan anggotanya adalah perintah deklarasi dan tidak ada hubungannya dengan nilai-nilai mereka. Sebagai contoh:
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()
.
Berikut ini satu implementasinya:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
Berikut ini adalah penggunaannya:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
__setattr__(self, name, value)
dan mungkin __delattr__(self, name)
sehingga jika Anda secara tidak sengaja menulis Animals.DOG = CAT
, itu tidak akan berhasil secara diam-diam.
Animals.DOG
; juga, nilai-nilai konstanta adalah string, sehingga perbandingan dengan konstanta ini lebih lambat daripada jika, katakanlah, bilangan bulat diizinkan sebagai nilai.
setattr()
fungsi di dalam __init__()
metode alih-alih __getattr__()
metode overidding . Saya berasumsi ini seharusnya bekerja dengan cara yang sama: class Enum (objek): def __init __ (self, enum_string_list): if type (enum_string_list) == list: for enum_string in enum_string_list: setattr (self, enum_string, enum_string) lain: meningkatkan AttributeError
try-except
blok?
Jika Anda membutuhkan nilai numerik, inilah cara tercepat:
dog, cat, rabbit = range(3)
Di Python 3.x Anda juga bisa menambahkan placeholder yang berkilau bintangnya di akhir, yang akan menyerap semua nilai yang tersisa dari rentang jika Anda tidak keberatan membuang-buang memori dan tidak dapat menghitung:
dog, cat, rabbit, horse, *_ = range(100)
Solusi terbaik untuk Anda akan tergantung pada apa yang Anda butuhkan dari kepalsuan Anda enum
.
Enum sederhana:
Jika Anda enum
hanya memerlukan daftar nama yang mengidentifikasi item yang berbeda , solusinya oleh Mark Harrison (di atas) sangat bagus:
Pen, Pencil, Eraser = range(0, 3)
Menggunakan range
juga memungkinkan Anda untuk menetapkan nilai awal :
Pen, Pencil, Eraser = range(9, 12)
Selain hal-hal di atas, jika Anda juga mensyaratkan bahwa barang-barang itu milik wadah semacam, maka sematkan di kelas:
class Stationery:
Pen, Pencil, Eraser = range(0, 3)
Untuk menggunakan item enum, Anda sekarang harus menggunakan nama kontainer dan nama item:
stype = Stationery.Pen
Enum kompleks:
Untuk daftar panjang enum atau penggunaan enum yang lebih rumit, solusi ini tidak akan cukup. Anda bisa melihat resep oleh Will Ware untuk Mensimulasikan Enumerasi dengan Python yang diterbitkan dalam Python Cookbook . Versi online yang tersedia di sini .
Info lebih lanjut:
PEP 354: Pencacahan dengan Python memiliki detail menarik dari proposal enum dengan Python dan mengapa ditolak.
range
Anda dapat menghilangkan argumen pertama jika 0
my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
. Kemudian my_enum
dapat digunakan dalam pencarian, misalnya, my_enum['Item0']
dapat menjadi indeks menjadi suatu urutan. Anda mungkin ingin membungkus hasil str.split
dalam fungsi yang melempar pengecualian jika ada duplikat.
Flag1, Flag2, Flag3 = [2**i for i in range(3)]
Pola typesafe enum yang digunakan di Java pra-JDK 5 memiliki sejumlah keunggulan. Sama seperti dalam jawaban Alexandru, Anda membuat kelas dan bidang level kelas adalah nilai enum; Namun, nilai enum adalah instance dari kelas daripada bilangan bulat kecil. Ini memiliki keuntungan bahwa nilai enum Anda tidak secara tidak sengaja membandingkan sama dengan bilangan bulat kecil, Anda dapat mengontrol bagaimana mereka dicetak, menambahkan metode arbitrer jika itu berguna dan membuat pernyataan menggunakan 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
Sebuah utas terbaru tentang python-dev menunjukkan ada beberapa perpustakaan enum di alam liar, termasuk:
Kelas Enum dapat berupa one-liner.
class Enum(tuple): __getattr__ = tuple.index
Cara menggunakannya (memajukan dan membalikkan pencarian, kunci, nilai, item, dll.)
>>> 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
kata kunci untuk mencari anggota yang rapi. Contoh penggunaan:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Jadi saya setuju. Mari kita tidak menegakkan keamanan jenis dengan Python, tapi saya ingin melindungi diri saya dari kesalahan konyol. Jadi apa yang kita pikirkan tentang ini?
class Animal(object):
values = ['Horse','Dog','Cat']
class __metaclass__(type):
def __getattr__(self, name):
return self.values.index(name)
Itu menjaga saya dari tabrakan nilai dalam mendefinisikan enum saya.
>>> Animal.Cat
2
Ada keuntungan lain yang berguna: pencarian terbalik sangat cepat:
def name_of(self, i):
return self.values[i]
Animal = Enum('horse', 'dog', 'cat')
. Saya juga menangkap ValueError di getattr jika ada item yang hilang di self.values - sepertinya lebih baik untuk meningkatkan AttributeError dengan string nama yang disediakan. Saya tidak bisa membuat metaclass bekerja di Python 2.7 berdasarkan pengetahuan saya yang terbatas di area itu, tetapi kelas kustom Enum saya berfungsi dengan baik dengan metode instan.
Python tidak memiliki built-in yang setara dengan enum
, dan jawaban lain memiliki ide untuk mengimplementasikannya sendiri (Anda mungkin juga tertarik dengan versi over the top di buku masak Python).
Namun, dalam situasi di mana sebuah enum
akan dipanggil dalam C, saya biasanya berakhir hanya menggunakan string sederhana : karena cara objek / atribut diimplementasikan, (C) Python dioptimalkan untuk bekerja sangat cepat dengan string pendek, jadi tidak akan benar-benar bermanfaat untuk menggunakan bilangan bulat. Untuk menjaga dari kesalahan ketik / nilai tidak valid Anda dapat memasukkan cek di tempat-tempat yang dipilih.
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(Satu kelemahan dibandingkan menggunakan kelas adalah Anda kehilangan manfaat autocomplete)
Pada 2013-05-10, Guido setuju untuk menerima PEP 435 ke dalam pustaka standar Python 3.4. Ini berarti bahwa Python akhirnya telah membangun dukungan untuk enumerasi!
Ada backport yang tersedia untuk Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, dan 2.4. Ada di Pypi sebagai enum34 .
Pernyataan:
>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
Perwakilan:
>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>
Pengulangan:
>>> for color in Color:
... print(color)
...
Color.red
Color.green
Color.blue
Akses terprogram:
>>> Color(1)
Color.red
>>> Color['blue']
Color.blue
Untuk informasi lebih lanjut, lihat proposal . Dokumentasi resmi mungkin akan segera menyusul.
Saya lebih suka mendefinisikan enum dengan Python seperti ini:
class Animal:
class Dog: pass
class Cat: pass
x = Animal.Dog
Ini lebih tahan bug daripada menggunakan bilangan bulat karena Anda tidak perlu khawatir tentang memastikan bilangan bulat itu unik (mis. Jika Anda mengatakan Dog = 1 dan Cat = 1 Anda akan mengacaukannya).
Ini lebih tahan bug daripada menggunakan string karena Anda tidak perlu khawatir tentang kesalahan ketik (mis. X == "catt" gagal diam-diam, tetapi x == Animal.Catt adalah pengecualian runtime).
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()
Gunakan seperti ini:
Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError
jika Anda hanya ingin simbol yang unik dan tidak peduli dengan nilainya, ganti baris ini:
__metaclass__ = M_add_class_attribs(enumerate(names))
dengan ini:
__metaclass__ = M_add_class_attribs((object(), name) for name in names)
enum(names)
ke enum(*names)
- maka Anda bisa drop kurung tambahan ketika menyebutnya.
Dari Python 3.4 akan ada dukungan resmi untuk enum. Anda dapat menemukan dokumentasi dan contoh di sini di halaman dokumentasi Python 3.4 .
Enumerasi dibuat menggunakan sintaks kelas, yang membuatnya mudah dibaca dan ditulis. Metode pembuatan alternatif dijelaskan dalam API Fungsional. Untuk mendefinisikan enumerasi, subkelas Enum sebagai berikut:
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
Hmmm ... Saya kira hal yang paling dekat dengan enum adalah kamus, didefinisikan seperti ini:
months = {
'January': 1,
'February': 2,
...
}
atau
months = dict(
January=1,
February=2,
...
)
Kemudian, Anda dapat menggunakan nama simbolis untuk konstanta seperti ini:
mymonth = months['January']
Ada opsi lain, seperti daftar tupel, atau tupel tupel, tetapi kamus adalah satu-satunya yang memberi Anda cara "simbolik" (string konstan) untuk mengakses nilai.
Sunting: Saya juga menyukai jawaban Alexandru!
Lain, sangat sederhana, implementasi enum dengan Python, menggunakan namedtuple
:
from collections import namedtuple
def enum(*keys):
return namedtuple('Enum', keys)(*keys)
MyEnum = enum('FOO', 'BAR', 'BAZ')
atau, sebagai alternatif,
# 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())
Seperti metode di atas subclass itu set
, ini memungkinkan:
'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO
Tetapi memiliki lebih banyak fleksibilitas karena dapat memiliki kunci dan nilai yang berbeda. Ini memungkinkan
MyEnum.FOO < MyEnum.BAR
untuk bertindak seperti yang diharapkan jika Anda menggunakan versi yang mengisi nilai angka berurutan.
Apa yang saya gunakan:
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))
Cara Penggunaan:
>>> 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'))
Jadi ini memberi Anda konstanta integer seperti state.PUBLISHED dan dua-tupel untuk digunakan sebagai pilihan dalam model Django.
davidg merekomendasikan menggunakan dikt. Saya akan melangkah lebih jauh dan menggunakan set:
months = set('January', 'February', ..., 'December')
Sekarang Anda dapat menguji apakah suatu nilai cocok dengan salah satu nilai dalam set seperti ini:
if m in months:
seperti dF, meskipun, saya biasanya hanya menggunakan konstanta string di tempat enum.
Ini yang terbaik yang pernah saya lihat: "Enum Kelas Satu dengan Python"
http://code.activestate.com/recipes/413486/
Ini memberi Anda kelas, dan kelas berisi semua enum. Enum dapat dibandingkan satu sama lain, tetapi tidak memiliki nilai tertentu; Anda tidak dapat menggunakannya sebagai nilai integer. (Saya menolak ini pada awalnya karena saya terbiasa dengan C enum, yang merupakan nilai integer. Tetapi jika Anda tidak dapat menggunakannya sebagai integer, Anda tidak dapat menggunakannya sebagai integer karena kesalahan sehingga secara keseluruhan saya pikir ini adalah kemenangan .) Setiap enum adalah nilai unik. Anda dapat mencetak enum, Anda dapat mengulanginya, Anda dapat menguji bahwa nilai enum "di" enum. Cukup lengkap dan apik.
Sunting (cfi): Tautan di atas tidak kompatibel dengan Python 3. Ini port enum.py saya ke 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__()
metode harus memunculkan pengecualian untuk enum; tetapi harus ada cara untuk mendapatkan nilai. Dan harus dimungkinkan untuk menetapkan nilai integer spesifik pada waktu definisi kelas, sehingga Anda dapat menggunakan enum untuk hal-hal seperti konstanta dalam stat
modul.
Saya memiliki kesempatan untuk membutuhkan kelas Enum, untuk tujuan decoding format file biner. Fitur yang kebetulan saya inginkan adalah definisi enum ringkas, kemampuan untuk secara bebas membuat instance enum dengan nilai integer atau string, dan repr
esensiasi yang bermanfaat . Inilah yang akhirnya saya dapatkan:
>>> 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
...
Contoh aneh menggunakannya:
>>> 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
Fitur utama:
str()
, int()
dan repr()
semua menghasilkan keluaran paling berguna yang mungkin, masing-masing nama enumartion, nilai integernya, dan ekspresi Python yang mengevaluasi kembali ke enumerasi.is
__instancecheck__
metode. Kelas bukan kumpulan instance, jadi 1 in Fruit
tidak masuk akal. Namun, versi tertaut mendukung isinstance(1, Fruit)
yang akan lebih benar dalam hal pengertian kelas dan instance.
Standar baru dalam Python adalah PEP 435 , sehingga kelas Enum akan tersedia dalam versi Python mendatang:
>>> from enum import Enum
Namun untuk mulai menggunakannya sekarang, Anda dapat menginstal perpustakaan asli yang memotivasi PEP:
$ pip install flufl.enum
Kemudian Anda dapat menggunakannya sesuai panduan online-nya :
>>> 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)
Jika Anda beri nama, itu masalah Anda, tetapi jika tidak membuat objek, bukan nilai, Anda dapat melakukan ini:
>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False
Saat menggunakan implementasi lain yang diletakkan di sini (juga saat menggunakan contoh bernama dalam contoh saya), Anda harus yakin Anda tidak pernah mencoba membandingkan objek dari enum yang berbeda. Untuk inilah kemungkinan perangkap:
>>> 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
Astaga!
Saya sangat menyukai solusi Alec Thomas (http://stackoverflow.com/a/1695250):
def enum(**enums):
'''simple constant "enums"'''
return type('Enum', (object,), enums)
Ini terlihat elegan dan bersih, tetapi hanya fungsi yang menciptakan kelas dengan atribut yang ditentukan.
Dengan sedikit modifikasi pada fungsi, kita bisa membuatnya bertindak sedikit lebih 'enumy':
CATATAN: Saya membuat contoh berikut dengan mencoba mereproduksi perilaku gaya baru 'enum' pygtk (seperti 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
Ini menciptakan enum berdasarkan jenis tertentu. Selain memberikan akses atribut seperti fungsi sebelumnya, ia berperilaku seperti yang Anda harapkan dari Enum sehubungan dengan tipe. Itu juga mewarisi kelas dasar.
Misalnya, integer enums:
>>> 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
Hal menarik lain yang dapat dilakukan dengan metode ini adalah menyesuaikan perilaku spesifik dengan mengganti metode bawaan:
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'
Paket enum dari PyPI menyediakan implementasi enum yang kuat. Jawaban sebelumnya menyebutkan PEP 354; ini ditolak tetapi proposal itu diterapkan http://pypi.python.org/pypi/enum .
Penggunaannya mudah dan elegan:
>>> 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'
Saran Alexandru untuk menggunakan konstanta kelas untuk enum bekerja dengan sangat baik.
Saya juga ingin menambahkan kamus untuk setiap rangkaian konstanta untuk mencari representasi string yang dapat dibaca manusia.
Ini melayani dua tujuan: a) menyediakan cara sederhana untuk mencetak enum Anda dengan cantik dan b) kamus secara logis mengelompokkan konstanta sehingga Anda dapat menguji keanggotaan.
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())
Inilah pendekatan dengan beberapa karakteristik berbeda yang menurut saya berharga:
dan yang paling penting mencegah perbandingan antara enum dari berbagai jenis !
Berdasarkan erat pada http://code.activestate.com/recipes/413486-first-class-enums-in-python .
Banyak dokumen yang disertakan di sini untuk menggambarkan apa yang berbeda dari pendekatan ini.
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
Berikut ini varian dari solusi 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
Solusi ini adalah cara sederhana untuk mendapatkan kelas untuk enumerasi yang didefinisikan sebagai daftar (tidak ada lagi tugas bilangan bulat yang mengganggu):
enumeration.py:
import new
def create(class_name, names):
return new.classobj(
class_name, (object,), dict((y, x) for x, y in enumerate(names))
)
contoh.py:
import enumeration
Colors = enumeration.create('Colors', (
'red',
'orange',
'yellow',
'green',
'blue',
'violet',
))
type(class_name, (object,), dict(...))
saja saja?
Sementara proposal enum asli, PEP 354 , ditolak tahun lalu, itu terus muncul kembali. Beberapa jenis enum dimaksudkan untuk ditambahkan ke 3.2, tapi itu didorong kembali ke 3.3 dan kemudian dilupakan. Dan sekarang ada PEP 435 yang dimaksudkan untuk dimasukkan dalam Python 3.4. Referensi implementasi PEP 435 adalah flufl.enum
.
Pada April 2013, tampaknya ada konsensus umum bahwa sesuatu harus ditambahkan ke perpustakaan standar di 3.4 — selama orang-orang dapat sepakat tentang apa "seharusnya" itu. Itu bagian yang sulit. Lihat utas mulai di sini dan di sini , dan setengah lusin utas lainnya di awal 2013.
Sementara itu, setiap kali ini muncul, banyak desain dan implementasi baru muncul di PyPI, ActiveState, dll., Jadi jika Anda tidak menyukai desain FLUFL, coba pencarian PyPI .
Gunakan yang berikut ini.
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']
Saya menggunakan itu untuk pilihan model Django , dan itu terlihat sangat pythonic. Ini sebenarnya bukan Enum, tetapi ia berhasil.