Python'da bir "Numaralandırma" yı nasıl temsil edebilirim?


1143

Ben esas olarak bir C # geliştiricisiyim ama şu anda Python'daki bir proje üzerinde çalışıyorum.

Python'da bir Enum eşdeğerini nasıl temsil edebilirim?

Yanıtlar:


2688

PEP 435'te açıklandığı gibi numaralandırmalar Python 3.4'e eklenmiştir . Ayrıca, pypi üzerinde 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 ve 2.4'e rapor edilmiştir.

Daha gelişmiş Enum teknikleri için aenum kütüphanesini deneyin (2.7, 3.3+, aynı yazar enum34. Kod py2 ve py3 arasında mükemmel uyumlu değildir, örneğin __order__python 2'de ihtiyacınız olacak ).

  • Kullanmak için enum34, do$ pip install enum34
  • Kullanmak için aenum, do$ pip install aenum

Yükleme enum(numara yok) tamamen farklı ve uyumsuz bir sürüm yükler.


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)

Veya eşdeğer olarak:

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

Önceki sürümlerde, numaralandırmayı gerçekleştirmenin bir yolu:

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

Bu şekilde kullanılır:

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

Ayrıca otomatik numaralandırmayı aşağıdaki gibi bir şeyle kolayca destekleyebilirsiniz:

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

ve şu şekilde kullanılır:

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

Değerleri tekrar isimlere dönüştürme desteği şu şekilde eklenebilir:

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)

Bu, bu ada sahip herhangi bir şeyin üzerine yazar, ancak numaralarınızı çıktıda oluşturmak için kullanışlıdır. Ters eşleme yoksa KeyError atar. İlk örnekle:

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

1
Anlayamadım, neden enum (* sıralı, ** adlandırılmış) yönteminde kwargs (** adlı) geçtiler? Lütfen açıklar. Kwargs olmadan da çalışır. Ben kontrol ettim.
Seenu S

Python 2 işlevini Python 3'ün işlevsel Enum API'sı (ad, değerler) ile uyumlu olacak şekilde güncellemek güzel olurdu
bscan

**namedEski sürümler için numaralandırma işlevindeki var kwargs ( ) özel değerleri desteklemektir:enum("blue", "red", "green", black=0)
Éric Araujo

823

PEP 435'ten önce, Python'un eşdeğeri yoktu ama kendinizinkini uygulayabilirsiniz.

Kendimi, basit tutmayı seviyorum (internette korkunç karmaşık örnekler gördüm), böyle bir şey ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4'te ( PEP 435 ), Enum'u temel sınıf yapabilirsiniz. Bu size PEP'te açıklanan biraz ekstra işlevsellik kazandırır. Örneğin, enum üyeleri tamsayılardan farklıdır ve a nameve a'dan oluşurlar value.

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

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

print(Animal.DOG.value)
# 1

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

Değerleri yazmak istemiyorsanız, aşağıdaki kısayolu kullanın:

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

Enumuygulamalar listelere dönüştürülebilir ve yinelenebilir . Üyelerinin sırası beyan düzenidir ve değerleriyle ilgisi yoktur. Örneğin:

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
Hayır, bu bir sınıf değişkeni.
Georg Schölly

246
Python varsayılan olarak dinamiktir. Derleme zamanı güvenliğini Python gibi bir dilde, özellikle de yokken uygulamak için geçerli bir neden yoktur. Ve başka bir şey ... iyi bir model sadece yaratıldığı bağlamda iyidir. Kullandığınız araçlara bağlı olarak, iyi bir kalıp yerine geçebilir veya tamamen yararsız olabilir.
Alexandru Nedelcu

20
@Longpoke 100 değeriniz varsa, o zaman kesinlikle yanlış bir şey yapıyorsunuz;) Numaralarımla ilişkili sayıları severim ... yazmaları kolaydır (dizelere karşı), bir veritabanında kolayca kalıcı olabilir ve uyumludur C / C ++ numaralandırması, daha kolay marşlamayı kolaylaştırır.
Alexandru Nedelcu

50
Bunu, sayıların yerini alacak şekilde kullanıyorum object().
Tobu

9
Orijinal PEP354 artık yalnızca reddedilmekle kalmayıp artık yerini almıştır. PEP435, Python 3.4 için standart bir Numaralandırma ekler. Bkz. Python.org/dev/peps/pep-0435
Peter Hansen

322

İşte bir uygulama:

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

İşte kullanımı:

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

print(Animals.DOG)

51
Mükemmel. Bu, geçersiz kılma ile daha da geliştirilebilir __setattr__(self, name, value)ve belki de __delattr__(self, name)yanlışlıkla yazarsanız Animals.DOG = CAT, sessizce başarılı olmaz.
Joonas Pulakka

15
@shahjapan: İlginç, ancak nispeten yavaş: her erişim için bir test yapılır Animals.DOG; ayrıca, sabitlerin değerleri dizedir, böylece bu sabitlerle karşılaştırmalar, örneğin tamsayılara değer olarak izin verilenden daha yavaştır.
Eric O Lebigot

3
@shahjapan: Bu çözümün, örneğin Alexandru veya Mark'ın daha kısa çözümleri kadar okunaklı olmadığını iddia ediyorum. Yine de ilginç bir çözüm. :)
Eric O Lebigot

Ben overidding yöntemi yerine yöntemi setattr()içinde işlev kullanarak çalıştı . Bunun aynı şekilde çalıştığını varsayıyorum: sınıf Enum (nesne): def __init __ (self, enum_string_list): eğer type (enum_string_list) == list: enum_string için enum_string_list: setattr (self, enum_string, enum_string) başka: yükselt AttributeError__init__()__getattr__()
Harshith JV

8
@ AndréTerra: Set üyeliğini bir try-exceptblokta nasıl kontrol edersiniz ?
bgusach

210

Sayısal değerlere ihtiyacınız varsa, en hızlı yol şudur:

dog, cat, rabbit = range(3)

Python 3.x'te ayrıca, sonunda bellek israfını önemsememeniz ve sayamayacağınız durumlarda aralığın kalan tüm değerlerini emecek olan yıldızlı bir yer tutucu ekleyebilirsiniz.

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

1
Ama bu daha fazla hafıza gerektirebilir!
MJ

Python'un paketlenecek değer sayısını kontrol edeceği göz önüne alındığında, yıldızlı yer tutucunun noktasını görmüyorum (böylece sayımı sizin için yapacak).
Gabriel Devillers

@GabrielDevillers, sanırım atamak için gruptaki elemanların sayısı konusunda bir uyumsuzluk varsa Python bir istisna oluşturacaktır.
Mark Harrison

1
Gerçekten, benim testimde (Python2,3) yapar, ancak bu, programcıdan herhangi bir sayım hatasının ilk testte yakalanacağı anlamına gelir (doğru sayıyı veren bir mesajla).
Gabriel Devillers

1
Ben sayamam. Yıldızlı yer tutucu da mali durumumu sabitleyebilir mi?
javadba

131

Sizin için en iyi çözüm sahte neye ihtiyacınız olduğuna bağlıdır enum.

Basit numaralandırma:

enumYalnızca farklı öğeleri tanımlayan bir ad listesine ihtiyacınız varsa , Mark Harrison'ın çözümü (yukarıda) harika:

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

A kullanmak, rangeherhangi bir başlangıç ​​değeri ayarlamanızı da sağlar :

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

Yukarıdakilere ek olarak, öğelerin bir çeşit konteynere ait olmasını da istiyorsanız , bunları bir sınıfa gömün:

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

Numaralandırma öğesini kullanmak için artık kapsayıcı adını ve öğe adını kullanmanız gerekir:

stype = Stationery.Pen

Karmaşık numaralandırma:

Uzun enum listeleri veya enum'un daha karmaşık kullanımları için bu çözümler yeterli olmayacaktır. Python Yemek Kitabında yayınlanan Python'da Numaralandırma Simülasyonu için Will Ware tarifine bakabilirsiniz . Bunun çevrimiçi bir sürümünü burada bulabilirsiniz .

Daha fazla bilgi:

PEP 354: Python'daki numaralandırmalar, Python'daki numaralandırma teklifinin ve neden reddedildiğinin ilginç ayrıntılarına sahiptir.


7
ile range0 ise ilk argüman atlayabilirsiniz
ToonAlfrink

Bazı amaçlara uyan bir başka sahte numaralandırma my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2')))). Daha sonra my_enumaramada kullanılabilir, örneğin my_enum['Item0']bir diziye bir indeks olabilir. str.splitHerhangi bir kopya varsa bir istisna atan bir fonksiyonun sonucunu sarmak isteyebilirsiniz .
Ana Nimbus

Güzel! Bayraklar için yapabilirsinizFlag1, Flag2, Flag3 = [2**i for i in range(3)]
majkelx

En iyi cevap bu
Yuriy Pozniak

78

Java JDK öncesi 5'de kullanılan tipafe enum paterninin birçok avantajı vardır. Alexandru'nun cevabında olduğu gibi, bir sınıf yaratırsınız ve sınıf düzeyinde alanlar enum değerleridir; ancak, enum değerleri küçük tamsayılar yerine sınıfın örnekleridir. Bunun avantajı, enum değerlerinizin yanlışlıkla küçük tamsayılarla eşit olmamasıdır, nasıl yazdırılacağını kontrol edebilir, faydalıysa keyfi yöntemler ekleyebilir ve isinstance kullanarak iddialarda bulunabilirsiniz:

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 ile ilgili yeni bir iş parçacığı , vahşi doğada birkaç enum kütüphanesi olduğunu belirtti:


16
Bence bu çok kötü bir yaklaşım. Animal.DOG = Hayvan ("köpek") Animal.DOG2 = Hayvan ("köpek") iddia Animal.DOG == Animal.DOG2 başarısız ...
Karışıklık

11
@ Confusion Kullanıcının kurucuyu çağırması gerekmez, hatta bir kurucu bile bir uygulama detayıdır ve kodunuzu kimin kullandığını yeni numaralandırma değerleri oluşturmanın hiçbir anlamı olmadığını ve koddan çıkmanın "Doğru şeyi yapmak". Tabii ki bu sizi Animal.from_name ("dog") -> Animal.DOG uygulamasını engellemez.
Aaron Maenpaa

13
"Enum değerlerinizin yanlışlıkla küçük tamsayılarla eşit olmaması avantajı" Bunun avantajı nedir? Numaralandırmayı tamsayılarla karşılaştırmanın nesi yanlış? Özellikle enum'u veritabanında saklarsanız, genellikle tamsayı olarak saklanmasını istersiniz, bu nedenle onu bir noktada tamsayılarla karşılaştırmanız gerekir.
ibz

3
@Aaron Maenpaa. doğru. Bunu yapmak için hala kırık ve aşırı karmaşık bir yol.
aaronasterling

4
@AaronMcSmooth Bu gerçekten "Numaralandırmalar sadece birkaç ints için isimlerdir" ya da numaralandırma değerlerinin gerçek nesneler olduğu ve yöntemlere sahip olduğu nesneye yönelik yaklaşımdan (Java'daki numaralandırmaların) 1.5, ve hangi tip güvenli enum paterni için gidiyordu). Şahsen, switch ifadelerini sevmiyorum, bu yüzden gerçek nesneler olan enum değerlerine yaslanıyorum.
Aaron Maenpaa

61

Bir Enum sınıfı bir astar olabilir.

class Enum(tuple): __getattr__ = tuple.index

Nasıl kullanılır (ileri ve geri arama, anahtarlar, değerler, öğeler vb.)

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

Bence bu en basit ve en zarif çözüm. Python 2.4'te (evet, eski eski sunucu) tuples dizine eklenmedi. Listeyle değiştirmeyi çözdüm.
Massimo

Bunu bir Jupyter not defterinde denedim ve tek satırlık bir tanım olarak işe yaramayacağını, ancak getattr tanımını ikinci (girintili) bir satıra koymanın kabul edileceğini keşfettim .
user5920660

Bu çözüm in, düzenli üyeleri aramak için anahtar kelimeyi kullanmama izin verin . Örnek kullanım:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Farzad Abdolhosseini

51

Ben de katılıyorum. Python'da tip güvenliğini zorunlu kılmayalım, ama kendimi aptalca hatalardan korumak istiyorum. Peki bunun hakkında ne düşünüyoruz?

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

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

Numaralarımı tanımlarken beni değer-çarpışmadan koruyor.

>>> Animal.Cat
2

Bir başka kullanışlı avantaj daha var: gerçekten hızlı geri aramalar:

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

Bu hoşuma gitti, ama aynı zamanda bir tuple ile verimlilik için değerleri kilitlemek olabilir? Onunla oynadım ve init içindeki argümanlardan self.values ​​ayarlayan bir versiyon buldum . Beyan edebilmek güzel Animal = Enum('horse', 'dog', 'cat'). Ayrıca self.values eksik bir öğe durumunda getattr ValueError yakalamak - bunun yerine verilen ad dizesi ile bir AttributeError yükseltmek daha iyi görünüyor. Metaclass'ı Python 2.7'de bu alandaki sınırlı bilgiye dayanarak çalıştıramadım, ancak özel Enum sınıfım düz örnek yöntemlerle iyi çalışıyor.
trojjer

49

Python'un yerleşik bir eşdeğeri yoktur enumve diğer cevapların kendi uygulamanızı uygulamak için fikirleri vardır (ayrıca Python yemek kitabındaki üst sürümle de ilgilenebilirsiniz ).

Ancak, bir enumC için çağrılacak durumlarda , genellikle sadece basit dizeleri kullanarak sonuçlanır : nesnelerin / niteliklerin uygulanma şekli nedeniyle, (C) Python zaten kısa dizelerle çok hızlı çalışmak için optimize edilmiştir, bu yüzden olmaz tamsayıları kullanmanın herhangi bir performans avantajı olmaması. Yazım hatalarına / geçersiz değerlere karşı korunmak için seçilen yerlere kontrol ekleyebilirsiniz.

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

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

(Sınıf kullanmaya kıyasla bir dezavantaj, otomatik tamamlama avantajını kaybetmenizdir)


2
Bu çözümü tercih ederim. Mümkün olduğunca yerleşik türleri kullanmayı seviyorum.
Seun Osewa

Bu sürüm gerçekten en üstte değil. Sadece verilen test kodunun bir sürü var
Casebash

1
Aslında, "doğru" sürüm yorumlarda ve çok daha karmaşık - ana sürüm küçük bir hata var.
Casebash

39

2013-05-10'da Guido 435 PEP kabul etmeyi kabul etti Python 3.4 standart kütüphanesine . Bu, Python'un numaralandırmalar için yerleşik desteğe sahip olduğu anlamına gelir!

Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 ve 2.4 için bir backport mevcut. Enum34 olarak Pypi'de .

Beyan:

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

Temsil:

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

Tekrar:

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

Programlı erişim:

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

Daha fazla bilgi için teklife bakın . Muhtemelen yakında resmi belgeler gelecektir.


33

Python enums tanımlamak gibi tercih ederim:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Tamsayıların benzersiz olduğundan emin olmak için endişelenmenize gerek olmadığından tamsayı kullanmaktan daha fazla hata geçirmezdir (örneğin, Dog = 1 ve Cat = 1 derseniz, vidalanırsınız).

Yazım hataları hakkında endişelenmenize gerek olmadığı için dizeleri kullanmaktan daha fazla hata geçirmezdir (örn. X == "catt" sessizce başarısız olur, ancak x == Animal.Catt bir çalışma zamanı istisnasıdır).


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

Şöyle kullanın:

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

sadece benzersiz semboller istiyorsanız ve değerleri umursamıyorsanız, bu satırı değiştirin:

__metaclass__ = M_add_class_attribs(enumerate(names))

Bununla:

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

11
Eğer değişirse IMHO temizleyici olurdu enum(names)için enum(*names)onu ararken o zaman ekstra parantez düşebilir -.
Chris Lutz

Bu yaklaşımı seviyorum. Aslında bu özellik değeri Animal.DOG == 'DOG' güzel özelliği olan adıyla aynı dize ayarlamak için değiştirdim, böylece onlar otomatik olarak sizin için dize. (Hata ayıklama çıktısını yazdırmak için son derece yardımcı olur.)
Ted Mielczarek

23

Python 3.4'ten, numaralandırmalar için resmi destek olacak. Belgeleri ve örnekleri burada Python 3.4 belgeleri sayfasında bulabilirsiniz .

Numaralandırmalar, sözdizimi kullanılarak okunmasını ve yazılmasını kolaylaştırır. Alternatif bir oluşturma yöntemi Fonksiyonel API'de açıklanmaktadır. Bir numaralandırma tanımlamak için, Numaralandırma'yı aşağıdaki gibi alt sınıflayın:

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

Geri taşıma da artık destekleniyor. Bu yol.
14'te srock

22

Hmmm ... Sanırım bir numaraya en yakın şey, sözlük olarak şöyle tanımlanacaktır:

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

veya

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

Ardından, bunun gibi sabitler için sembolik adı kullanabilirsiniz:

mymonth = months['January']

Tuples listesi veya tuples tuples gibi başka seçenekler de vardır, ancak söz konusu değere erişmek için "sembolik" (sabit dize) yolunu sunan tek sözlüktür.

Edit: Ben de Alexandru'nun cevabını seviyorum!


Ve en önemlisi, birleşik giriş kutusu öğeleri olarak görünmesi için dize değerlerine ihtiyacınız olduğu gibi değerlerine erişmeniz gerekiyorsa, bir sözlükte kolayca yineleyebilirsiniz. Bunun yerine numaralandırma yerine sözlük kullanın.
LEMUEL ADANE

22

Python'da bir enumun başka, çok basit bir uygulaması namedtuple:

from collections import namedtuple

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

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

Veya alternatif olarak,

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

Yukarıdaki alt sınıflar gibi set, bu da şunları sağlar:

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

Ancak farklı anahtarlara ve değerlere sahip olabileceğinden daha fazla esnekliğe sahiptir. Bu izin verir

MyEnum.FOO < MyEnum.BAR

sıralı sayı değerlerini dolduran sürümü kullanırsanız beklenen şekilde davranır.


20

Ne kullanıyorum:

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

Nasıl kullanılır:

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

Böylece bu size state.PUBLISHED ve iki tuples gibi tamsayı sabitlerini Django modellerinde seçenek olarak kullanmak için verir.


17

davidg dicts kullanmanızı önerir. Bir adım daha ileri gidip setleri kullanırım:

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

Artık bir değerin kümedeki değerlerden biriyle eşleşip eşleşmediğini test edebilirsiniz:

if m in months:

dF gibi olsa da, genellikle sadece numaralandırma yerine dize sabitleri kullanın.


evet!, set devralır ve getattr yöntemi sağlarsa çok daha iyi !
shahjapan

17

Basit tutun:

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

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

Sonra:

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

16

Bu gördüğüm en iyisi: "Python'da Birinci Sınıf Numaralandırmalar"

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

Size bir sınıf verir ve sınıf tüm numaralandırmaları içerir. Numaralamalar birbirleriyle karşılaştırılabilir, ancak belirli bir değeri yoktur; bunları tamsayı değeri olarak kullanamazsınız. (İlk başta buna direndim çünkü tamsayı değerler olan C numaralarına alışkınım. Ama bir tamsayı olarak kullanamazsanız, yanlışlıkla bir tamsayı olarak kullanamazsınız, bu yüzden genel olarak bir kazanım olduğunu düşünüyorum .) Her numaralandırma benzersiz bir değerdir. Numaraları yazdırabilir, üzerlerini yineleyebilir, numaralandırma değerinin numarada "olduğunu" test edebilirsiniz. Oldukça eksiksiz ve kaygan.

Düzenle (cfi): Yukarıdaki bağlantı Python 3 uyumlu değil. İşte Python 3 için enum.py portum:

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)

Bu tarif, reddedilen bir PEP için temel olarak kullanılmıştır. python.org/dev/peps/pep-0354 Sevdiğim bir uzantı: enum değerleri, dahili tamsayı değerini çıkarmanızı sağlayan bir üye değişkene sahip olmalıdır. Yanlışlıkla bir tamsayıyı bir tamsayıya dökmek mümkün olmamalı, bu nedenle .__int__()yöntem bir enum için bir istisna ortaya koymalıdır; ancak değeri elde etmenin bir yolu olmalı. Ve sınıf tanımlama zamanında belirli tamsayı değerleri ayarlamak mümkün olmalıdır, böylece statmodüldeki sabitler gibi şeyler için bir enum kullanabilirsiniz .
steveha

14

İkili dosya biçimini deşifre etmek için bir Enum sınıfına ihtiyacım vardı. İstediğim özellikler özlü numaralandırma tanımı, tamsayı değeri veya dize ile enum örneklerini özgürce oluşturma yeteneği ve kullanışlı bir repryönlendirme. İşte sonuçta:

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

Tuhaf bir kullanım örneği:

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

Ana Özellikler:

  • str(), int()verepr() tüm üretmek en yararlı olası çıkış, enumartion sırasıyla adı, tam sayı değer, ve değerlendirir numaralandırma için geri bir Python ifadesi.
  • Yapıcı tarafından döndürülen numaralandırılmış değerler kesinlikle önceden tanımlanmış değerlerle sınırlıdır, yanlışlıkla enum değerleri yoktur.
  • Numaralandırılmış değerler singleton'dur; kesinlikle karşılaştırılabiliris

Enumları tanımlamayı kolaylaştırmak için kendi metasınıflı bir süper sınıfın kullanılmasını gerçekten seviyorum. Burada eksik olan bir __contains__ yöntemidir. Çoğunlukla bir işlev parametresinin izin verilen değerleri için numaralar istiyorum çünkü - verilen bir değişken numaralandırmanın bir parçası olup olmadığını kontrol etmek istiyorum.
xorsyst

Bu aslında orijinal olarak oluşturduğumun biraz kırpılmış bir versiyonudur (burada bulabilirsiniz: enum_strict.py ) v bir __instancecheck__yöntem tanımlar . Sınıflar örneklerin koleksiyonu değildir, bu yüzden 1 in Fruitsaçmadır. Ancak, bağlantılı sürüm isinstance(1, Fruit), sınıflar ve örnekler nosyonu açısından daha doğru olacağını destekler .
SingleNegationElimination

Ancak sınıfları unutmak ve sıralamalar açısından düşünmek, onları bir koleksiyon olarak düşünmek mantıklıdır. Örneğin, bir dosya açma modları numaralandırması (MODE.OPEN, MODE.WRITE, vb.) Olabilir. Benim işlevine parametrelerini doğrulamak istiyorum: MODE modunda ise: daha iyi çok okur isintance (modunda Modu)
xorsyst

GitHub'da sadece ints'tan
hans_meine

12

Python'daki yeni standart PEP 435'tir , bu nedenle Python'un gelecekteki sürümlerinde bir Enum sınıfı mevcut olacaktır:

>>> from enum import Enum

Ancak şimdi kullanmaya başlamak için PEP'i motive eden orijinal kütüphaneyi kurabilirsiniz :

$ pip install flufl.enum

Daha sonra çevrimiçi kılavuzuna göre kullanabilirsiniz :

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

Adını verirseniz, sorununuz var, ancak değerler yerine nesne oluşturmuyorsanız bunu yapmanıza izin verir:

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

Burada yer alan diğer uygulamaları kullanırken (örneğimde adlandırılmış örnekleri kullanırken), farklı numaralandırmalardan nesneleri karşılaştırmaya çalışmamanız gerekir. İşte olası bir tuzak:

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

Olmadı!


9

Alec Thomas'ın çözümünü gerçekten seviyorum (http://stackoverflow.com/a/1695250):

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

Zarif ve temiz görünüyor, ancak sadece belirtilen özelliklere sahip bir sınıf oluşturan bir işlev.

Fonksiyonda küçük bir değişiklikle, biraz daha 'numara' olarak hareket etmesini sağlayabiliriz:

Not: Aşağıdaki örnekleri, pygtk yeni stil 'numaralar' (Gtk.MessageType.WARNING gibi) davranışını yeniden çalışarak oluşturdu

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

Bu, belirtilen bir türe dayalı bir enum oluşturur. Önceki işlev gibi öznitelik erişimi vermenin yanı sıra, türlere göre bir Enum beklediğiniz gibi davranır. Ayrıca temel sınıfı devralır.

Örneğin, tamsayı numaralandırır:

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

Bu yöntemle yapılabilecek bir başka ilginç şey, yerleşik yöntemleri geçersiz kılarak belirli davranışı özelleştirmektir:

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'

Bu "temel" tip fikir temiz :)
MestreLion

evet, bunu yeni Python 3.4 Enum ile de yapabileceğinizi unutmayın: python.org/dev/peps/pep-0435/#other-derived-enumerations
bj0

7

PyPI'nin numaralandırma paketi, enumların sağlam bir şekilde uygulanmasını sağlar. Daha önceki bir cevap PEP 354'ten bahsetmiştir; bu reddedildi ancak teklif http://pypi.python.org/pypi/enum uygulandı .

Kullanımı kolay ve zariftir:

>>> 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'nun numaralandırmalar için sınıf sabitlerini kullanma önerisi gayet iyi çalışıyor.

Ayrıca insan tarafından okunabilir bir dize temsili aramak için her sabit kümesi için bir sözlük eklemek istiyorum.

Bu iki amaca hizmet eder: a) numaralandırmayı güzel yazdırmak için basit bir yol sağlar ve b) sözlük, sabitleri mantıksal olarak gruplandırır, böylece üyeliğinizi test edebilirsiniz.

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

İşte değerli bulduğum bazı farklı özelliklere sahip bir yaklaşım:

  • lexum düzenine göre değil, enum düzenindeki sıralamaya göre> ve <karşılaştırmasına izin verir
  • öğeyi ada, mülke veya dizine göre adresleyebilir: xa, x ['a'] veya x [0]
  • [:] veya [-1] gibi dilimleme işlemlerini destekler

ve en önemlisi farklı türdeki numaralandırmalar arasındaki karşılaştırmaları önler !

Http://code.activestate.com/recipes/413486-first-class-enums-in-python'a çok yakın .

Bu yaklaşımda neyin farklı olduğunu göstermek için birçok doktora dahil edildi.

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

Alec Thomas'ın çözümünün bir çeşidi :

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

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1

3

Bu çözüm, bir liste olarak tanımlanan numaralandırma için bir sınıf almanın basit bir yoludur (artık can sıkıcı tamsayı atamaları yok):

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
Bu, sınıf oluşturmanın gerçekten eski bir yoludur. Neden sadece kullanmıyorsunuz type(class_name, (object,), dict(...))?
terminus

3

Orijinal numaralandırma önerisi PEP 354 , yıllar önce reddedilirken, geri dönmeye devam ediyor. 3.2'ye bir tür enum eklenmesi amaçlandı, ancak 3.3'e geri itildi ve unutuldu. Ve şimdi Python 3.4'e dahil edilmek üzere tasarlanmış bir PEP 435 var . PEP 435'in referans uygulamasıdır flufl.enum.

Nisan 2013 itibarı ile , 3.4'te standart kütüphaneye bir şeylerin eklenmesi gerektiği konusunda genel bir fikir birliği var gibi görünüyor - insanlar "bu" şeyin ne olması gerektiği konusunda anlaşabildikleri sürece. Zor kısmı bu. Burada ve burada başlayan konuları ve 2013'ün ilk aylarında yarım düzine diğer konuları görün.

Bu arada, bu her ortaya çıktığında, PyPI, ActiveState, vb.'de bir dizi yeni tasarım ve uygulama görünür, bu nedenle FLUFL tasarımını beğenmezseniz, bir PyPI araması yapmayı deneyin .


3

Aşağıdakileri kullanın.

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

Bunu Django model seçimleri için kullandım ve çok pitonik görünüyor. Gerçekten bir Enum değil, ama işi yapıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.