views:

149

answers:

2

Example:

>>> def write_to_terminal(fmt, *args):
...     print fmt % args
>>> LOG = logging.getLogger(__name__)
>>> info = multicall(write_to_terminal, LOG.info)
>>> debug = multicall(write_debug_to_terminal, LOG.debug)
>>> ...
>>> info('Hello %s', 'guido') # display in terminal *and* log the message

Is there an elegant way to write multicall? Perhaps with the help of the standard library .. without reinventing the wheel?

+5  A: 

Something like this?

def multicall(*functions):
    def call_functions(*args, **kwds):
        for function in functions:
            function(*args, **kwds)
    return call_functions

And if you want to aggregate the results:

def multicall(*functions):
    def call_functions(*args, **kwds):
        return [function(*args, **kwds) for function in functions]
    return call_functions

EDIT

Decorators were suggested; in that case it would look like this:

def appendcalls(*functions):
    def decorator(decorated_function):
        all_functions = [decorated_function] + list(functions)
        def call_functions(*args, **kwds):
            for function in all_functions:
                function(*args, **kwds)
        return call_functions
    return decorator


LOG = logging.getLogger(__name__)

@appendcalls(LOG.info)
def info(fmt, *args):
    print fmt % args

info('Hello %s', 'guido')

appendcalls() takes any number of functions to be called after the decorated function. You may want to implement the decorator differently, depending on what return value you want -- the original from the decorated function, a list of all function results or nothing at all.

Steef
It seems that even `functools` doesn't help in avoiding to write this myself.
Sridhar Ratnakumar
Actually, I want to retain the original `info` (`write_to_terminal` in my example) and provide the new info (which also calls LOG) *in addition to* the existing version that does not call LOG. `@appendcalls` does not retain the original function. To example, I don't want to "append" one function to another .. but create a *new* function that calls N number of other functions passing the same arguments.
Sridhar Ratnakumar
@snd, in this case you can't use a decorator (which by definition reassigns the name of the function it's decorating!), and @Steef's answer's first part (before the Edit:) is what you're looking for. And yep, functools doesn't offer this rarely-needed, easy-to-write HOF (nor a trillion others, for that matter!-)
Alex Martelli
+1  A: 

You could look into Python decorators.

A clear description is here: http://www.artima.com/weblogs/viewpost.jsp?thread=240808

rledley
+1, this sounds like excellent usage for decorators
kigurai
How exactly would you apply a decorator over more than one function?
Sridhar Ratnakumar