Reputation: 2227
I was looking for a Command pattern implementation in Python... (According to Wikipedia,
the command pattern is a design pattern in which an object is used to represent and encapsulate all the information needed to call a method at a later time.
)
The only thing I found was Command Dispatch pattern:
class Dispatcher:
def do_get(self): ...
def do_put(self): ...
def error(self): ...
def dispatch(self, command):
mname = 'do_' + command
if hasattr(self, mname):
method = getattr(self, mname)
method()
else:
self.error()
May be I'm wrong, but it looks like these are two different concepts, which accidentally have similar names.
Am i missing something?
Upvotes: 27
Views: 18682
Reputation: 54882
The simplest command pattern is already built into Python, simply use a callable:
def greet(who):
print "Hello %s" % who
greet_command = lambda: greet("World")
# pass the callable around, and invoke it later
greet_command()
The command pattern as an object oriented design pattern makes more sense if your commands need to be able to do more than just be invoked. Common usecase is when you need to be able to undo/redo your actions. Then a command class is a good way to couple the forward and backwards actions together. For example:
class MoveFileCommand(object):
def __init__(self, src, dest):
self.src = src
self.dest = dest
os.rename(self.src, self.dest)
def undo(self):
os.rename(self.dest, self.src)
undo_stack = []
undo_stack.append(MoveFileCommand('foo.txt', 'bar.txt'))
undo_stack.append(MoveFileCommand('bar.txt', 'baz.txt'))
# foo.txt is now renamed to baz.txt
undo_stack.pop().undo() # Now it's bar.txt
undo_stack.pop().undo() # and back to foo.txt
Upvotes: 65
Reputation: 375574
If I recall the gang of four correctly, the Command pattern is about commands like "File - Save", not commands like "svn commit", which is what your code is good for.
Martin suggests the Command pattern is unneeded because functions as first class objects take its place, but the Command pattern is richer than just doit()
, having, for example, also undo()
, is_enabled()
, etc.
Upvotes: 4
Reputation: 15019
Did some searching and found this. It appears to do the job of encapsulating an action.
def demo(a,b,c):
print 'a:',a
print 'b:',b
print 'c:',c
class Command:
def __init__(self, cmd, *args):
self._cmd=cmd
self._args=args
def __call__(self, *args):
return apply(self._cmd, self._args+args)
cmd=Command(dir,__builtins__)
print cmd()
cmd=Command(demo,1,2)
cmd(3)
Upvotes: 4
Reputation: 127457
Yes, you do miss something: the command pattern is only necessary in languages that don't have function pointers (or functions as first class objects), such as Java. In languages with functions-as-objects, you can use the function itself; no need to have a separate command object (which then should have a "doit" method).
In the example could you quote, the getattr()
call gives you the "command object" (i.e. the bound method); adding parenthesis after it "invokes" (i.e. calls) the command object.
Upvotes: 5