General Command pattern and Command Dispatch pattern in Python


Question

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?

1
23
9/29/2009 7:23:23 PM

Accepted Answer

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
57
9/15/2013 10:41:21 AM

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)

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon