Today we’ll be talking about functions in Python. Functions in Python are often described as first-class objects. It just means they are objects no less than integers, strings or our custom objects created by instantiating classes. In particular it means, functions may be used just like the other objects: they can be assigned to variables, they can be passed as arguments to other functions and they can be returned from other functions.
Let’s have a look at each of these cases one by one.
Functions Assigned to Variables
First, let’s see how functions may be assigned to variables:
Here’s a simple function that prints a character in rows and columns:
>>> def dense_repeat(character, rows, columns):
... for row in range(rows):
... for column in range(columns):
... print(f" {character} ", end = "")
... print()
...
Let’s call it to see how it works:
>>> dense_repeat ("X", 4, 6)
X X X X X X
X X X X X X
X X X X X X
X X X X X X
Now let’s assign the function to a variable:
>>> a = dense_repeat
Now we can use the variable as the function:
>>> a("+", 8, 15)
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
Functions Passed As Arguments to Functions
OK, the next thing we can do is pass a function as an argument to another function. Here’s the function we used before:
def dense_repeat(character, rows, columns):
for row in range(rows):
for column in range(columns):
print(f" {character} ", end = "")
print()
Here’s a similar function. It just adds more spaces around the characters:
def scarce_repeat(character, rows, columns):
for row in range(rows):
for column in range(columns):
print(f" {character} ", end = "")
print("\n")
Now let’s create another function. It will take a function as an argument and call it:
def repeat(func, character, rows, columns):
# We can use the __name__ attribute on the function object
# to retrieve the function's name.
print(f"using the {func.__name__} function:")
func(character, rows, columns)
Now let’s use our repeat function twice. We’ll pass the dense_repeat function first and then the scarce_repeat function. The other arguments are the same so that it’s easier to compare how the two functions work:
repeat(dense_repeat, "X", 6, 8)
repeat(scarce_repeat, "X", 6, 8)
And here is the output:
using the dense_repeat function:
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
using the scarce_repeat function:
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
X X X X X X X X
Functions Returned from Functions
Finally, let’s see how functions may be returned from other functions:
Let’s modify the dense_repeat function so that it returns a function:
def dense_repeat(character):
def layout(rows, columns):
for row in range(rows):
for column in range(columns):
print(f" {character} ", end = "")
print()
# Now the dense_repeat function returns the layout function.
return layout
# If we now assign the result returned from the dense_repeat function
# to a variable, what we actually assign is the layout function.
star_pattern = dense_repeat("*")
# Now we can use star_pattern as the layout function. What's interesting,
# it still remembers the character to print, although the enclosing
# dense_repeat function has already returned. We call such functions
# closures.
star_pattern(10, 8)
If you want to learn more about closures, I have an article about them, so feel free to read it.
And here’s the output:
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
* * * * * * * *
As you can see, you can use functions just like any other objects. This makes them first-class objects.
Here’s the video version of this article: