tags:

views:

318

answers:

5

What's the best way to pass a method and a method parameter to another method?

Is there a better way to do the following?

def method1(name)
    return 'Hello ' + name

def method2(methodToCall, methodToCallParams, question):
    greetings = methodToCall(methodToCallParams)
    return greetings + ', ' + question

method2(method1, 'Sam', 'How are you?')
A: 

You're thinking of currying, where you bind a function and arguments together to be called later. Usually currying is used so that you can add additional arguments at the time the function is actually called.

Rather than re-write the wheel, here's a link to an example: http://code.activestate.com/recipes/52549/.

If, however, the case you've mocked up in the question really is that simple, you can pass a list of args as positional parameters, or a list of kwargs as named parameters, to another function.

def method1(name):
    return 'Hello %s' % name

args = ['Joe']
method1(*args)

def method1a(name=None, salutation=None):
    return 'Hello %s %s' % (name, salutation)

kwargs = {'name':'Joe', 'salutation':'Mr'}
method1a(**kwargs)
Jarret Hardie
This doesn't really answer the question: you're only showing how to pack and unpack arguments, not how to bind them to a function and pass it around as one.
jkp
+10  A: 

Hey there

If you want to package the invocation up in one hit, you can use the functools module:

from functools import partial

def some_function(param_one, param_two):
    print "Param One: %s" % param_one
    print "Param Two: %s" % param_two

def calling_function(target):
    target()

calling_function(partial(some_function, "foo", "bar"))

You can do tweakier things with functools.partial too, such as binding only some parameters, leaving you with a function with a new signature. It's overkill in a lot of cases to use it but it certainly has it's place.

jkp
wow this is exactly what I'm looking for except that I'm on python 2.4 and this requires 2.6 :( If I don't get a better answer, I'll accept your answer
Dan
You can always upvote :)
jkp
i don't have my 15 rep yet :(
Dan
I just posted an answer showing how to create a roughly equivalent function to functools.partial for use with Python versions prior to 2.5, in conjunction with @jkp's answer it should get the job done.
Jay
+3  A: 

You could do it this way:

def method1(name):
    def wrapper():
        return 'Hello ' + name
    return wrapper

def method2(method, question):
    output = method()
    return output + ', ' + question

method2(method1(name = 'Sam'), 'How are you?')

You can of course pass some variables in the method() call too:

def method1(name):
    def wrapper(greeting):
        return greeting + name
    return wrapper

def method2(method, question):
    output = method(greeting = 'Hello ')
    return output + ', ' + question

method2(method1(name = 'Sam'), 'How are you?')
Caotic
Great.. it works perfectly
Dan
functools.partial is a cleaner way to do this.
Carl Meyer
+1  A: 

Another option, if you are working on a Python version pre 2.5 is to use a lambda as a closure:

def some_func(bar):
    print bar

def call_other(other):
    other()

call_other(lambda param="foo": some_func(param))

HTH

jkp
I need the return value.. but I'm upping u cause it's still a nice answer
Dan
+2  A: 

You can used functools.partial to do this, as jkp pointed out

However, functools is new in Python 2.5, so to handle this in the past I used the following code (this code is in the Python docs for functools.partial, in fact).

# functools is Python 2.5 only, so we create a different partialfn if we are
# running a version without functools available
try:
    import functools
    partialfn = functools.partial
except ImportError:
    def partialfn(func, *args, **keywords):
        def newfunc(*fargs, **fkeywords):
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords)
            return func(*(args + fargs), **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc
Jay