< Previous Page | Next Page >
So far, our scripts have been simple, single-use code blocks.
One way to organize our Python code and to make it more readable and reusable is to factor-out useful pieces into reusable functions.
Here we'll cover two ways of creating functions: the def
statement, useful for any type of function, and the lambda
statement, useful for creating short anonymous functions.
A function is a snippet of code that is used to group your entire program into smaller, and more modular chunks of code that can be "called" separately. This also helps to reuse code and avoid writing the same code over and over again.
We've seen functions before. For example, print
in Python 3 is a function:
print('abc')
Here print
is the function name, and 'abc'
is the function's argument.
In addition to arguments, there are keyword arguments that are specified by name.
One available keyword argument for the print()
function (in Python 3) is sep
, which tells what character or characters should be used to separate multiple items:
print(1, 2, 3)
print(1, 2, 3, sep='--')
When non-keyword arguments are used together with keyword arguments, the keyword arguments must come at the end.
Functions become even more useful when we begin to define our own, organizing functionality to be used in multiple places.
In Python, functions are defined with the def
statement.
For example, we can encapsulate a version of our Fibonacci sequence code from the previous section as follows:
def fibonacci(N):
L = []
a, b = 0, 1
while len(L) < N:
a, b = b, a + b
L.append(a)
return L
Now we have a function named fibonacci
which takes a single argument N
, does something with this argument, and return
s a value; in this case, a list of the first N
Fibonacci numbers:
fibonacci(10)
Python functions can return any Python object, simple or compound, which means constructs that may be difficult in other languages are straightforward in Python.
For example, multiple return values are simply put in a tuple, which is indicated by commas:
def real_imag_conj(val):
return val.real, val.imag, val.conjugate()
r, i, c = real_imag_conj(3 + 4j)
print(r, i, c)
As you can see, a function is signalled by the keyword def
, which is followed by the name the function should have. In brackets we can enter one or multiple parameters the function should take as an input (a function can also have no parameters, then we just use empty brackets ( )
). Think of the parameters as variables or values that can be taken in and later be used inside the function’s code. Inside the function body we also have the possibility to add a docstring
(signalled by triple quotation marks). A docstring is just a quick description of what the function does that can be used by the programmer for more clarity. If you want to read a docstring of a function, you can just enter the function's name, append .doc and print it. print(addAllNumbers.__doc__)
will for example print the docstring of the function defined above.
#Basic structure of Functions in Python
def addAllNumbers(myList):
"""This function adds the numbers of a given list"""
sumOfList = sum(myList)
print("The sum of all numbers is: {}".format(sumOfList))
#Calling a function (running the code in a function)
addAllNumbers([1,1,1,1]) #Result: The sum of all numbers is 4
addAllNumbers([1,2,3,4]) #Result: The sum of all numbers is 10
print(addAllNumbers.__doc__)
An important concept to understand is the return statement of a function. Previously, we mentioned that parameters take values from outside the function to use them inside the function. The return is kind of the opposite of that. It can be used to take a value from inside the function and make it accessible outside of the function. Before we looked only at functions that calculated a result without returning anything. This means, we calculated the sum of all numbers in a list, but only printed it. However, it might be necessary to use the result from the function somewhere else in our code, i.e. we do not want to print the result out just yet. This is when we use the return statement. Let us see how the returned value can be accessed from the outer code.
#Function that instead of printing returns the calculated result.
def addAllNumbers(myList):
sumOfList = sum(myList)
return sumOfList
#Assign the return value to a variable
returnedValue = addAllNumbers([1,1,1,1]) #The variable returnedValue now stores the returned value (4)
print(returnedValue)
Recursion is the process when a function calls itself in its code. This is a very powerful tool to write more compact and easier to understand code. Recursion is often used in searching and sorting algorithms.
#We can make a function that calculates the factorial of a number (e.g. 5 * 4 * 3 * 2 * 1)
def factorial(n):
if n == 1:
#If we arrive at n==1 stop the recursion and return.
return n
else:
#Call the function again with the parameter (n-1).
return n*factorial(n-1)
#Call the function the first time
factorial(5)
Often when defining a function, there are certain values that we want the function to use most of the time, but we'd also like to give the user some flexibility.
In this case, we can use default values for arguments.
Consider the fibonacci
function from before.
What if we would like the user to be able to play with the starting values?
We could do that as follows:
def fibonacci(N, a=0, b=1):
L = []
while len(L) < N:
a, b = b, a + b
L.append(a)
return L
With a single argument, the result of the function call is identical to before:
fibonacci(10)
But now we can use the function to explore new things, such as the effect of new starting values:
fibonacci(10, 0, 2)
The values can also be specified by name if desired, in which case the order of the named values does not matter:
fibonacci(10, b=3, a=1)
lambda
) Functions¶Earlier we quickly covered the most common way of defining functions, the def
statement.
You'll likely come across another way of defining short, one-off functions with the lambda
statement.
It looks something like this:
add = lambda x, y: x + y
add(1, 2)
This lambda function is roughly equivalent to
def add(x, y):
return x + y
So why would you ever want to use such a thing? Primarily, it comes down to the fact that everything is an object in Python, even functions themselves! That means that functions can be passed as arguments to functions.
As an example of this, suppose we have some data stored in a list of dictionaries:
data = [{'first':'Guido', 'last':'Van Rossum', 'YOB':1956},
{'first':'Grace', 'last':'Hopper', 'YOB':1906},
{'first':'Alan', 'last':'Turing', 'YOB':1912}]
Now suppose we want to sort this data.
Python has a sorted
function that does this:
sorted([2,4,3,5,1,6])
But dictionaries are not orderable: we need a way to tell the function how to sort our data.
We can do this by specifying the key
function, a function which given an item returns the sorting key for that item:
# sort alphabetically by first name
sorted(data, key=lambda item: item['first'])
# sort by year of birth
sorted(data, key=lambda item: item['YOB'])
While these key functions could certainly be created by the normal, def
syntax, the lambda
syntax is convenient for such short one-off functions like these. Lambda functions will be covered more extensively in the Data Science Chapter of this coding crashcourse
< Previous Page | Home Page | Next Page >