Python’s Functions Are First-Class

Python’s functions are first-class objects. You can assign them to variables, store them in data structures, pass them as arguments to other functions, and even return them as values from other functions.

Grokking these concepts intuitively will make understanding advanced features in Python like lambdas and decorators much easier. It also puts you on a path towards functional programming techniques.

In this tutorial I’ll guide you through a number of examples to help you develop this intuitive understanding. The examples will build on top of one another, so you might want to read them in sequence and even to try out some of them in a Python interpreter session as you go along.

Wrapping your head around the concepts we’ll be discussing here might take a little longer than expected. Don’t worry—that’s completely normal. I’ve been there. You might feel like you’re banging your head against the wall, and then suddenly things will “click” and fall into place when you’re ready.

Throughout this tutorial I’ll be using this yell function for demonstration purposes. It’s a simple toy example with easily recognizable output:

Because the yell function is an object in Python you can assign it to another variable, just like any other object:

>>>bark=yell

This line doesn’t call the function. It takes the function object referenced by yell and creates a second name pointing to it, bark. You could now also execute the same underlying function object by calling bark:

>>>bark('woof')'WOOF!'

Function objects and their names are two separate concerns. Here’s more proof: You can delete the function’s original name (yell). Because another name (bark) still points to the underlying function you can still call the function through it:

>>>delyell>>>yell('hello?')NameError:"name 'yell' is not defined">>>bark('hey')'HEY!'

By the way, Python attaches a string identifier to every function at creation time for debugging purposes. You can access this internal identifier with the __name__ attribute:

>>>bark.__name__'yell'

While the function’s __name__ is still “yell” that won’t affect how you can access it from your code. This identifier is merely a debugging aid. A variable pointing to a function and the function itself are two separate concerns.

(Since Python 3.3 there’s also __qualname__ which serves a similar purpose and provides a qualified name string to disambiguate function and class names.)

Functions Can Be Stored In Data Structures

As functions are first-class citizens you can store them in data structures, just like you can with other objects. For example, you can add functions to a list:

You can even call a function object stored in the list without assigning it to a variable first. You can do the lookup and then immediately call the resulting “disembodied” function object within a single expression:

>>>funcs[0]('heyho')'HEYHO!'

Functions Can Be Passed To Other Functions

Because functions are objects you can pass them as arguments to other functions. Here’s a greet function that formats a greeting string using the function object passed to it and then prints it:

defgreet(func):greeting=func('Hi, I am a Python program')print(greeting)

You can influence the resulting greeting by passing in different functions. Here’s what happens if you pass the yell function to greet:

>>>greet(yell)'HI, I AM A PYTHON PROGRAM!'

Of course you could also define a new function to generate a different flavor of greeting. For example, the following whisper function might work better if you don’t want your Python programs to sound like Optimus Prime:

defwhisper(text):returntext.lower()+'...'>>>greet(whisper)'hi, i am a python program...'

The ability to pass function objects as arguments to other functions is powerful. It allows you to abstract away and pass around behavior in your programs. In this example, the greet function stays the same but you can influence its output by passing in different greeting behaviors.

Functions that can accept other functions as arguments are also called higher-order functions. They are a necessity for the functional programming style.

The classical example for higher-order functions in Python is the built-in map function. It takes a function and an iterable and calls the function on each element in the iterable, yielding the results as it goes along.

Here’s how you might format a sequence of greetings all at once by mapping the yell function to them:

>>>list(map(yell,['hello','hey','hi']))['HELLO!','HEY!','HI!']

map has gone through the entire list and applied the yell function to each element.

Functions Can Be Nested

Python allows functions to be defined inside other functions. These are often called nested functions or inner functions. Here’s an example:

Of course you could then go on and call the returned function, either directly or by assigning it to a variable name first:

>>>speak_func=get_speak_func(0.7)>>>speak_func('Hello')'HELLO!'

Let that sink in for a second here… This means not only can functions accept behaviors through arguments but they can also return behaviors. How cool is that?

You know what, this is starting to get a little loopy here. I’m going to take a quick coffee break before I continue writing (and I suggest you do the same.)

Functions Can Capture Local State

You just saw how functions can contain inner functions and that it’s even possible to return these (otherwise hidden) inner functions from the parent function.

Best put on your seat belts on now because it’s going to get a little crazier still—we’re about to enter even deeper functional programming territory. (You had that coffee break, right?)

Not only can functions return other functions, these inner functions can also capture and carry some of the parent function’s state with them.

I’m going to slightly rewrite the previous get_speak_func example to illustrate this. The new version takes a “volume” and a “text” argument right away to make the returned function immediately callable:

Take a good look at the inner functions whisper and yell now. Notice how they no longer have a text parameter? But somehow they can still access the text parameter defined in the parent function. In fact, they seem to capture and “remember” the value of that argument.

Functions that do this are called lexical closures (or just closures, for short). A closure remembers the values from its enclosing lexical scope even when the program flow is no longer in that scope.

In practical terms this means not only can functions return behaviors but they can also pre-configure those behaviors. Here’s another bare-bones example to illustrate this idea:

In this example make_adder serves as a factory to create and configure “adder” functions. Notice how the “adder” functions can still access the n argument of the make_adder function (the enclosing scope).

Objects Can Behave Like Functions

Object’s aren’t functions in Python. But they can be made callable, which allows you to treat them like functions in many cases.

If an object is callable it means you can use round parentheses () on it and pass function call arguments to it. Here’s an example of a callable object:

Python Decorators: A Step-By-Step Introduction – Understanding decorators is a milestone for any serious Python programmer. Here’s your step-by-step guide to how decorators can help you become a more efficient and productive Python developer.

Python Iterators: A Step-By-Step Introduction – Understanding iterators is a milestone for any serious Pythonista. With this step-by-step tutorial you’ll understanding class-based iterators in Python, completely from scratch.

Functional linked lists in Python – Linked lists are fundamental data structures that every programmer should know. This article explains how to implement a simple linked list data type in Python using a functional programming style.

Working With File I/O in Python – Learn the basics of working with files in Python. How to read from files, how to write data to them, what file seeks are, and why files should be closed.

How to Reverse a String in Python – An overview of the three main ways to reverse a Python string: “slicing”, reverse iteration, and the classic in-place reversal algorithm. Also includes performance benchmarks.