Passing functions as arguments to other functions

Transcript

In Python you can pass a function to another function as an argument.

Passing a function as an argument

We have a list of strings represent fruit names:

>>> fruits = ['kumquat', 'cherimoya', 'Loquat', 'longan', 'jujube']

If we wanted to alphabetize this list we could pass it to the built-in sorted function:

>>> sorted(fruits)
['Loquat', 'cherimoya', 'jujube', 'kumquat', 'longan']

Except that sorted doesn't quite alphabetize this list because when Python sorts strings it puts all the uppercase letters before all the lowercase letters (due to their ASCII/unicode ordering).

We can fix this, by supplying a key argument to the built-in sorted function.

>>> help(sorted)

sorted(iterable, /, *, key=None, reverse=False)
    ...

This key argument should be a function that will be called with each of the individual items in our fruits iterable and it should return the thing to sort by.

In our case, we could make a function that lowercases any string given to it in order to sort by the lowercase versions of each string:

>>> def lowercase(string):
...     return string.lower()
...

To sort by lowercased strings we'll call sorted with a key argument that points to this lowercase function:

>>> sorted(fruits, key=lowercase)
['cherimoya', 'jujube', 'kumquat', 'longan', 'Loquat']

That properly alphabetized our list of strings (while maintaining their original capitalization in the resulting list).

Notice that when we called sorted, we didn't put parentheses (()) after lowercase to call it, like this:

>>> lowercase('X')
'x'

We didn't call lowercase at all. Instead we just referred to lowercase:

>>> lowercase
<function lowercase at 0x7fc8a3fc09d0>

We passed the lowercase function object to the sorted function (via its key argument):

>>> sorted(fruits, key=lowercase)

So that sorted could call lowercase itself repeatedly on each of the individual items of fruits list (using the return value it got as a sort of comparison key to sort by).

So in Python, there are functions that accept other functions as arguments.

Writing a function accepting function as argument

We can also make our own functions that accept a function as an argument.

We have a function, called get_two, that accepts a function object (func) and a value (thing):

>>> def get_two(func, thing):
...     return func(thing), func(thing)
...

And it calls the given function object with the given thing two times (returning a two-item tuple).

If we import the random module and call get_two with random.randrange and the number 3:

>>> import random
>>> get_two(random.randrange, 3)
(2, 0)

It will call random.randrange on 3 two times:

>>> get_two(random.randrange, 3)
(2, 0)

This function call is randomly generating two numbers between 0 and 2 (either 0, 1, or 2):

>>> get_two(random.randrange, 3)
(2, 2)
>>> get_two(random.randrange, 3)
(1, 2)
>>> get_two(random.randrange, 3)
(0, 1)

This get_two function accepts a function as an argument:

>>> def get_two(func, thing):
...     return func(thing), func(thing)
...

The one thing that you can do with every function is call it. So this get_two function is assuming that func points to a function object or some other callable object (anything that you can call by putting parentheses after it).

Summary

In Python you can pass function objects in to other functions. Functions can be passed around in Python.

In fact there are functions built into Python that expect functions to be given as one or more of their arguments so that they can then call them later.