In Python a function object is an object reference to any callable, such as a function, a lambda function, or a method. The definition also includes classes, since an object reference to a class is a callable that, when called, returns an object of the given class—for example, x = int(5)
. In computer science a functor is an object that can be called as though it were a function, so in Python terms a functor is just another kind of function object. Any class that has a __call__()
special method is a functor. The key benefit that functors offer is that they can maintain some state information. For example, we could create a functor that always strips basic punctuation from the ends of a string. We would create and use it like this:
strip_punctuation = Strip(",;:.!?")
strip_punctuation("Land ahoy!") # returns: 'Land ahoy'
Here we create an instance of the Strip
functor initializing it with the value ",;:.!?"
. Whenever the instance is called it returns the string it is passed with any punctuation characters stripped off. Here’s the complete implementation of the Strip
class:
class Strip:
def __init__(self, characters):
self.characters = characters
def __call__(self, string):
return string.strip(self.characters)
We could achieve the same thing using a plain function or lambda, but if we need to store a bit more state or perform more complex processing, a functor is often the right solution.
A functor’s ability to capture state by using a class is very versatile and powerful, but sometimes it is more than we really need. Another way to capture state is to use a closure. A closure is a function or method that captures some external state. For example:
def make_strip_function(characters):
def strip_function(string):
return string.strip(characters)
return strip_function
strip_punctuation = make_strip_function(",;:.!?")
strip_punctuation("Land ahoy!") # returns: 'Land ahoy'
The make_strip_function()
function takes the characters to be stripped as its sole argument and returns a function, strip_function()
, that takes a string argument and which strips the characters that were given at the time the closure was created. So just as we can create as many instances of the Strip
class as we want, each with its own characters to strip, we can create as many strip functions with their own characters as we like.
The classic use case for functors is to provide key functions for sort routines. Here is a generic SortKey
functor class (from file SortKey.py
):
class SortKey:
def __init__(self, *attribute_names):
self.attribute_names = attribute_names
def __call__(self, instance):
values = []
for attribute_name in self.attribute_names:
values.append(getattr(instance, attribute_name))
return values
When a SortKey
object is created it keeps a tuple of the attribute names it was initialized with. When the object is called it creates a list of the attribute values for the instance it is passed—in the order they were specified when the SortKey
was initialized. For example, imagine we have a Person
class:
class Person:
def __init__(self, forename, surname, email):
self.forename = forename
self.surname = surname
self.email = email
Suppose we have a list of Person
objects in the people
list. We can sort the list by surnames like this: people.sort(key=SortKey("surname"))
. If there are a lot of people there are bound to be some surname clashes, so we can sort by surname, and then by forename within surname, like this: people.sort(key=SortKey("surname", "forename"))
. And if we had people with the same surname and forename we could add the email attribute too. And of course, we could sort by forename and then surname by changing the order of the attribute names we give to the SortKey
functor.
Another way of achieving the same thing, but without needing to create a functor at all, is to use the operator
module’s operator.attrgetter()
function. For example, to sort by surname we could write: people.sort(key=operator.attrgetter("surname"))
. And similarly, to sort by surname and forename: people.sort(key=operator.attrgetter("surname", "forename"))
. The operator. attrgetter()
function returns a function (a closure) that, when called on an object, returns those attributes of the object that were specified when the closure was created.
Functors are probably used rather less frequently in Python than in other languages that support them because Python has other means of doing the same things—for example, using closures or item and attribute getters.