21

Just looking at ways of getting named constants in python.

class constant_list:
    (A_CONSTANT, B_CONSTANT, C_CONSTANT) = range(3)

Then of course you can refer to it like so:

constant_list.A_CONSTANT

I suppose you could use a dictionary, using strings:

constant_dic = {
    "A_CONSTANT" : 1,
    "B_CONSTANT" : 2,
    "C_CONSTANT" : 3,}

and refer to it like this:

constant_dic["A_CONSTANT"]

My question, then, is simple. Is there any better ways of doing this? Not saying that these are inadequate or anything, just curious - any other common idioms that I've missed?

Thanks in advance.

Bernard
  • 45,296
  • 18
  • 54
  • 69
  • This is a duplicate of (at least) [this question](http://stackoverflow.com/questions/36932/whats-the-best-way-to-implement-an-enum-in-python) and [this question](http://stackoverflow.com/questions/108523/how-should-i-best-emulate-andor-avoid-enums-in-python). I really wish the SO search did a better job. – dF. Oct 13 '08 at 13:11

6 Answers6

19

For 2.3 or after:

class Enumerate(object):
  def __init__(self, names):
    for number, name in enumerate(names.split()):
      setattr(self, name, number)

To use:

 codes = Enumerate('FOO BAR BAZ')

codes.BAZ will be 2 and so on.

If you only have 2.2, precede this with:

 from __future__ import generators

 def enumerate(iterable):
   number = 0
   for name in iterable:
     yield number, name
     number += 1

(This was taken from here)

Bernard
  • 45,296
  • 18
  • 54
  • 69
Tomalak
  • 332,285
  • 67
  • 532
  • 628
2

This is the best one I have seen: "First Class Enums in Python"

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

It gives you a class, and the class contains all the enums. The enums can be compared to each other, but don't have any particular value; you can't use them as an integer value. (I resisted this at first because I am used to C enums, which are integer values. But if you can't use it as an integer, you can't use it as an integer by mistake so overall I think it is a win.) Each enum is a unique object. You can print enums, you can iterate over them, you can test that an enum value is "in" the enum. It's pretty complete and slick.

steveha
  • 74,789
  • 21
  • 92
  • 117
2

I find the enumeration class recipe (Active State, Python Cookbook) to be very effective.

Plus it has a lookup function which is nice.

Pev

Simon Peverett
  • 4,128
  • 3
  • 32
  • 37
2

An alternative construction for constant_dic:

constants = ["A_CONSTANT", "B_CONSTANT", "C_CONSTANT"]
constant_dic = dict([(c,i) for i, c in enumerate(constants)])
Anthony Cramp
  • 4,495
  • 6
  • 26
  • 27
2

The following acts like a classisc "written in stone" C enum -- once defined, you can't change it, you can only read its values. Neither can you instantiate it. All you have to do is "import enum.py" and derive from class Enum.

# this is enum.py
class EnumException( Exception ):
   pass

class Enum( object ):
   class __metaclass__( type ):
      def __setattr__( cls, name, value ):
         raise EnumException("Can't set Enum class attribute!")
      def __delattr__( cls, name ):
         raise EnumException("Can't delete Enum class attribute!")

   def __init__( self ):
      raise EnumException("Enum cannot be instantiated!")

This is the test code:

# this is testenum.py
from enum import *

class ExampleEnum( Enum ):
   A=1
   B=22
   C=333

if __name__ == '__main__' :

   print "ExampleEnum.A |%s|" % ExampleEnum.A
   print "ExampleEnum.B |%s|" % ExampleEnum.B
   print "ExampleEnum.C |%s|" % ExampleEnum.C
   z = ExampleEnum.A
   if z == ExampleEnum.A:
      print "z is A"

   try:
       ExampleEnum.A = 4   
       print "ExampleEnum.A |%s| FAIL!" % ExampleEnum.A
   except EnumException:
       print "Can't change Enum.A (pass...)"

   try:
       del ExampleEnum.A
   except EnumException:
       print "Can't delete Enum.A (pass...)"

   try:
       bad = ExampleEnum()
   except EnumException:
       print "Can't instantiate Enum (pass...)"
Kevin Little
  • 12,436
  • 5
  • 39
  • 47
1

In Python, strings are immutable and so they are better for constants than numbers. The best approach, in my opinion, is to make an object that keeps constants as strings:

class Enumeration(object):
    def __init__(self, possibilities):
        self.possibilities = set(possibilities.split())

    def all(self):
        return sorted(self.possibilities)

    def __getattr__(self, name):
        if name in self.possibilities:
            return name
        raise AttributeError("Invalid constant: %s" % name)

You could then use it like this:

>>> enum = Enumeration("FOO BAR")
>>> print enum.all()
['BAR', 'FOO']
>>> print enum.FOO
FOO
>>> print enum.FOOBAR
Traceback (most recent call last):
  File "enum.py", line 17, in <module>
    print enum.FOOBAR
  File "enum.py", line 11, in __getattr__
    raise AttributeError("Invalid constant: %s" % name)
AttributeError: Invalid constant: FOOBAR
Dzinx
  • 55,586
  • 10
  • 60
  • 78