views:

598

answers:

5

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?

+3  A: 

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.

Martin v. Löwis
I think it's a bad idea to presume that the Command pattern isn't "necessary" just because a function is a first class object. The command pattern isn't just about passing around callables, it's about creating robust descriptions of execution models. It's also often useful to be able to persist a partially applied command for some period of time, then recall that command and complete it later. Persisting functions in python is tricky, persisting a user defined command is less so.
free-dom
+1  A: 

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)
FModa3
+1  A: 

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.

Ned Batchelder
Indeed, the Command pattern is richer
I believe the integration of undo is confusing issues. Every description of the command pattern I have looked at mentions that you can undo, but then the actual code only has an interface for an Execute() method (with no support for Undo). So I think the perceived "richness" of the command pattern actually mixes independent use cases, where the primary use case is only about *parameter-less* callback operations.
Martin v. Löwis
+4  A: 

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
        self()
    def __call__(self):
        os.rename(src, dest)
    def undo(self):
        os.rename(dest, 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
Ants Aasma
A: 

A possible (?) variation which I have used numerous times.

Here goes the simplified version

from functools import wraps, update_wrapper

class Command(object):
    def __init__(self, f):
        update_wrapper(self, f)
        if getattr(f, 'console_debug', None):
            f = console_debugger(f)
        self.f = f
    def __call__(self, *args, **kw):
        res = self.f(*args, **kw)
        return res

def console_debugger(f):
    def wrapper(*args, **kw):
        print f.__name__, 'called with :', args, kw
        res = f(*args, **kw)
        print f.__name__, 'returned :', res
        return res
    return wrapper

def add(x, y):
    return x + y
add.console_debug = True

def fail(x, y):
    """ I fail :( """
    a + b
fail.console_debug = True

add = Command(add)
fail = Command(fail)

add(1, 2)
print fail.__doc__
fail(1, 2)
Shekhar