Duplicate:
What’s the best way to implement an ‘enum’ in Python?
Whats the recognised way of doing enumerations in python?
For example, at the moment I'm writing a game and want to be able to move "up", "down", "left" and "right". I'm using strings because I haven't yet figured out how enumerations work in python, and so my logic is littered with things like this:
def move(self, direction):
if direction == "up":
# Do something
I want to replace "up"
with something like Directions.up
UPDATE 1: Python 3.4 will have a built-in well designed enum library. The values always know their name and type; there is an integer-compatible mode but the recommended default for new uses are singletons, unequal to any other object.
UPDATE 2: Since writing this I realized the critical test for enums is serialization. Other aspects can be refactored later, but if your enum goes into files / onto the wire, ask yourself up front what should happen if it's deserialized by an older/newer version (that might support a different set of values)...
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.
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.)
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).
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 self.name
UP = Direction(0, 1, "up")
DOWN = Direction(0, -1, "down")
LEFT = Direction(-1, 0, "left")
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.
class Directions:
up = 0
down = 1
left = 2
right =3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With