ฉันเป็นนักพัฒนาซอฟต์แวร์ C # เป็นหลัก แต่ปัจจุบันฉันทำงานโครงการใน Python
ฉันจะเป็นตัวแทนของ Enum ใน Python ได้อย่างไร
ฉันเป็นนักพัฒนาซอฟต์แวร์ C # เป็นหลัก แต่ปัจจุบันฉันทำงานโครงการใน Python
ฉันจะเป็นตัวแทนของ Enum ใน Python ได้อย่างไร
คำตอบ:
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'
**named
) ในฟังก์ชั่น enum สำหรับเวอร์ชั่นเก่าคือการรองรับค่าที่กำหนดเอง:enum("blue", "red", "green", black=0)
ก่อนหน้า PEP 435 Python ไม่มีอะไรเทียบเท่า แต่คุณสามารถนำไปปฏิบัติใช้เองได้
ตัวฉันเองฉันชอบทำให้มันง่าย (ฉันเคยเห็นตัวอย่างที่ซับซ้อนอย่างน่ากลัวในเน็ต) บางอย่างเช่นนี้ ...
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
ใน Python 3.4 ( PEP 435 ) คุณสามารถทำให้Enumเป็นคลาสพื้นฐานได้ นี่ทำให้คุณมีฟังก์ชั่นพิเศษเพิ่มอีกเล็กน้อยซึ่งอธิบายไว้ใน PEP ยกตัวอย่างเช่นสมาชิก enum แตกต่างจากจำนวนเต็มและพวกเขาจะประกอบไปด้วยและname
value
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
object()
ฉันจะใช้นี้มีตัวเลขที่ถูกแทนที่ด้วย
นี่คือการดำเนินการอย่างหนึ่ง:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
นี่คือการใช้งาน:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
__setattr__(self, name, value)
และบางที__delattr__(self, name)
ถ้าคุณตั้งใจเขียนAnimals.DOG = CAT
มันจะไม่ประสบความสำเร็จ
Animals.DOG
; นอกจากนี้ค่าของ constats เป็นสตริงดังนั้นการเปรียบเทียบกับค่าคงที่เหล่านี้จะช้ากว่าถ้าพูดอนุญาตให้จำนวนเต็มเป็นค่า
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
try-except
บล็อกได้อย่างไร?
หากคุณต้องการค่าตัวเลขนี่คือวิธีที่เร็วที่สุด:
dog, cat, rabbit = range(3)
ใน Python 3.x คุณสามารถเพิ่มตัวยึดตำแหน่งที่ติดดาวไว้ท้ายสุดซึ่งจะดูดซับค่าที่เหลือทั้งหมดของช่วงในกรณีที่คุณไม่คำนึงถึงการสิ้นเปลืองหน่วยความจำและไม่สามารถนับได้:
dog, cat, rabbit, horse, *_ = range(100)
ทางออกที่ดีที่สุดสำหรับคุณจะขึ้นอยู่กับสิ่งที่คุณต้องการจากคุณปลอม 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 และสาเหตุที่ถูกปฏิเสธ
range
คุณสามารถละเว้นอาร์กิวเมนต์แรกถ้าเป็น 0
my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
ปลอมที่เหมาะสมกับวัตถุประสงค์บางอย่าง จากนั้นmy_enum
สามารถใช้ในการค้นหาเช่นmy_enum['Item0']
สามารถเป็นดัชนีในลำดับ คุณอาจต้องการตัดผลลัพธ์ของstr.split
ฟังก์ชันที่แสดงข้อผิดพลาดหากมีรายการที่ซ้ำกัน
Flag1, Flag2, Flag3 = [2**i for i in range(3)]
รูปแบบ 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 รวมถึง
คลาส 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)]
in
คำหลักเพื่อค้นหาสมาชิกที่เรียบร้อย ตัวอย่างการใช้งาน:'Claimed' in Enum(['Unclaimed', 'Claimed'])
ดังนั้นฉันเห็นด้วย อย่าบังคับใช้ความปลอดภัยใน 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]
Animal = Enum('horse', 'dog', 'cat')
มันเป็นสิ่งที่ดีที่จะสามารถที่จะประกาศ ฉันยังจับ ValueError ในgetattrในกรณีที่มีรายการที่หายไปในตัวเองค่า - มันน่าจะดีกว่าที่จะยกระดับ AttributeError ด้วยสตริงชื่อที่ให้มาแทน ฉันไม่สามารถทำให้ metaclass ทำงานใน Python 2.7 ตามความรู้ที่ จำกัด ของฉันในพื้นที่นั้น แต่คลาส Enum ที่กำหนดเองของฉันทำงานได้ดีด้วยวิธีการแบบอินสแตนซ์โดยตรง
Python ไม่มีคำสั่งในตัวenum
และคำตอบอื่น ๆ มีแนวคิดสำหรับการนำไปใช้ของคุณเอง (คุณอาจสนใจรุ่นที่สูงกว่าใน Python ตำราอาหาร)
อย่างไรก็ตามในสถานการณ์ที่enum
จะมีการเรียกใช้ใน C ฉันมักจะจบลงด้วยการใช้สตริงธรรมดา : เนื่องจากวิธีการใช้งานวัตถุ / คุณลักษณะ (C) Python ได้รับการปรับให้เหมาะสมกับการทำงานอย่างรวดเร็วด้วยสตริงสั้น ๆ ดังนั้นจึงไม่มี ไม่มีประโยชน์ใด ๆ กับการใช้จำนวนเต็ม เพื่อป้องกันการพิมพ์ผิด / ค่าที่ไม่ถูกต้องคุณสามารถแทรกการตรวจสอบในสถานที่ที่เลือก
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(ข้อเสียอย่างหนึ่งเมื่อเทียบกับการใช้คลาสคือคุณเสียประโยชน์จากการเติมข้อความอัตโนมัติ)
เมื่อวันที่ 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
สำหรับข้อมูลเพิ่มเติมโปรดดูที่ข้อเสนอ เอกสารที่เป็นทางการอาจจะตามมาในไม่ช้า
ฉันชอบกำหนด enums ใน Python ดังนี้:
class Animal:
class Dog: pass
class Cat: pass
x = Animal.Dog
มันมีข้อผิดพลาดมากกว่าการใช้จำนวนเต็มเนื่องจากคุณไม่ต้องกังวลเกี่ยวกับการทำให้มั่นใจว่าจำนวนเต็มนั้นไม่ซ้ำกัน (เช่นถ้าคุณบอกว่า Dog = 1 และ Cat = 1 คุณจะถูกเมา)
มันมีข้อผิดพลาดมากกว่าการใช้สตริงเนื่องจากคุณไม่ต้องกังวลเกี่ยวกับการพิมพ์ผิด (เช่น x == "catt" ล้มเหลวอย่างเงียบ ๆ แต่ x == Animal.Catt เป็นข้อยกเว้นรันไทม์)
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)
enum(names)
ไปenum(*names)
- จากนั้นคุณสามารถวางวงเล็บพิเศษเมื่อเรียกมันได้
จาก Python 3.4 จะมีการสนับสนุนอย่างเป็นทางการสำหรับ enums คุณสามารถค้นหาเอกสารและตัวอย่างที่นี่ในหน้าเอกสารหลาม 3.4
การแจกแจงถูกสร้างขึ้นโดยใช้คลาสไวยากรณ์ซึ่งทำให้ง่ายต่อการอ่านและเขียน วิธีการสร้างทางเลือกอธิบายไว้ใน Functional API ในการกำหนดการแจงนับ Subclass Enum เป็นดังนี้:
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
อืม ... ฉันคิดว่าสิ่งที่ใกล้เคียงที่สุดกับ enum น่าจะเป็นพจนานุกรมนิยามเช่นนี้:
months = {
'January': 1,
'February': 2,
...
}
หรือ
months = dict(
January=1,
February=2,
...
)
จากนั้นคุณสามารถใช้ชื่อสัญลักษณ์สำหรับค่าคงที่เช่นนี้:
mymonth = months['January']
มีตัวเลือกอื่น ๆ เช่นรายการของ tuples หรือ tuple ของ tuples แต่พจนานุกรมเป็นเพียงหนึ่งเดียวที่ให้คุณมีวิธี "สัญลักษณ์" (สตริงคงที่) ในการเข้าถึงค่า
แก้ไข: ฉันชอบคำตอบของ Alexandru ด้วย!
อีกวิธีที่ง่ายมากคือการนำ 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
เพื่อทำตามที่คาดไว้ถ้าคุณใช้รุ่นที่เติมค่าตัวเลขตามลำดับ
สิ่งที่ฉันใช้:
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
davidg แนะนำให้ใช้ dicts ฉันจะไปอีกขั้นหนึ่งแล้วใช้ชุด:
months = set('January', 'February', ..., 'December')
ตอนนี้คุณสามารถทดสอบว่าค่าตรงกับหนึ่งในค่าในชุดเช่นนี้:
if m in months:
เช่น dF แต่ฉันมักจะใช้ค่าคงที่สตริงแทน enums
ง่าย ๆ เข้าไว้:
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
นี่คือสิ่งที่ดีที่สุดที่ฉันเคยเห็น: "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)
.__int__()
วิธีการนี้ควรเพิ่มข้อยกเว้นสำหรับ enum แต่ควรมีวิธีในการรับค่าออก และควรตั้งค่าจำนวนเต็มเฉพาะในเวลาที่กำหนดคลาสดังนั้นคุณสามารถใช้ enum สำหรับสิ่งต่าง ๆ เช่นค่าคงที่ใน stat
โมดูล
ฉันมีโอกาสที่จะต้องใช้คลาส Enum เพื่อวัตถุประสงค์ในการถอดรหัสรูปแบบไฟล์ไบนารี มีฉันเกิดขึ้นเพื่อต้องการคือความหมายกระชับ enum ความสามารถในการสร้างได้อย่างอิสระกรณีของ enum โดยค่าทั้งจำนวนเต็มหรือสตริงและมีประโยชน์repr
esentation นี่คือสิ่งที่ฉันลงเอยด้วย:
>>> 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 ค่าจำนวนเต็มของตนและการแสดงออกงูหลามที่ประเมินกลับไปนับis
__instancecheck__
วิธีการ คลาสไม่ใช่ชุดของอินสแตนซ์ดังนั้นจึง1 in Fruit
ไร้สาระ อย่างไรก็ตามเวอร์ชันที่เชื่อมโยงรองรับisinstance(1, Fruit)
ซึ่งจะมีความถูกต้องมากขึ้นในแง่ของแนวคิดของคลาสและอินสแตนซ์
มาตรฐานใหม่ใน 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
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
อ๊ะ!
ฉันชอบโซลูชันของ 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'
แพคเกจ 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'
คำแนะนำของ 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())
นี่คือวิธีการที่มีคุณสมบัติแตกต่างกันที่ฉันพบว่ามีค่า:
และที่สำคัญที่สุดคือป้องกันการเปรียบเทียบระหว่าง 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
นี่คือตัวแปรในโซลูชันของ 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
วิธีนี้เป็นวิธีง่ายๆในการรับคลาสสำหรับการแจงนับที่กำหนดเป็นรายการ (ไม่มีการกำหนดจำนวนเต็มที่น่ารำคาญ):
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',
))
type(class_name, (object,), dict(...))
แทน
ในขณะที่ข้อเสนอ Enum ดั้งเดิมPEP 354ถูกปฏิเสธเมื่อหลายปีก่อนมันกลับมาอย่างต่อเนื่อง Enum บางชนิดมีจุดประสงค์เพื่อเพิ่มเป็น 3.2 แต่ถูกผลักกลับไปที่ 3.3 แล้วลืมไป และตอนนี้มีPEP 435สำหรับการรวมใน Python 3.4 การดำเนินการอ้างอิงของ PEP flufl.enum
435
เมื่อวันที่เมษายน 2013 ดูเหมือนว่าจะมีฉันทามติทั่วไปว่าควรเพิ่มบางสิ่งลงในไลบรารีมาตรฐานใน 3.4 ตราบใดที่ผู้คนสามารถเห็นด้วยกับสิ่งที่ "บางอย่าง" ควรจะเป็น นั่นเป็นส่วนที่ยาก ดูกระทู้เริ่มต้นที่นี่และที่นี่และอีกครึ่งโหลกระทู้อื่น ๆ ในช่วงต้นเดือน 2013
ในขณะเดียวกันเวลานี้ขึ้นมาทุกฆ่าของการออกแบบใหม่และการใช้งานปรากฏบน PyPI, ActiveState ฯลฯ ดังนั้นหากคุณไม่ชอบการออกแบบ FLUFL ลองค้นหา PyPI
ใช้สิ่งต่อไปนี้
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 จริงๆ แต่ทำงานได้