ฉันจะแสดง 'Enum' ใน Python ได้อย่างไร


1143

ฉันเป็นนักพัฒนาซอฟต์แวร์ C # เป็นหลัก แต่ปัจจุบันฉันทำงานโครงการใน Python

ฉันจะเป็นตัวแทนของ Enum ใน Python ได้อย่างไร

คำตอบ:


2688

Enums ได้รับการเพิ่มหลาม 3.4 ตามที่อธิบายในPEP 435 มันได้รับการbackported เป็น 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 และ 2.4ใน pypi

สำหรับเทคนิค Enum ขั้นสูงลองใช้ไลบรารี aenum (2.7, 3.3+, ผู้แต่งเดียวกันกับenum34รหัสไม่สามารถทำงานร่วมกันได้อย่างสมบูรณ์ระหว่าง py2 และ py3 เช่นคุณจะต้องใช้__order__python 2 )

  • ที่จะใช้enum34ทำ$ pip install enum34
  • ที่จะใช้aenumทำ$ pip install aenum

การติดตั้งenum(ไม่ใช้ตัวเลข) จะติดตั้งเวอร์ชันที่แตกต่างและเข้ากันไม่ได้โดยสิ้นเชิง


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)

หรือเทียบเท่า:

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

ในเวอร์ชันก่อนหน้าวิธีหนึ่งในการทำให้สำเร็จ enums คือ:

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

ซึ่งใช้เช่น:

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

คุณสามารถรองรับการแจงนับอัตโนมัติได้อย่างง่ายดายด้วยสิ่งนี้:

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

และใช้เช่นนั้น:

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

การสนับสนุนการแปลงค่ากลับเป็นชื่อสามารถเพิ่มด้วยวิธีนี้:

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)

สิ่งนี้จะเขียนทับสิ่งใด ๆ ที่มีชื่อนั้น แต่มีประโยชน์สำหรับการแสดงผล enums ของคุณในผลลัพธ์ มันจะโยน KeyError หากไม่มีการแมปย้อนกลับ ด้วยตัวอย่างแรก:

>>> Numbers.reverse_mapping['three']
'THREE'

1
ฉันไม่สามารถเข้าใจได้ทำไมพวกเขาถึงผ่าน kwargs (** ชื่อ) ในวิธีการ enum (* ต่อเนื่อง, ** ชื่อ) กรุณาอธิบาย หากไม่มี kwargs ก็จะใช้งานได้ ฉันตรวจสอบแล้ว
Seenu S

มันเป็นการดีที่จะปรับปรุงฟังก์ชั่น Python 2 ให้เข้ากันได้กับ API การทำงานของ Python 3 ของ Enum (ชื่อ, ค่า)
bscan

var kwargs ( **named) ในฟังก์ชั่น enum สำหรับเวอร์ชั่นเก่าคือการรองรับค่าที่กำหนดเอง:enum("blue", "red", "green", black=0)
Éric Araujo

823

ก่อนหน้า PEP 435 Python ไม่มีอะไรเทียบเท่า แต่คุณสามารถนำไปปฏิบัติใช้เองได้

ตัวฉันเองฉันชอบทำให้มันง่าย (ฉันเคยเห็นตัวอย่างที่ซับซ้อนอย่างน่ากลัวในเน็ต) บางอย่างเช่นนี้ ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

ใน Python 3.4 ( PEP 435 ) คุณสามารถทำให้Enumเป็นคลาสพื้นฐานได้ นี่ทำให้คุณมีฟังก์ชั่นพิเศษเพิ่มอีกเล็กน้อยซึ่งอธิบายไว้ใน PEP ยกตัวอย่างเช่นสมาชิก enum แตกต่างจากจำนวนเต็มและพวกเขาจะประกอบไปด้วยและnamevalue

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

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

print(Animal.DOG.value)
# 1

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

หากคุณไม่ต้องการพิมพ์ค่าใช้ทางลัดต่อไปนี้:

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

Enumการใช้งานที่สามารถแปลงเป็นรายการและมี iterable คำสั่งของสมาชิกคือคำสั่งประกาศและไม่มีอะไรเกี่ยวข้องกับคุณค่าของพวกเขา ตัวอย่างเช่น:

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

51
ไม่มันเป็นตัวแปรระดับ
Georg Schölly

246
Python เป็นแบบไดนามิกตามค่าเริ่มต้น ไม่มีเหตุผลที่ถูกต้องในการบังคับใช้ความปลอดภัยเวลาคอมไพล์ในภาษาเช่น Python โดยเฉพาะเมื่อไม่มี และอีกสิ่งหนึ่ง ... รูปแบบที่ดีนั้นดีในบริบทที่มันถูกสร้างขึ้นเท่านั้น รูปแบบที่ดีสามารถถูกแทนที่หรือไร้ประโยชน์อย่างสมบูรณ์ขึ้นอยู่กับเครื่องมือที่คุณใช้
Alexandru Nedelcu

20
@ Longpoke ถ้าคุณมีค่า 100 แล้วคุณทำสิ่งที่ผิดอย่างแน่นอน;) ฉันชอบตัวเลขที่เกี่ยวข้องกับ enums ของฉัน ... พวกเขาเขียนได้ง่าย (vs strings) สามารถเก็บไว้ในฐานข้อมูลได้อย่างง่ายดายและเข้ากันได้กับ C / C ++ enum ซึ่งทำให้การจัดการง่ายขึ้น
Alexandru Nedelcu

50
object()ฉันจะใช้นี้มีตัวเลขที่ถูกแทนที่ด้วย
Tobu

9
PEP354 ดั้งเดิมไม่เพียง แต่ถูกปฏิเสธอีกต่อไป แต่ถูกแทนที่แล้ว PEP435 เพิ่ม Enum มาตรฐานสำหรับ Python 3.4 ดูpython.org/dev/peps/pep-0435
Peter Hansen

322

นี่คือการดำเนินการอย่างหนึ่ง:

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

นี่คือการใช้งาน:

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

print(Animals.DOG)

51
ยอดเยี่ยม สิ่งนี้สามารถปรับปรุงให้ดีขึ้นได้โดยการเอาชนะ__setattr__(self, name, value)และบางที__delattr__(self, name)ถ้าคุณตั้งใจเขียนAnimals.DOG = CATมันจะไม่ประสบความสำเร็จ
Joonas Pulakka

15
@shahjapan: น่าสนใจ แต่ค่อนข้างช้า: ทำการทดสอบสำหรับการเข้าถึงแต่ละครั้งเช่นAnimals.DOG; นอกจากนี้ค่าของ constats เป็นสตริงดังนั้นการเปรียบเทียบกับค่าคงที่เหล่านี้จะช้ากว่าถ้าพูดอนุญาตให้จำนวนเต็มเป็นค่า
Eric O Lebigot

3
@Shahjapan: ฉันจะยืนยันว่าวิธีนี้ไม่ชัดเจนเท่าโซลูชันที่สั้นกว่าของ Alexandru หรือ Mark เช่น มันเป็นทางออกที่น่าสนใจ :)
Eric O Lebigot

ฉันลองใช้setattr()ฟังก์ชั่น__init__()Inside __getattr__()method แทนวิธีoveridding ฉันคิดว่านี่ควรจะทำงานในลักษณะเดียวกัน: class Enum (วัตถุ): def __init __ (self, enum_string_list): if type (enum_string_list) == รายการ: สำหรับ enum_string ใน enum_string_list: setattr (self, enum_string, enum_string) AttributeError
Harshith JV

8
@ AndréTerra: คุณจะตรวจสอบการเป็นสมาชิกในtry-exceptบล็อกได้อย่างไร?
bgusach

210

หากคุณต้องการค่าตัวเลขนี่คือวิธีที่เร็วที่สุด:

dog, cat, rabbit = range(3)

ใน Python 3.x คุณสามารถเพิ่มตัวยึดตำแหน่งที่ติดดาวไว้ท้ายสุดซึ่งจะดูดซับค่าที่เหลือทั้งหมดของช่วงในกรณีที่คุณไม่คำนึงถึงการสิ้นเปลืองหน่วยความจำและไม่สามารถนับได้:

dog, cat, rabbit, horse, *_ = range(100)

1
แต่นี่อาจใช้หน่วยความจำมากขึ้น!
MJ

ฉันไม่เห็นจุดของตัวยึดตำแหน่งที่ติดดาวเนื่องจาก Python จะตรวจสอบจำนวนค่าที่จะแกะออก (ดังนั้นจะนับจำนวนให้คุณ)
Gabriel Devillers

@ GabrielDevillers ฉันคิดว่า Python จะยกข้อยกเว้นหากมีจำนวนองค์ประกอบใน tuple ไม่ตรงกัน
Mark Harrison

1
อันที่จริงมันทำในการทดสอบของฉัน (Python2,3) แต่นั่นหมายความว่าข้อผิดพลาดการนับใด ๆ จากโปรแกรมเมอร์จะถูกจับในการทดสอบครั้งแรก (มีข้อความให้นับที่ถูกต้อง)
Gabriel Devillers

1
ฉันไม่สามารถนับได้ ตัวยึดตำแหน่งที่ติดดาวสามารถแก้ไขการเงินของฉันได้หรือไม่
javadba

131

ทางออกที่ดีที่สุดสำหรับคุณจะขึ้นอยู่กับสิ่งที่คุณต้องการจากคุณปลอม enum

enum ง่าย ๆ :

หากคุณต้องการenumรายชื่อที่ระบุถึงรายการต่างโซลูชันของMark Harrison (ด้านบน) นั้นยอดเยี่ยม:

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

การใช้ a rangeยังอนุญาตให้คุณตั้งค่าเริ่มต้น :

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

นอกเหนือจากข้างต้นหากคุณต้องการให้ไอเท็มนั้นเป็นของคอนเทนเนอร์บางประเภทให้ฝังไว้ในคลาส:

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

ในการใช้รายการ enum ตอนนี้คุณจะต้องใช้ชื่อคอนเทนเนอร์และชื่อรายการ:

stype = Stationery.Pen

คอมเพล็กซ์ enum:

สำหรับรายการที่ยาวของ enum หรือการใช้ enum ที่ซับซ้อนยิ่งขึ้นโซลูชันเหล่านี้จะไม่เพียงพอ คุณอาจจะมองไปที่สูตรโดยจะพัสดุสำหรับการจำลอง Enumerations ในหลามตีพิมพ์ในหลามตำรา รุ่นออนไลน์ของที่มีอยู่ที่นี่

ข้อมูลเพิ่มเติม:

PEP 354: การแจกแจงใน Pythonมีรายละเอียดที่น่าสนใจเกี่ยวกับข้อเสนอสำหรับ enum ใน Python และสาเหตุที่ถูกปฏิเสธ


7
ด้วยrangeคุณสามารถละเว้นอาร์กิวเมนต์แรกถ้าเป็น 0
ToonAlfrink

อีก enum my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))ปลอมที่เหมาะสมกับวัตถุประสงค์บางอย่าง จากนั้นmy_enumสามารถใช้ในการค้นหาเช่นmy_enum['Item0']สามารถเป็นดัชนีในลำดับ คุณอาจต้องการตัดผลลัพธ์ของstr.splitฟังก์ชันที่แสดงข้อผิดพลาดหากมีรายการที่ซ้ำกัน
Ana Nimbus

ดี! สำหรับ Flags คุณสามารถFlag1, Flag2, Flag3 = [2**i for i in range(3)]
majkelx

นี่คือคำตอบที่ดีที่สุด
Yuriy Pozniak

78

รูปแบบ typesafe enum ที่ใช้ใน Java pre-JDK 5 มีข้อดีหลายประการ เช่นเดียวกับคำตอบของ Alexandru คุณสร้างเขตข้อมูลคลาสและคลาสเป็นค่า enum อย่างไรก็ตามค่า enum เป็นอินสแตนซ์ของคลาสมากกว่าจำนวนเต็มขนาดเล็ก นี่คือข้อดีที่ค่า enum ของคุณไม่ได้เปรียบเทียบโดยไม่เท่ากับจำนวนเต็มขนาดเล็กคุณสามารถควบคุมวิธีการพิมพ์เพิ่มวิธีการโดยพลการหากมีประโยชน์และยืนยันโดยใช้ 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

กระทู้ล่าสุดของpython-devชี้ให้เห็นว่ามีไลบรารี enum อยู่สองสามรายการใน wild รวมถึง


16
ฉันคิดว่านี่เป็นวิธีที่ไม่ดีมาก Animal.DOG = สัตว์ ("สุนัข") Animal.DOG2 = สัตว์ ("สุนัข") ยืนยัน Animal.DOG == Animal.DOG2 ล้มเหลว ...
Confusion

11
@Confusion ผู้ใช้ไม่ควรเรียกตัวสร้างความจริงที่ว่าแม้กระทั่งตัวสร้างคือรายละเอียดการใช้งานและคุณต้องสื่อสารกับผู้ที่เคยใช้รหัสของคุณที่ทำให้ค่าการแจงนับใหม่ไม่มีเหตุผลและรหัสออกจะไม่เหมาะสม "ทำในสิ่งที่ถูกต้อง". แน่นอนว่าไม่ได้หยุดคุณจากการใช้ Animal.from_name ("dog") -> Animal.DOG
Aaron Maenpaa

13
"ข้อดีที่ค่า enum ของคุณไม่เปรียบเทียบโดยไม่ได้ตั้งใจเท่ากับจำนวนเต็มเล็กน้อย" ข้อดีคืออะไร มีอะไรผิดปกติเมื่อเปรียบเทียบ enum ของคุณกับจำนวนเต็ม? โดยเฉพาะอย่างยิ่งถ้าคุณเก็บ Enum ไว้ในฐานข้อมูลคุณมักจะต้องการให้มันเก็บเป็นจำนวนเต็มดังนั้นคุณจะต้องเปรียบเทียบมันกับจำนวนเต็มในบางจุด
ibz

3
@Aaaron Maenpaa แก้ไข. มันยังคงเป็นวิธีที่แตกหักและซับซ้อนเกินไปที่จะทำ
aaronasterling

4
@AaronMcSmooth ขึ้นอยู่กับว่าคุณมาจากมุมมอง C ของ "Enums เป็นเพียงชื่อของ ints สองสาม" หรือวิธีการเชิงวัตถุเพิ่มเติมที่ค่า enum เป็นวัตถุจริงและมีวิธี (ซึ่งเป็นวิธี enums ใน Java 1.5 คือและรูปแบบที่ปลอดภัยสำหรับประเภท enum) โดยส่วนตัวฉันไม่ชอบคำสั่งเปลี่ยนดังนั้นฉันจึงเอนตัวไปยังค่า enum ที่เป็นวัตถุจริง
Aaron Maenpaa

61

คลาส Enum สามารถเป็นหนึ่งซับได้

class Enum(tuple): __getattr__ = tuple.index

วิธีใช้ (การค้นหาไปข้างหน้าและย้อนกลับการค้นหาคีย์ค่ารายการ ฯลฯ )

>>> 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)]

ฉันคิดว่านี่เป็นทางออกที่งดงามที่สุดที่ง่ายที่สุด ใน python 2.4 (ใช่เซิร์ฟเวอร์เก่า) tuples ยังไม่มีดัชนี ฉันแก้ไขการแทนที่ด้วยรายการ
Massimo

ฉันลองสิ่งนี้ในสมุดบันทึก Jupyter และค้นพบว่ามันจะไม่ทำงานเป็นคำจำกัดความบรรทัดเดียว แต่การใส่คำนิยามgetattrบนบรรทัดที่สอง (เยื้อง) จะเป็นที่ยอมรับ
user5920660

วิธีการแก้ปัญหานี้ให้ฉันใช้inคำหลักเพื่อค้นหาสมาชิกที่เรียบร้อย ตัวอย่างการใช้งาน:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Farzad Abdolhosseini

51

ดังนั้นฉันเห็นด้วย อย่าบังคับใช้ความปลอดภัยใน Python แต่ฉันต้องการปกป้องตัวเองจากความผิดพลาดที่โง่เขลา แล้วเราคิดอย่างไรเกี่ยวกับเรื่องนี้?

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

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

มันทำให้ฉันจากการชนกันของมูลค่าในการกำหนด enums ของฉัน

>>> Animal.Cat
2

มีข้อได้เปรียบที่มีประโยชน์อื่น: การค้นหาแบบย้อนกลับที่รวดเร็วจริงๆ:

def name_of(self, i):
    return self.values[i]

ฉันชอบสิ่งนี้ แต่คุณอาจล็อคค่าเพื่อประสิทธิภาพด้วยสิ่งอันดับ? ผมเล่นรอบกับมันและมาพร้อมกับรุ่นที่ self.values ชุดจาก args ในinit Animal = Enum('horse', 'dog', 'cat')มันเป็นสิ่งที่ดีที่จะสามารถที่จะประกาศ ฉันยังจับ ValueError ในgetattrในกรณีที่มีรายการที่หายไปในตัวเองค่า - มันน่าจะดีกว่าที่จะยกระดับ AttributeError ด้วยสตริงชื่อที่ให้มาแทน ฉันไม่สามารถทำให้ metaclass ทำงานใน Python 2.7 ตามความรู้ที่ จำกัด ของฉันในพื้นที่นั้น แต่คลาส Enum ที่กำหนดเองของฉันทำงานได้ดีด้วยวิธีการแบบอินสแตนซ์โดยตรง
trojjer

49

Python ไม่มีคำสั่งในตัวenumและคำตอบอื่น ๆ มีแนวคิดสำหรับการนำไปใช้ของคุณเอง (คุณอาจสนใจรุ่นที่สูงกว่าใน Python ตำราอาหาร)

อย่างไรก็ตามในสถานการณ์ที่enumจะมีการเรียกใช้ใน C ฉันมักจะจบลงด้วยการใช้สตริงธรรมดา : เนื่องจากวิธีการใช้งานวัตถุ / คุณลักษณะ (C) Python ได้รับการปรับให้เหมาะสมกับการทำงานอย่างรวดเร็วด้วยสตริงสั้น ๆ ดังนั้นจึงไม่มี ไม่มีประโยชน์ใด ๆ กับการใช้จำนวนเต็ม เพื่อป้องกันการพิมพ์ผิด / ค่าที่ไม่ถูกต้องคุณสามารถแทรกการตรวจสอบในสถานที่ที่เลือก

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

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

(ข้อเสียอย่างหนึ่งเมื่อเทียบกับการใช้คลาสคือคุณเสียประโยชน์จากการเติมข้อความอัตโนมัติ)


2
ฉันชอบวิธีนี้ ฉันชอบที่จะใช้ชนิดในตัวที่เป็นไปได้
Seun Osewa

รุ่นนั้นไม่ได้อยู่ด้านบนสุดจริงๆ มันมีรหัสทดสอบมากมายที่ให้มา
Casebash

1
ที่จริงแล้วรุ่น "ถูกต้อง" อยู่ในความคิดเห็นและมีความซับซ้อนมากขึ้น - รุ่นหลักมีข้อผิดพลาดเล็กน้อย
Casebash

39

เมื่อวันที่ 2013-05-10 Guido ตกลงที่จะยอมรับPEP 435ในห้องสมุดมาตรฐาน Python 3.4 ซึ่งหมายความว่า Python ในที่สุดได้รับการสนับสนุนสำหรับการแจกแจง!

มีแบ็คพอร์ทสำหรับ Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 และ 2.4 มันอยู่ใน Pypi เป็นenum34

ประกาศ:

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

ตัวแทน:

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

ย้ำ:

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

การเข้าถึงแบบเป็นโปรแกรม:

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

สำหรับข้อมูลเพิ่มเติมโปรดดูที่ข้อเสนอ เอกสารที่เป็นทางการอาจจะตามมาในไม่ช้า


33

ฉันชอบกำหนด enums ใน Python ดังนี้:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

มันมีข้อผิดพลาดมากกว่าการใช้จำนวนเต็มเนื่องจากคุณไม่ต้องกังวลเกี่ยวกับการทำให้มั่นใจว่าจำนวนเต็มนั้นไม่ซ้ำกัน (เช่นถ้าคุณบอกว่า Dog = 1 และ Cat = 1 คุณจะถูกเมา)

มันมีข้อผิดพลาดมากกว่าการใช้สตริงเนื่องจากคุณไม่ต้องกังวลเกี่ยวกับการพิมพ์ผิด (เช่น x == "catt" ล้มเหลวอย่างเงียบ ๆ แต่ x == Animal.Catt เป็นข้อยกเว้นรันไทม์)


31
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()

ใช้แบบนี้:

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

หากคุณต้องการสัญลักษณ์ที่เป็นเอกลักษณ์และไม่สนใจค่าเหล่านี้ให้แทนที่บรรทัดนี้:

__metaclass__ = M_add_class_attribs(enumerate(names))

ด้วยสิ่งนี้:

__metaclass__ = M_add_class_attribs((object(), name) for name in names)

11
IMHO มันจะสะอาดกว่าถ้าคุณเปลี่ยนenum(names)ไปenum(*names)- จากนั้นคุณสามารถวางวงเล็บพิเศษเมื่อเรียกมันได้
คริสลัทซ์

ฉันชอบวิธีนี้ ฉันเปลี่ยนมันเพื่อตั้งค่าคุณลักษณะเป็นสตริงเดียวกันกับชื่อซึ่งมีคุณสมบัติที่ดีที่ Animal.DOG == 'DOG' ดังนั้นพวกเขาจึงสร้างสตริงให้กับคุณโดยอัตโนมัติ (ช่วยอย่างมากสำหรับการพิมพ์ออก debug)
Ted Mielczarek

23

จาก Python 3.4 จะมีการสนับสนุนอย่างเป็นทางการสำหรับ enums คุณสามารถค้นหาเอกสารและตัวอย่างที่นี่ในหน้าเอกสารหลาม 3.4

การแจกแจงถูกสร้างขึ้นโดยใช้คลาสไวยากรณ์ซึ่งทำให้ง่ายต่อการอ่านและเขียน วิธีการสร้างทางเลือกอธิบายไว้ใน Functional API ในการกำหนดการแจงนับ Subclass Enum เป็นดังนี้:

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

สนับสนุนการพอร์ตย้อนกลับในขณะนี้เช่นกัน นี่คือวิธีที่จะไป
srock

22

อืม ... ฉันคิดว่าสิ่งที่ใกล้เคียงที่สุดกับ enum น่าจะเป็นพจนานุกรมนิยามเช่นนี้:

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

หรือ

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

จากนั้นคุณสามารถใช้ชื่อสัญลักษณ์สำหรับค่าคงที่เช่นนี้:

mymonth = months['January']

มีตัวเลือกอื่น ๆ เช่นรายการของ tuples หรือ tuple ของ tuples แต่พจนานุกรมเป็นเพียงหนึ่งเดียวที่ให้คุณมีวิธี "สัญลักษณ์" (สตริงคงที่) ในการเข้าถึงค่า

แก้ไข: ฉันชอบคำตอบของ Alexandru ด้วย!


และที่สำคัญที่สุดคุณสามารถทำซ้ำในพจนานุกรมได้อย่างง่ายดายหากคุณต้องการเข้าถึงค่าของมันเช่นคุณต้องการค่าสตริงเพื่อให้ปรากฏเป็นรายการในกล่องคำสั่งผสม ดังนั้นให้ใช้พจนานุกรมแทนการแจกแจงแทน
LEMUEL ADANE

22

อีกวิธีที่ง่ายมากคือการนำ Enum ไปใช้ใน Python โดยใช้namedtuple:

from collections import namedtuple

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

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

หรืออีกวิธีหนึ่งคือ

# 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())

เช่นเดียวกับวิธีการด้านบนคลาสย่อยsetนี้จะช่วยให้:

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

แต่มีความยืดหยุ่นมากกว่าเนื่องจากสามารถมีคีย์และค่าต่างกัน สิ่งนี้ช่วยให้

MyEnum.FOO < MyEnum.BAR

เพื่อทำตามที่คาดไว้ถ้าคุณใช้รุ่นที่เติมค่าตัวเลขตามลำดับ


20

สิ่งที่ฉันใช้:

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))

วิธีใช้:

>>> 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'))

ดังนั้นนี่จะให้ค่าคงที่จำนวนเต็มเช่น state.PUBLISHED และสองทูเปิลเพื่อใช้เป็นตัวเลือกในโมเดล Django


17

davidg แนะนำให้ใช้ dicts ฉันจะไปอีกขั้นหนึ่งแล้วใช้ชุด:

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

ตอนนี้คุณสามารถทดสอบว่าค่าตรงกับหนึ่งในค่าในชุดเช่นนี้:

if m in months:

เช่น dF แต่ฉันมักจะใช้ค่าคงที่สตริงแทน enums


ดีกว่านี้มากถ้าคุณสืบทอดและตั้งค่าวิธีgetattr !
shahjapan

17

ง่าย ๆ เข้าไว้:

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

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

แล้ว:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

16

นี่คือสิ่งที่ดีที่สุดที่ฉันเคยเห็น: "First Class Enums in Python"

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

มันให้ชั้นเรียนของคุณและชั้นเรียนมี enums ทั้งหมด enums สามารถเปรียบเทียบกันได้ แต่ไม่มีค่าใด ๆ เป็นพิเศษ คุณไม่สามารถใช้มันเป็นค่าจำนวนเต็ม (ฉันต่อต้านสิ่งนี้ในตอนแรกเพราะฉันคุ้นเคยกับ C enums ซึ่งเป็นค่าจำนวนเต็ม แต่ถ้าคุณไม่สามารถใช้มันเป็นจำนวนเต็มคุณจะไม่สามารถใช้มันเป็นจำนวนเต็มโดยไม่ได้ตั้งใจดังนั้นโดยรวมแล้วฉันคิดว่ามันเป็นชัยชนะ .) แต่ละ enum เป็นค่าที่ไม่ซ้ำกัน คุณสามารถพิมพ์ enums คุณสามารถวนซ้ำพวกเขาคุณสามารถทดสอบว่าค่า enum คือ "ใน" enum มันค่อนข้างสมบูรณ์และเนียน

แก้ไข (cfi): ลิงค์ด้านบนไม่รองรับ Python 3 นี่คือพอร์ตของ enum.py ของฉันถึง 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)

สูตรนี้ใช้เป็นพื้นฐานสำหรับ PEP ซึ่งถูกปฏิเสธ python.org/dev/peps/pep-0354 นามสกุลหนึ่งที่ฉันชอบ: ค่า enum ควรมีตัวแปรสมาชิกที่ช่วยให้คุณได้รับค่าจำนวนเต็มภายในออก ไม่ควรโยนค่า enum เป็นจำนวนเต็มโดยไม่ได้ตั้งใจดังนั้น.__int__()วิธีการนี้ควรเพิ่มข้อยกเว้นสำหรับ enum แต่ควรมีวิธีในการรับค่าออก และควรตั้งค่าจำนวนเต็มเฉพาะในเวลาที่กำหนดคลาสดังนั้นคุณสามารถใช้ enum สำหรับสิ่งต่าง ๆ เช่นค่าคงที่ใน statโมดูล
steveha

14

ฉันมีโอกาสที่จะต้องใช้คลาส Enum เพื่อวัตถุประสงค์ในการถอดรหัสรูปแบบไฟล์ไบนารี มีฉันเกิดขึ้นเพื่อต้องการคือความหมายกระชับ enum ความสามารถในการสร้างได้อย่างอิสระกรณีของ enum โดยค่าทั้งจำนวนเต็มหรือสตริงและมีประโยชน์representation นี่คือสิ่งที่ฉันลงเอยด้วย:

>>> 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
... 

ตัวอย่างที่แปลกของการใช้มัน:

>>> 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

ฟีเจอร์หลัก:

  • str(), int()และrepr()การผลิตทั้งหมดที่ส่งออกมีประโยชน์มากที่สุดตามลำดับชื่อของ enumartion ค่าจำนวนเต็มของตนและการแสดงออกงูหลามที่ประเมินกลับไปนับ
  • ค่าที่ส่งคืนโดยตัวสร้างจะถูก จำกัด อย่างเคร่งครัดกับค่าที่กำหนดไว้ล่วงหน้าไม่มีค่า enum โดยไม่ตั้งใจ
  • ค่าที่แจกแจงคือ singletons; พวกเขาสามารถเปรียบเทียบอย่างเคร่งครัดกับis

ฉันชอบการใช้ superclass กับ metaclass ของตัวเองเพื่อให้ง่ายต่อการกำหนด enums สิ่งที่หายไปที่นี่คือวิธี __contain__ ฉันต้องการที่จะสามารถตรวจสอบว่าตัวแปรที่กำหนดเป็นส่วนหนึ่งของ enum - ส่วนใหญ่เป็นเพราะฉันต้องการ enums สำหรับค่าที่อนุญาตของพารามิเตอร์ฟังก์ชั่น
xorsyst

อันที่จริงแล้วนี่เป็นเวอร์ชันที่ถูกตัดทอนเล็กน้อยของรุ่นแรกที่ฉันสร้างขึ้น (ซึ่งคุณสามารถค้นหาได้ที่นี่: enum_strict.py ) v ซึ่งกำหนด__instancecheck__วิธีการ คลาสไม่ใช่ชุดของอินสแตนซ์ดังนั้นจึง1 in Fruitไร้สาระ อย่างไรก็ตามเวอร์ชันที่เชื่อมโยงรองรับisinstance(1, Fruit)ซึ่งจะมีความถูกต้องมากขึ้นในแง่ของแนวคิดของคลาสและอินสแตนซ์
SingleNegationElimination

แต่ลืมเรียนและคิดในแง่ของ enums แล้วมันทำให้รู้สึกคิดว่าพวกเขาเป็นคอลเลกชัน ตัวอย่างเช่นฉันอาจมี enum ของโหมดการเปิดไฟล์ (MODE.OPEN, MODE.WRITE เป็นต้น) ฉันต้องการตรวจสอบพารามิเตอร์กับฟังก์ชั่นของฉัน: ถ้าโหมดใน MODE:อ่านได้ดีกว่าisintance (โหมด, โหมด)
xorsyst

ฉันได้วางบางสิ่งที่คล้ายกันมากซึ่งรองรับมากกว่าแค่ ints และได้รับการบันทึกและทดสอบบน GitHub: github.com/hmeine/named_constants
hans_meine

12

มาตรฐานใหม่ใน Python คือPEP 435ดังนั้นคลาส Enum จะพร้อมใช้งานใน Python เวอร์ชันอนาคต:

>>> from enum import Enum

อย่างไรก็ตามเพื่อเริ่มใช้งานตอนนี้คุณสามารถติดตั้งไลบรารี่ดั้งเดิมที่เป็นตัวกระตุ้น PEP:

$ pip install flufl.enum

จากนั้นคุณสามารถใช้มันตามคู่มือออนไลน์ :

>>> 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

10
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

หากคุณตั้งชื่อเป็นปัญหาของคุณ แต่หากไม่ได้สร้างวัตถุแทนค่าให้คุณทำสิ่งนี้:

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

เมื่อใช้ implementations อื่น ๆ ที่วางไว้ที่นี่ (รวมถึงเมื่อใช้อินสแตนซ์ที่มีชื่อในตัวอย่างของฉัน) คุณต้องแน่ใจว่าคุณไม่เคยลองเปรียบเทียบวัตถุจาก enums ที่แตกต่างกัน สำหรับต่อไปนี้เป็นข้อผิดพลาดที่เป็นไปได้:

>>> 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

อ๊ะ!


9

ฉันชอบโซลูชันของ Alec Thomas (http://stackoverflow.com/a/1695250):

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

มันดูหรูหราและสะอาดตา แต่เป็นเพียงฟังก์ชั่นที่สร้างคลาสด้วยคุณสมบัติที่ระบุ

ด้วยการปรับเปลี่ยนฟังก์ชั่นเล็กน้อยเราสามารถทำให้ 'enumy' เพิ่มมากขึ้น:

หมายเหตุ: ฉันสร้างตัวอย่างต่อไปนี้โดยพยายามสร้างลักษณะการทำงานของสไตล์ใหม่ของ pygtk 'enums' (เช่น 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

สิ่งนี้จะสร้าง enum ตามประเภทที่ระบุ นอกเหนือจากการให้สิทธิ์การเข้าถึงคุณลักษณะเช่นฟังก์ชันก่อนหน้านี้แล้วมันจะทำงานตามที่คุณคาดหวังว่า Enum จะต้องคำนึงถึงประเภท นอกจากนี้ยังสืบทอดคลาสฐาน

ตัวอย่างเช่นจำนวนเต็ม 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

อีกสิ่งที่น่าสนใจที่สามารถทำได้ด้วยวิธีนี้คือการปรับแต่งพฤติกรรมที่เฉพาะเจาะจงโดยการแทนที่วิธีการในตัว:

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'

นี้ "ฐาน" ประเภทความคิดเป็นระเบียบ :)
MestreLion

ใช่ทราบว่าคุณสามารถทำได้ด้วย Python 3.4 Enum ใหม่: python.org/dev/peps/pep-0435/#other-derived-enumerations
bj0

7

แพคเกจ Enum จากPyPIให้การใช้งาน enums อย่างมีประสิทธิภาพ คำตอบก่อนหน้านี้ที่กล่าวถึง PEP 354; ถูกปฏิเสธ แต่ข้อเสนอได้ดำเนินการ http://pypi.python.org/pypi/enum

การใช้งานง่ายและสง่างาม:

>>> 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

คำแนะนำของ Alexandru ในการใช้ค่าคงที่คลาสสำหรับ enums ทำงานได้ค่อนข้างดี

ฉันยังต้องการเพิ่มพจนานุกรมสำหรับค่าคงที่แต่ละชุดเพื่อค้นหาการแทนค่าสตริงที่มนุษย์สามารถอ่านได้

สิ่งนี้มีจุดประสงค์สองประการ: ก) เป็นวิธีที่ง่ายในการพิมพ์ enum ของคุณและ b) พจนานุกรมจัดกลุ่มค่าคงที่ทางตรรกะเพื่อให้คุณสามารถทดสอบการเป็นสมาชิก

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

นี่คือวิธีการที่มีคุณสมบัติแตกต่างกันที่ฉันพบว่ามีค่า:

  • อนุญาตให้> และ <การเปรียบเทียบตามลำดับใน enum ไม่ใช่คำศัพท์
  • สามารถระบุรายการโดยใช้ชื่อทรัพย์สินหรือดัชนี: xa, x ['a'] หรือ x [0]
  • รองรับการแบ่งส่วนข้อมูลเช่น [:] หรือ [-1]

และที่สำคัญที่สุดคือป้องกันการเปรียบเทียบระหว่าง enums ประเภทต่าง ๆ !

ตามอย่างใกล้ชิดในhttp://code.activestate.com/recipes/413486-first-class-enums-in-python

มีการสอนมากมายที่นี่เพื่อแสดงให้เห็นถึงความแตกต่างของวิธีการนี้

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


3

วิธีนี้เป็นวิธีง่ายๆในการรับคลาสสำหรับการแจงนับที่กำหนดเป็นรายการ (ไม่มีการกำหนดจำนวนเต็มที่น่ารำคาญ):

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',
))

2
นี่เป็นวิธีที่ล้าสมัยในการสร้างคลาส ทำไมไม่ใช้เพียงแค่type(class_name, (object,), dict(...))แทน
ปลายทาง

3

ในขณะที่ข้อเสนอ Enum ดั้งเดิมPEP 354ถูกปฏิเสธเมื่อหลายปีก่อนมันกลับมาอย่างต่อเนื่อง Enum บางชนิดมีจุดประสงค์เพื่อเพิ่มเป็น 3.2 แต่ถูกผลักกลับไปที่ 3.3 แล้วลืมไป และตอนนี้มีPEP 435สำหรับการรวมใน Python 3.4 การดำเนินการอ้างอิงของ PEP flufl.enum435

เมื่อวันที่เมษายน 2013 ดูเหมือนว่าจะมีฉันทามติทั่วไปว่าควรเพิ่มบางสิ่งลงในไลบรารีมาตรฐานใน 3.4 ตราบใดที่ผู้คนสามารถเห็นด้วยกับสิ่งที่ "บางอย่าง" ควรจะเป็น นั่นเป็นส่วนที่ยาก ดูกระทู้เริ่มต้นที่นี่และที่นี่และอีกครึ่งโหลกระทู้อื่น ๆ ในช่วงต้นเดือน 2013

ในขณะเดียวกันเวลานี้ขึ้นมาทุกฆ่าของการออกแบบใหม่และการใช้งานปรากฏบน PyPI, ActiveState ฯลฯ ดังนั้นหากคุณไม่ชอบการออกแบบ FLUFL ลองค้นหา PyPI


3

ใช้สิ่งต่อไปนี้

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']

ฉันใช้มันสำหรับตัวเลือกรุ่นDjangoและมันก็ดูไพเราะมาก มันไม่ใช่ Enum จริงๆ แต่ทำงานได้

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