Partial Function Application

As we will see when we begin GUI programming, we sometimes have situations where we need to call a particular function, but we actually know what one of the parameters will be when we are writing the code. For example, we might have several buttons that all need to invoke the same function, but parameterized in some way by which particular button is the cause of the invocation.

In the simplest case we want to store a function (i.e., an object reference to a function) that we can then call later. A function stored like this is known as a callback. Here is a trivial example:

def hello(who):

print "Hello", who def goodbye(who):

print "Goodbye", who funclist = [hello, goodbye] # Some time later for func in funclist: func("Me")

This prints "Hello Me", and then "Goodbye Me". Here, we have stored two functions and then called them later on. Notice that we passed the same argument, "Me", each time we called func(). Since we know what the argument is in ad vance, it would be nice to be able to somehow package up both the function to be called and the parameter we want to use into a single callable object.

A solution to this is partial function application (also known as "currying"), which simply means that we take a function and zero, one, or more parameters for it, and wrap them up into a new function which, when invoked, will call the original function with the parameters we wrapped, and with any others that are passed at call time. Such wrapped functions are called closures because they encapsulate some of their calling context when they are created.

To get a flavor for how this works, let us imagine a very simple GUI program where we have two buttons that, when pressed, will call the same action() function. (We won't worry about how we transform button presses into function calls right now; it is very easy, and fully explained in Chapter 4.)

def action(button):

print "You pressed button", button

Now when we create our buttons, naturally we know which ones they are, so we want to tell the first button to make the call action("One") and the second to call action("Two"). But this presents us with a problem. We know what we want called, but we don't want the call to take place until a button is pressed. So, for example, we want to give the first button a function which wraps action() and the parameter "One", so that when the first button is pressed it can call action() with the right parameter.

So, what we need is a function that will take a function and an argument and return a function, that when, called will call the original function with the original argument. In Python 2.5, this is easy assuming our previous definition of action():

import functools buttonOneFunc = functools.partial(action, "One") buttonTwoFunc = functools.partial(action, "Two")

The functools.partial() function takes a function as the first argument, and then any number of other arguments, and returns a function that, when called, will call the passed function with the passed arguments, and with any additional arguments that are given at call time.

So, when buttonOneFunc() is called, it will simply call action("One") just as we want. As we mentioned earlier, a function's name is simply an object reference that happens to refer to a function, so it can be passed as a parameter like any other object reference.

But where does this leave users of earlier versions of Python? We could provide our own very simple and less powerful version of partial(). For example:

Python def partial(func, arg): def callme():

return func(arg) return callme

Inside the partial() function we create an inner function, callme(), that, when called, will call the function and argument that were passed to the partial() function. After creating the callme() function, we then return an object reference to it so that it can be called later.

This means that we can now write:

buttonOneFunc = partial(action, "One") buttonTwoFunc = partial(action, "Two")

Ideally, it would be nice to use functools.partial() when it is available, and fall back on our own simple partial() function otherwise. Well, since we can define functions at runtime, this is perfectly possible:

import sys if sys.version_info[:2] < (2, 5): def partial(func, arg): def callme():

return func(arg) return callme else:

from functools import partial

The if statement ensures that if we are using a version of Python older than 2.5 we create a partial() function that takes a function and a single argument and returns a function that, when called, will call the function passed in with the argument. But if we are using a later version of Python, we use the functools.partial() function, so in our code we can always call partial(), and whichever version was created will be the one used.

Now, just as before, we can write:

buttonOneFunc = partial(action, "One") buttonTwoFunc = partial(action, "Two")

Only this time the code will work with both old and new versions of Python.

The partial() function we have defined is just about the simplest possible. It is also possible to create much more sophisticated wrappers that can take positional and keyword arguments at the time they are wrapped, and additional positional and keyword arguments at the time they are called; functionality that functools.partial() already provides. We use partial() in several places from Part II onward, but in each case the simple partial() function shown in this section could be used if Python 2.5 or later was not available.

In the next section, we will continue to take a fairly high-level view of functions, and look at the possibilities that are available to us for the notification and handling of error conditions.

Was this article helpful?

0 0


The main focus of this report is to show how to get involved in video marketing on the run, how to rank quickly on YouTube and Google using FREE semi-automatic tools and services. QUICKLY AND FREE. I will show methods and techniques I use to rank my videos, as well as free resources and tools to make video clips, to get backlinks and free traffic.

Get My Free Ebook

Post a comment