class Directions:
up = 0
down = 1
left = 2
right =3
I gave a +1 to Kugel, but another leaner option is
dirUp, dirDown, dirLeft, dirRight = range(4)
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
>>>
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
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.
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