2015-08-25 20 views
10

Python'un enum kütüphanesi ile uğraştım ve bir bilmeceyle karşılaştım. şey tanımlanır burada docs, onlar, bir auto-numbering enum bir örneğini göstermektedir:Bir enum üyenin değerini otomatik olarak ismine ayarlama

class Color(AutoNumber): 
    red =() 
    green =() 
    ... 

Ben benzer bir sınıf yapmak istiyoruz ama değer otomatik üyesinin adından atanabilir VE işlevselliği tutmak Eğer str ve enummixin stuff

yapmaktan olsun Yani böyle bir şey: Ben varyasyonları m bir çok enum modülünün kaynak kodunda bakıp denedim

class Animal(MagicStrEnum): 
    horse =() 
    dog =() 

Animal.dog == 'dog' # True 

__new__ etrafında ESSING ve EnumMeta sınıf

+1

Bunu mu demek istediniz: Animal.dog.value == 'dog''? –

+0

ile ilgili: http://www.acooke.org/cute/Pythonssad0.html – wim

+0

EnumMeta._create_() öğesini enum.py (https://hg.python.org/cpython/file) adresinden değiştirerek yapılabilir. /3.4/Lib/enum.py#l295, ancak kodu korumak için _is_sunder() 'dan gelen adı geçen isimlerde ValueError nedeniyle hemen geçersiz kılınamaz. Ancak, eğer isimler = [' red ',' green ',' blue ' ] ve Renk = Enum ('Renk', adlar = zip (isimler, isimler)), daha sonra Color.red.value == 'red', Color.green.value == 'green' ve Color.blue.value == 'mavi' –

cevap

0

Belki gerçekten kendinize tekme istiyorsanız otomatik Enum sınıfa

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

>>> Animal.ant.name == "ant" 
True 

olsa tarafından sağlanmaktadır name özellik için arıyoruz. Ve eminim ki bu, tüm dünyadaki gotchas'ları tanıtacaktır (en bariz olanı elimden geldim).

from enum import Enum, EnumMeta, _EnumDict 

class AutoStrEnumDict(_EnumDict): 
    def __setitem__(self, key, value): 
     super().__setitem__(key, key) 

class AutoStrEnumMeta(EnumMeta): 
    @classmethod 
    def __prepare__(metacls, cls, bases): 
     return AutoStrEnumDict() 
    def __init__(self, name, bases, attrs): 
     super().__init__(name, bases, attrs) 
     # override Enum.__str__ 
     # can't put these on the class directly otherwise EnumMeta overwrites them 
     # should also consider resetting __repr__, __format__ and __reduce_ex__ 
     if self.__str__ is not str.__str__: 
      self.__str__ = str.__str__ 

class AutoStrNameEnum(str, Enum, metaclass=AutoStrEnumMeta): 
    pass 

class Animal(AutoStrNameEnum): 
    horse =() 
    dog =() 

print(Animal.horse) 
assert Animal.horse == "horse" 
assert str(Animal.horse) == "horse" 
# and not equal to "Animal.horse" (the gotcha mentioned earlier) 
+0

'_EnumDict' ile yaptığınız değişiklik, 'str' olarak dönüştürülecek yöntemleri eklemeyi imkansız hale getiriyor –

+1

Çevrede dolaşılması kolay. değer bir işlevdir, ya da daha sağlam hale getirmek için, bir sentinel değer yaratın ve yalnızca değer sentinel olduğunda dönüşümü gerçekleştirin – Dunes

6

güncelle: 2017/03/01 Python 3.6 içinde

(ve Aenum 2.0) Flag ve IntFlag sınıfları eklendi; bunun bir parçası bu trivially kolaylaştırır yeni auto() helper oldu:

>>> class AutoName(Enum): 
...  def _generate_next_value_(name, start, count, last_values): 
...   return name 
... 
>>> class Ordinal(AutoName): 
...  NORTH = auto() 
...  SOUTH = auto() 
...  EAST = auto() 
...  WEST = auto() 
... 
>>> list(Ordinal) 
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>] 

Orijinal cevap

bir AutoStr sınıfıyla zorluk enum üyenin adı geçmedi olmasıdır onu oluşturan kod içine, bu nedenle kullanılamaz. Başka bir kırışıklık, str'un değişmez olmasıdır, bu yüzden oluşturulduktan sonra bu tür enumları değiştiremeyiz (örneğin, bir class decorator kullanarak).bize verdiği

Animal = Enum('Animal', [(a, a) for a in ('horse', 'dog')], type=str) 

: yapmak

kolay şey Functional API kullanmaktır

>>> list(Animal) 
[<Animal.horse: 'horse'>, <Animal.dog: 'dog'>] 

>>> Animal.dog == 'dog' 
True 

yapılacak sonraki en kolay şey, bir temel sınıf yapmak istiyorum varsayarak Gelecekteki numaralandırma kullanımınız için DocEnem:

ve kullanımda:

class SpecKind(DocEnum): 
    REQUIRED = "required value" 
    OPTION = "single value per name" 
    MULTI = "multiple values per name (list form)" 
    FLAG = "boolean value per name" 
    KEYWORD = 'unknown options' 

Not ilk seçenek aksine DocEnum üye değilstr ler olduğunu.


da bunu zor yoldan yapmak isterseniz: ile alt sınıfı EnumMeta ve keman yeni Enum 'ın önce üye oluşturulur sınıf sözlüğü: bize verdiği

from enum import EnumMeta, Enum, _EnumDict 

class StrEnumMeta(EnumMeta): 
    def __new__(metacls, cls, bases, oldclassdict): 
     """ 
     Scan through `oldclassdict` and convert any value that is a plain tuple 
     into a `str` of the name instead 
     """ 
     newclassdict = _EnumDict() 
     for k, v in oldclassdict.items(): 
      if v ==(): 
       v = k 
      newclassdict[k] = v 
     return super().__new__(metacls, cls, bases, newclassdict) 

class AutoStrEnum(str, Enum, metaclass=StrEnumMeta): 
    "base class for name=value str enums" 

class Animal(AutoStrEnum): 
    horse =() 
    dog =() 
    whale =() 

print(Animal.horse) 
print(Animal.horse == 'horse') 
print(Animal.horse.name, Animal.horse.value) 

:


Bildirim: Python stdlib Enum, enum34 backport ve Advanced Enumeration (aenum) kitaplığının yazarıyım.

+0

@VillasV: Teşekkürler Bu hatayı bulmak için! Üzgünüm, yorumcular doğru bir düzenleme olduğunu görmediler. –

İlgili konular