2015-08-25 12 views
10

Ho gironzolato con la libreria enum di Python e mi sono imbattuto in un enigma. Nei documenti, essi mostrano un esempio di auto-numbering enum, in cui si definisce qualcosa:Impostazione automatica del valore di un membro di enum al suo nome

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

Voglio fare un corso simile, ma il valore sarebbe automaticamente impostate dal nome del membro e mantenere la funzionalità che si ottiene dal fare il str e enummixin stuff

Quindi qualcosa di simile:

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

Animal.dog == 'dog' # True 

ho guardato il codice sorgente del modulo enum e ho provato un sacco di variazioni m essing giro con __new__ e la classe di EnumMeta

+1

intendi 'Animal.dog.value == 'dog''? –

+0

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

+0

Penso che potrebbe essere fatto modificando EnumMeta._create_() in enum.py (https://hg.python.org/cpython/file /3.4/Lib/enum.py#l295, tuttavia non può essere immediatamente cancellato a causa di ValueError sui nomi sundered provenienti da _is_sunder() per proteggere il codice. Tuttavia, se names = ['red', 'green', 'blue' ] e Color = Enum ('Colore', names = zip (nomi, nomi)), quindi Color.red.value == 'red', Color.green.value == 'green' e Color.blue.value == 'blu'. –

risposta

0

Forse si sta cercando per l'attributo name che è fornito automaticamente dalla classe Enum

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

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

Anche se davvero si vuole spararsi in un piede. E sono sicuro che questo introdurrà un intero mondo di trucchi (ne ho eliminato uno più ovvio).

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

La tua modifica a '_EnumDict' rende impossibile aggiungere metodi poiché verrebbero anche convertiti in' str's. –

+1

Facile da aggirare. il valore è una funzione, oppure per renderlo più robusto, creare un valore sentinella e effettuare la conversione solo quando il valore è la sentinella – Dunes

6

Aggiornamento: 2017-03-01

In Python 3.6 (e Aenum 2.0) Flag e IntFlag sono stati aggiunti classi; parte di quella era una nuova auto() helper che rende questo banalmente semplice:

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

risposta originale

La difficoltà con una classe AutoStr è che il nome del membro enum non è passato nel codice che lo crea, quindi non è disponibile per l'uso. Un'altra ruga è che str è immutabile, quindi non possiamo cambiare quei tipi di enumerazione dopo che sono stati creati (ad esempio usando un class decorator).

La cosa più semplice da fare è usare il Functional API:

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

che ci dà:

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

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

La prossima cosa più facile da fare, supponendo che si vuole fare una classe di base per il tuo uso futuro di enumerazione, sarebbe qualcosa di simile al mio DocEnem:

class DocEnum(Enum): 
    """ 
    compares equal to all cased versions of its name 
    accepts a doctring for each member 
    """ 
    def __new__(cls, *args): 
     """Ignores arguments (will be handled in __init__)""" 
     obj = object.__new__(cls) 
     obj._value_ = None 
     return obj 

    def __init__(self, doc=None): 
     # first, fix _value_ 
     self._value_ = self._name_.lower() 
     self.__doc__ = doc 

    def __eq__(self, other): 
     if isinstance(other, basestring): 
      return self._value_ == other.lower() 
     elif not isinstance(other, self.__class__): 
      return NotImplemented 
     return self is other 

    def __ne__(self, other): 
     return not self == other 

e in uso:

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' 

Nota che a differenza della prima opzione, DocEnum membri sono nonstr s.


Se si vuole farlo nel modo più duro: sottoclasse EnumMeta e violino con i nuovi Enum s' Dizionario classe vengono creati prima i membri:

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) 

Il che ci dà:

Animal.horse 
True 
horse horse 

Disclosure: Sono l'autore dello Python stdlib Enum, dello enum34 backport e della libreria Advanced Enumeration (aenum).

+0

@VillasV: Grazie s per aver trovato quell'errore! Mi dispiace che i revisori non abbiano visto che era una modifica corretta. –

Problemi correlati