tags:

views:

679

answers:

6

I've been using a small class to emulate Enums in some Python projects. Is there a better way or does this make the most sense for some situations?

Class code here:

class Enum(object):
'''Simple Enum Class
Example Usage:
>>> codes = Enum('FOO BAR BAZ') # codes.BAZ will be 2 and so on ...'''
def __init__(self, names):
    for number, name in enumerate(names.split()):
        setattr(self, name, number)
+5  A: 

Enums have been proposed for inclusion into the language before, but were rejected (see http://www.python.org/dev/peps/pep-0354/), though there are existing packages you could use instead of writing your own implementation:

Ycros
+2  A: 

The builtin way to do enums is:

(FOO, BAR, BAZ) = range(3)

which works fine for small sets, but has some drawbacks:

  • you need to count the number of elements by hand
  • you can't skip values
  • if you add one name, you also need to update the range number

For a complete enum implementation in python, see: http://code.activestate.com/recipes/67107/

tsg
notably, Python will raise an exception if the range size doesn't match the element count.
Casey
+3  A: 

What I see more often is this, in top-level module context:

FOO_BAR = 'FOO_BAR'
FOO_BAZ = 'FOO_BAZ'
FOO_QUX = 'FOO_QUX'

...and later...

if something is FOO_BAR: pass # do something here
elif something is FOO_BAZ: pass # do something else
elif something is FOO_QUX: pass # do something else
else: raise Exception('Invalid value for something')

Note that the use of is rather than == is taking a risk here -- it assumes that folks are using your_module.FOO_BAR rather than the string 'FOO_BAR' (which will normally be interned such that is will match, but that certainly can't be counted on), and so may not be appropriate depending on context.

One advantage of doing it this way is that by looking anywhere a reference to that string is being stored, it's immediately obvious where it came from; FOO_BAZ is much less ambiguous than 2.

Besides that, the other thing that offends my Pythonic sensibilities re the class you propose is the use of split(). Why not just pass in a tuple, list or other enumerable to start with?

Charles Duffy
+2  A: 

There's a lot of good discussion here.

Eli Bendersky
+3  A: 

The most common enum case is enumerated values that are part of a State or Strategy design pattern. The enums are specific states or specific optional strategies to be used. In this case, they're almost always part and parcel of some class definition

class DoTheNeedful( object ):
    ONE_CHOICE = 1
    ANOTHER_CHOICE = 2 
    YET_ANOTHER = 99
    def __init__( self, aSelection ):
        assert aSelection in ( self.ONE_CHOICE, self.ANOTHER_CHOICE, self.YET_ANOTHER )
        self.selection= aSelection

Then, in a client of this class.

dtn = DoTheNeeful( DoTheNeeful.ONE_CHOICE )
S.Lott
+1  A: 

I started with something that looks a lot like S.Lott's answer but I only overloaded 'str' and 'eq' (instead of the whole object class) so I could print and compare the enum's value.

class enumSeason():
    Spring = 0
    Summer = 1
    Fall = 2
    Winter = 3
    def __init__(self, Type):
        self.value = Type
    def __str__(self):
        if self.value == enumSeason.Spring:
            return 'Spring'
        if self.value == enumSeason.Summer:
            return 'Summer'
        if self.value == enumSeason.Fall:
            return 'Fall'
        if self.value == enumSeason.Winter:
            return 'Winter'
    def __eq__(self,y):
        return self.value==y.value

Print(x) will yield the name instead of the value and two values holding Spring will be equal to one another.

   >>> x = enumSeason(enumSeason.Spring)
   >>> print(x)
   Spring
   >>> y = enumSeason(enumSeason.Spring)
   >>> x == y
   True
Spell