tags:

views:

463

answers:

6
+14  A: 
class Directions:
    up = 0
    down = 1
    left = 2
    right =3
Kugel
+9  A: 

I gave a +1 to Kugel, but another leaner option is

dirUp, dirDown, dirLeft, dirRight = range(4)
cjrh
You could combine the two solutions. class Direction: up, down, left, right = range(4)
dangph
+8  A: 

The collections.namedtuple object can provide such a namespace:

>>> import collections
>>> dircoll=collections.namedtuple('directions', ('UP', 'DOWN', 'LEFT', 'RIGHT'))
>>> directions=dircoll(0,1,2,3)
>>> directions
directions(UP=0, DOWN=1, LEFT=2, RIGHT=3)
>>> directions.DOWN
1
>>> 
gimel
+3  A: 

If you are using Python 2.6+ then you can use namedtuple. They have the advantage having fixed number of properties, and when you need all enum values, you can use it like a tuple.

For more control over enum values, you may create your own enumeration class.

def enum(args, start=0):
    class Enum(object):
        __slots__ = args.split()

        def __init__(self):
            for i, key in enumerate(Enum.__slots__, start):
                setattr(self, key, i)

    return Enum()

>>> e_dir = enum('up down left right')
>>> e_dir.up
0
>>> e_dir = enum('up down left right', start=1)
>>> e_dir.up
1

Declaring __slots__ seals your Enum class, no more attributes can be set to an object which was created from a class with __slots__ property.

Your Enum class can also be namedtuple based, in that case you also get the features of a tuple. See namedtuple docs on subclassing namedtuple

Imran
+5  A: 

If you are sure that you need an enum, others have answered how to do it. But let's see why you want them? Understanding the motivation will help with choosing the solution.

  • Atomic values - in C, small numbers are easy to pass around, strings aren't. In Python, strings like "up" are perfectly good for many uses. Moreover, any solution that ends up with just a number is worse for debugging!

  • Meaningful values - in C, you frequently have to deal with existing magic numbers, and just want some syntax sugar for that. That's not the case here. However, there is other meaningful information you might want to associate with directions, e.g. the (dx,dy) vector - more on that below.

  • Type checking - in C, enums help catching invalid values at compile time. But Python generally prefers sacrificing compiler checking for less typing.

  • Introspection (doesn't exist in C enums) - you want to know all the valid values.

    • Completion - the editor can show you the possible values and help you type them.

Strings Redeemed (aka Symbols)

So, on the light side of Pythonic solutions, just use strings, and maybe have a list/set of all valid values:

DIRECTIONS = set(['up', 'down', 'left', 'right'])

def move(self, direction):
    # only if you feel like checking
    assert direction in DIRECTIONS
    # you can still just use the strings!
    if direction == 'up':
        # Do something

Note that the debugger would tell you that the function was called with 'up' as its argument. Any solution where direction is actually 0 is much worse than this!

In the LISP family of languages, this usage is dubbed symbols - atomic objects usable as easily as numbers would be, but carrying a textual value. (To be precise, symbols are string-like but a separate type. However, Python routinely uses regular strings where LISP would use symbols.)

Namespaced Strings

You can combine the idea that 'up' is better than 0 with the other solutions.

If you want to catch mispellings (at run time):

UP = 'up'
...
RIGHT = 'right'

And if you want to insist on typing a prefix to get completion, put the above in a class:

class Directions:
    UP = "up"
    ...
    RIGHT = "right"

or just in a separate file, making it a module.

A module allows lazy users to do from directions import * to skip the prefix - up to you whether you consider this a plus or minus... (I personally would hate to be forced to type Directions.UP if I'm using it frequently).

Objects with functionality

What if there is useful information/functionality associated with each value? "right" is not just one of 4 arbitrary values, it's the positive direction on the X axis!

If what you are doing in that if is something like:

def move(self, direction):
    if direction == 'up':
        self.y += STEP
    elif direction == 'down':
        self.y -= STEP
    elif direction == 'left':
        self.x -= STEP
    elif direction == 'right':
        self.x += STEP

than what you'd really like to write is:

def move(self, direction):
    self.x += direction.dx * STEP
    self.y += direction.dy * STEP

and that's it!

So you want to stuff this into either instances:

# Written in full to give the idea.
# Consider using collections.namedtuple
class Direction(object):
    def __init__(self, dx, dy, name):
        self.dx = dx
        self.dy = dy
        self.name = name
    def __str__(self):
        return name

UP = Direction(0, 1, "up")
...
RIGHT = Direction(1, 0, "right")

or just classes:

class Direction(object):
    pass

class Up(Direction):
    dx = 0
    dy = 1

...

class Right(Direction):
    dx = 1
    dy = 0

Remember that in Python, classes are also objects (distinct from any other object), and you can compare them: direction == Up etc.

Generally, instances are probably cleaner, but if your enumerated concepts have some hierarchical relationship, sometimes modeling them directly with classes is very nice.

Beni Cherniavsky-Paskin
+1 for a very good answer
Jonas Gulle
+1 very good answer and good arguments + oop considerations ...
neuro
+1  A: 

This is simple and effective:

class Enum(object):
  def __init__(self, *keys):
    self.__dict__.update(zip(keys, range(len(keys))))

Usage:

>>> x = Enum('foo', 'bar', 'baz', 'bat')
>>> x.baz
2
>>> x.bat
3
Robert Rossney
(Incidentally, that's not my invention. In fact, I'm reasonably sure that it originated with GVR his bad self.)
Robert Rossney
+1 for the simplicity
neuro