Vra

Ek is hoofsaaklik'n C# ontwikkelaar, maar ek is tans besig met'n projek in Python.

Hoe kan ek verteenwoordig die ekwivalent van'n Enum in Python?

Was dit nuttig?

Oplossing

Enums is bygevoeg om te Python 3.4 soos beskryf in PEP 435.Dit het ook backported te 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, en 2,4 op pypi.

Vir meer gevorderde Enum tegnieke probeer om die aenum biblioteek (2.7, 3.3+, dieselfde skrywer as enum34.Kode is nie heeltemal versoenbaar tussen py2 en py3, bv.wat jy nodig het __order__ in python 2).

  • Om te gebruik enum34, doen $ pip install enum34
  • Om te gebruik aenum, doen $ pip install aenum

Die installering van enum (geen getalle) sal die installering van'n heeltemal verskillende en onversoenbare weergawe.


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)

of anders gestel:

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

In vroeër weergawes, een manier om dit te bewerkstellig enums is:

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

wat is gebruik word soos so:

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

Jy kan ook maklik ondersteuning outomatiese opsomming met iets soos hierdie:

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

en gebruik soos so:

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

Ondersteuning vir die omskakeling van die waardes terug na die name kan bygevoeg word op hierdie manier:

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)

Hierdie skryf enigiets met daardie naam nie, maar dit is nuttig vir die lewering van jou enums in die produksie.Dit sal gooi KeyError as die omgekeerde kartering nie bestaan nie.Met die eerste voorbeeld:

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

Ander wenke

Voor PEP 435, Python het nie 'n ekwivalente maar jy kan jou eie te implementeer.

My, Ek hou daarvan om dit eenvoudig (Ek het 'n paar verskriklik komplekse voorbeelde op die netto gesien), iets soos hierdie ...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

In Python 3.4 ( PEP 435 ), kan jy maak Enum die basis klas. Dit kry jy 'n bietjie ekstra funksies, in die PEP beskryf. Byvoorbeeld, enum lede is apart van heelgetalle, en hulle is saamgestel uit 'n name en 'n value.

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

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

print(Animal.DOG.value)
# 1

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

As jy nie wil hê om die waardes te tik, gebruik die volgende kortpad:

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

Enum implementering kan omgeskakel word na lyste en is iterable . Die einde van sy lede is die verklaring orde en het niks te doen met hul waardes. Byvoorbeeld:

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

Hier is een implementering:

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

Hier is die gebruik daarvan:

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

print(Animals.DOG)

As jy die numeriese waardes nodig, hier is die vinnigste manier:

dog, cat, rabbit = range(3)

In Python 3.x jy kan ook 'n ster plekhouer voeg aan die einde, wat sal verlustig in al die oorblywende waardes van die reeks in die geval dat jy nie omgee nie mors geheue en kan nie tel:

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

Die beste oplossing vir jou sal afhang van wat jy nodig het van jou vals enum .

Eenvoudige enum:

As jy die enum as net 'n lys van name identifisering verskillende items , die oplossing deur Mark Harrison (bo) is 'n groot:

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

Die gebruik van 'n range kan jy ook stel 'n begin waarde

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

In bykomend tot die bogenoemde, as jy ook vereis dat die items behoort aan 'n houer van 'n soort, dan embed hulle in 'n klas:

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

Om die enum item gebruik, sal jy nou nodig het om die naam houer en die naam item gebruik:

stype = Stationery.Pen

Komplekse enum:

Vir 'n lang lys van enum of meer ingewikkeld gebruike van enum, hierdie oplossings sal nie voldoende wees. Jy kan na die resep kyk deur Will Ware vir Simuleer Enumerations in Python gepubliseer in die Python Kookboek . 'N aanlyn weergawe van wat beskikbaar is rel="noreferrer">.

Meer info:

PEP 354: Enumerations in Python het die interessante besonderhede van 'n voorstel vir 'enum in Python en waarom dit verwerp.

'n Enum klas kan 'n one-liner wees.

class Enum(tuple): __getattr__ = tuple.index

Hoe om dit te gebruik (vorentoe en reverse lookup, sleutels, waardes, items, ens.)

>>> 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 het nie 'n ingeboude in gelykstaande aan enum, en ander antwoorde het idees vir die uitvoering van jou eie (jy kan ook belangstel in die oor die top weergawe in die Python kookboek).

Maar in situasies waar 'n enum sal genoem word in C, ek gewoonlik beland net die gebruik van eenvoudige snare : as gevolg van die manier waarop voorwerpe / eienskappe geïmplementeer word, (C) Python is geskik om in elk geval baie vinnig werk met kort snare, so daar sal nie werklik enige prestasie voordeel vir die gebruik van heelgetalle. Om te waak teen typos / ongeldig waardes wat jy kan kontrole voeg in geselekteerde plekke.

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

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

(Een nadeel in vergelyking met die gebruik van 'n klas is wat jy verloor die voordeel van outomaties)

So, ek stem saam.Laat ons nie dwing tipe veiligheid in Python, maar ek wil graag om myself te beskerm van dom foute.So, wat doen ons dink oor dit?

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

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

Dit hou my uit waarde-botsing in die definiëring van my enums.

>>> Animal.Cat
2

Daar is nog'n handige voordeel:regtig vinnig omgekeerde soektogte:

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

Op 2013/05/10, Guido ingestem om PEP 435 aanvaar in die Python 3.4 standaard biblioteek. Dit beteken dat Python het uiteindelik ingeboude ondersteuning vir keuse lys!

Daar is 'n backport beskikbaar vir Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, en 2.4. Dit is op Pypi as enum34 .

Verklaring:

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

Verteenwoordiging:

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

Iteration:

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

Programmatiese toegang:

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

Vir meer inligting, verwys na die voorstel . Amptelike dokumentasie sal waarskynlik volg binnekort.

Ek verkies om enums definieer in Python soos so:

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

Dit is meer fout-proof as die gebruik van heelgetalle omdat jy nie hoef te bekommer oor om te verseker dat die heelgetalle is uniek (bv as jy sê Dog = 1 en Kat = 1 jy geskroef).

Dit is meer fout-proof as die gebruik van snare omdat jy nie hoef te bekommer oor typos (bv x == "Catt" versuim stil, maar x == Animal.Catt is 'n runtime uitsondering).

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

Gebruik dit soos volg:

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

As jy wil net unieke simbole en gee nie om oor die waardes, hierdie lyn te vervang:

__metaclass__ = M_add_class_attribs(enumerate(names))

met hierdie:

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

Hmmm ... Ek veronderstel die naaste ding aan 'n enum sou wees 'n woordeboek, gedefinieer óf soos volg:

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

of

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

Toe, jy kan die simboliese naam vir die konstantes soos volg:

mymonth = months['January']

Daar is ander opsies, soos 'n lys van tuples, of 'n tuple van tuples, maar die woordeboek is die enigste een wat jou met 'n "simboliese" (konstante string) manier om toegang te verkry tot die waarde.

Edit: Ek hou van antwoord Alexandru se te

!

'n Ander, baie eenvoudig, implementering van 'n enum in Python, met behulp van namedtuple:

from collections import namedtuple

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

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

of, alternatiewelik,

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

Soos die metode hierbo dat subklasse set, dit laat:

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

Maar het meer buigsaamheid as dit verskillende sleutels en waardes kan hê. Dit laat

MyEnum.FOO < MyEnum.BAR

om op te tree as wat verwag as jy die weergawe wat vul in opeenvolgende aantal waardes te gebruik.

Wat ek gebruik:

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

Hoe om te gebruik:

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

So dit gee jou heelgetal konstantes soos state.PUBLISHED en die twee-tuples om te gebruik as keuses in Django modelle.

Van Python 3.4 sal daar amptelike ondersteuning vir enums wees. Jy kan dokumentasie en voorbeelde hier op Python 3.4 dokumentasie bladsy vind.

  

Enumerations is geskep met behulp van die klas sintaksis, wat hulle maklik maak   om te lees en skryf. 'N alternatiewe skepping metode beskryf in   Funksionele API. Om 'n opsomming te definieer, subklas Enum soos volg:

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

davidg beveel die gebruik van dicts. Ek sal 'n stap verder gaan en gebruik stelle:

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

Nou kan jy toets of 'n waarde wedstryde een van die waardes in die stel soos volg:

if m in months:

soos dF, al is, ek gewoonlik net gebruik string konstantes in die plek van enums.

Dit is die beste een wat ek gesien het:"In die eerste Klas Enums in Python"

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

Dit gee jou'n klas, en die klas bevat al die enums.Die enums kan word in vergelyking met mekaar, maar nie'n spesifieke waarde;jy kan nie gebruik hulle as'n heelgetal waarde.(Ek weerstaan dit op die eerste, want ek is wat gebruik word om C enums, wat heelgetal waardes.Maar as jy nie kan dit gebruik as'n heelgetal is, kan jy nie gebruik dit as'n heelgetal deur die fout so algehele ek dink dit is'n wen.) Elke enum is'n unieke waarde.Jy kan druk enums, jy kan itereer oor hulle, kan jy toets wat'n enum waarde is "in" die enum.Dit is redelik volledige en glad.

Wysig (cfi):Die skakel hierbo is nie Python 3 versoenbaar is.Hier is my hawe van enum.py om te 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)

Hou dit eenvoudig:

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

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

Toe:

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

Ek het die geleentheid om die behoefte van 'Enum klas, met die doel om te ontsyfer 'n binêre lêer formaat gehad. Die eienskappe wat ek toevallig wil hê, is bondige enum definisie, die vermoë om vrylik gevalle van die enum skep deur óf heelgetal waarde of string, en 'n nuttige representation. Hier is wat ek beland met:

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

'n fantastiese voorbeeld van die gebruik daarvan:

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

Belangrike kenmerke:

  • str(), int() en repr() al produseer die mees bruikbare uitset moontlik, onderskeidelik die naam van die enumartion, sy heelgetal waarde, en 'n Python uitdrukking wat terug evalueer om die opsomming.
  • vervat waardes teruggekeer deur die konstruktor is streng beperk tot die gedefinieerde waardes, geen toevallige enum waardes.
  • vervat waardes is single; hulle kan streng vergelykbaar met is

Ek het regtig soos Alec Thomas 'n oplossing (http://stackoverflow.com/a/1695250):

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

Dit is elegant en skoon te kyk, maar dit is net 'n funksie wat 'n klas skep met die gespesifiseerde eienskappe.

Met 'n bietjie verandering aan die funksie, kan ons dit 'n bietjie meer 'enumy' optree:

  

NOTA: Ek het die volgende voorbeelde deur te probeer om die weer te gee   gedrag van pygtk se nuwe styl 'enums' (soos 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

Dit skep 'n enum gebaseer af 'n bepaalde tipe. In bykomend tot die gee kenmerk toegang soos die vorige funksie, dit optree as jy 'n Enum sou verwag om ten opsigte van tipes. Dit erf ook die basis klas.

Byvoorbeeld, heelgetal 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

Nog 'n interessante ding wat gedoen kan word met hierdie metode is pas spesifieke gedrag deur oorheersende ingeboude metodes:

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'
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

As jy dit noem, is jou probleem, maar as dit nie die skep van voorwerpe in plaas van waardes kan jy om dit te doen:

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

By die gebruik van ander implementasies hier gevestig (ook by die gebruik van die naam van gevalle in my voorbeeld) wat jy moet seker maak jy nooit probeer om voorwerpe vergelyk van verskillende enums. Want hier is 'n moontlike slaggat:

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

Yikes!

Die nuwe standaard in Python is PEP 435 , so 'n Enum klas sal beskikbaar wees in toekomstige weergawes van Python wees:

>>> from enum import Enum

Maar om te begin om dit te gebruik en nou kan jy die oorspronklike biblioteek wat gemotiveer die PEP:

$ pip install flufl.enum

Dan kan dit gebruik as per die aanlyn-gids :

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

Die enum pakket van PyPI bied 'n robuuste implementering van enums. 'N vroeëre antwoord genoem PEP 354; hierdie is verwerp, maar die voorstel is geïmplementeer http://pypi.python.org/pypi/enum .

Gebruik maklik en elegant:

>>> 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 se voorstel van die gebruik van die klas konstantes vir enums werk baie goed.

Ek hou ook daarvan om 'n woordeboek te voeg vir elke stel van konstantes aan 'n maklik leesbare string verteenwoordiging lookup.

Dit het twee doelwitte:. A) dit bied 'n eenvoudige manier om mooi-druk jou enum en b) die woordeboek logies groepe die konstantes sodat jy kan toets vir lidmaatskap

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

Hier volg 'n benadering met 'n paar verskillende eienskappe Ek vind waardevolle:

  • kan> en
  • kan aanspreek item by die naam, eiendom of indeks: x.a, x [ 'n '] of x [0]
  • ondersteun sny bedrywighede soos [:] of [-1]

en die belangrikste verhoed vergelykings tussen enums van verskillende tipes

nou Gegrond op http://code.activestate.com / resepte / 413486-eerste-klas-enums-in-luislang .

Baie doctests hier ingesluit om te illustreer wat anders oor hierdie benadering.

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

Hier is 'n variant op oplossing Alec Thomas se :

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

Hierdie oplossing is 'n eenvoudige manier om 'n klas vir die opsomming gedefinieer as 'n lys (nie meer irriterende heelgetal opdragte):

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

Hoewel die oorspronklike enum voorstel, PEP 354 , verwerp jaar gelede dit hou kom terug. 'N soort van enum is bedoel om by te voeg tot 3.2, maar dit het terug gestoot tot 3.3 en dan vergeet. En nou is daar 'n PEP 435 bedoel vir insluiting in Python 3.4. Die verwysing implementering van PEP 435 is flufl.enum .

As van April 2013, lyk dit asof daar 'n algemene konsensus dat iets wees moet word om die standaard biblioteek bygevoeg in 3.4-solank mense kan saamstem oor wat dit "iets" moet wees. Dit is die moeilike deel. Sien die drade begin hier en hier , en 'n half dosyn ander drade in die vroeë maande van 2013

Intussen het elke keer as dit kom, 'n keur van nuwe ontwerpe en implementering verskyn op PyPI, Active, ens, so as jy nie hou van die FLUFL ontwerp, probeer 'n PyPI search .

'n variant (met ondersteuning te noem 'n enum waarde kry) om Alec Thomas se netjiese antwoord :

class EnumBase(type):
    def __init__(self, name, base, fields):
        super(EnumBase, self).__init__(name, base, fields)
        self.__mapping = dict((v, k) for k, v in fields.iteritems())
    def __getitem__(self, val):
        return self.__mapping[val]

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

Numbers = enum(ONE=1, TWO=2, THREE='three')
print Numbers.TWO
print Numbers[Numbers.ONE]
print Numbers[2]
print Numbers['three']
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top