Function programming in Python
For efficiently programming with Python, we need to learn how to write functions. In this article we will be familiar with basic of function programming in Python 3 and learn about some Python’s built-in functions through several examples. As a prerequisite for this tutorial, it is better to learn the following first:
The following is a good resource to learn more about function programming subject:
Defining functions
We can use Python to define any function that we want. A function should include:
- name, to be able to refer it later
- documentation, to explain the function (optional)
- parameters, that could be zero or more
- body, that contains all computations that the function is doing
In general, we can use def
command to define functions
like:
def name(par1, par2, par3, ...):
"Documentation."
bodyreturn result
For example:
def mult(a, b):
"Two numbers multiplication."
= a * b
m return m
12, 13)
mult(## 156
In general, we can define three types of functions:
- computative, find the results by substituting parameters and doing calculations
- iterative, find the results by iteration (control flow tools)
- recursive, find the results by recursion (function itself)
In above example, we used a computative function to find a * b. Now let’s use iterative method:
def mult_itr(a, b):
"""
Two integers multiplication by iteration.
Note that a * b is equal to a + a + ... + a; b times.
"""
= 0
result while b > 0:
+= a
result -= 1
b return result
12, 13)
mult_itr(## 156
We can also use recursive method such that:
def mult_rec(a, b):
"""
Two integers multiplication by recursion.
Note that a * b is equal to a + a(b-1).
"""
if b == 1:
return a
else:
return a + mult_rec(a,b-1)
12, 13)
mult_rec(## 156
At each iteration, both iterative and recursive functions follow this pattern to find 12 * 13:
12
12 + 12
12 + 12 + 12
...12 + 12 + ... + 12 (13 times)
For another example let’s compute an integer factorial with all mentioned methods:
def fact(n):
"Compute n factorial by using Python math library"
import math
return math.factorial(n)
6)
fact(## 720
def fact_itr(n):
"""
Compute n factorial by iteration.
Note that n! is equal to n(n-1)(n-2)...1.
"""
= 1
result while n > 1:
*= n
result -= 1
n return result
6)
fact_itr(## 720
def fact_rec(n):
"""
Compute n factorial by recursion.
Note that n! is equal to n(n-1)!.
"""
if n == 1:
return n
else:
return n * fact_rec(n-1)
6)
fact_rec(## 720
Each of the above method has their pros and cons. For instance, let’s try a function that returns numbers in Fibonacci series:
def fib(n):
"""
Function to return nth Fibonacci number.
By using Binet's Fibonacci number formula.
"""
= 5**0.5
phi if n < 2:
return n
else:
return ((1+phi)**n - (1-phi)**n)/(2**n * phi)
for x in range(12)]
[fib(x) ## [0, 1, 1.0, 2.0, 3.0000000000000004, 5.000000000000001, 8.000000000000002, 13.000000000000002, 21.000000000000004, 34.00000000000001, 55.000000000000014, 89.00000000000003]
def fib_itr(n):
"""
Iterative function to return nth Fibonacci number.
Note that Fibonacci series is 0, 1, 0+1=1, 1+1=2, 1+2=3, 2+3=5, ... .
"""
= []
series = 0, 1
a, b while len(series) < n:
series.append(a)= b, a + b
a, b return a
for x in range(12)]
[fib_itr(x) ## [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
def fib_rec(n):
"""
Inefficient recursive function to return nth Fibonacci number.
Note that Fibonacci series is f(n-2) + f(n-1).
"""
if n < 2:
return n
else:
return fib_rec(n-2) + fib_rec(n-1)
for x in range(12)]
[fib_rec(x) ## [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Here, computative method provides an estimation (which is actually
a very good estimation) of the Fibonacci number and recursive method
requires a lot of time as n gets bigger since at each
iteration it needs to compute two fib_rec
such that (for
n = 5):
5)
fib_rec(|________
_______| |
4) fib_rec(3)
fib_rec(|___ ___|___
___| | | |
3) fib(2) fib(2) 1
fib(|__ __|__ __|__
__| | | | | |
2) 1 1 0 1 0
fib(|__
__| |
1 0
Therefore, it is very important to always choose the right approach based on the function context.
Python provides another method that let us to write functions in a very compact way that called lambda function.
Lambda function
Python includes a quick one-line construction of functions that is often convenient to make your code compact. In general we can write lambda function such that:
= lambda par1, par2, par3, ...: body name
For example:
= lambda x: x**3
cube
2)
cube(## 8
Which is same as:
def cube(x):
return x**3
2)
cube(## 8
For another example, let’s find Euclidean norm of a vector by a lambda function:
= lambda v, p = 2: sum([abs(x)**p for x in v])**(1/p)
pnorm
## Example
= [2,3,4]
v
pnorm(v)## 5.385164807134504
1)
pnorm(v,## 9.0
Or for instance, let’s replace an string in a mathematical formula with a number and find the answer:
= lambda f, x, z: eval(f.replace(str(x), str(z)))
re
## Example
= '2*x + 5'
f 'x',2)
re(f,## 9
For last example in here, let’s define single limit formula to find the second derivative of f(x) = x^3 for x = 2 by using lambda:
def deriv2nd(f, x, h = 1E-6):
"Single limit formula."
return (f(x-h) - 2*f(x) + f(x+h))/float(h**2)
= lambda x: x**3
f 2)
deriv2nd(f, ## 12.002843163827492
As we know, the second derivative of f(x) = x^3 is equal
to 6 * x which is equal to 12 for x = 2.
Note that we can find the derivative of functions by using
SymPy
package, for example:
import sympy
= sympy.symbols('x')
x = x**3
f = f.diff(x,2)
ff
ff## 6*x
2})
ff.subs({x:## 12
Map and filter
Map and filter are two Python built-in functions that let us to expand our function programming tools. In general, map function is:
map(function, iterable, ...)
When a function, that could be another built-in function or a created function by def or lambda, applies on each element of an iterable (i.e. lists, tuples or dictionaries - see here to learn more). For an example let’s assume we have sales rate for each month during each season of a year and are interested to know what is the total seasonal sales:
= [(1,3,4,5),(3,4,5,6),(7,6,5,5),(8,6,9,1)]
sale = list(map(sum, sale))
seasonal
seasonal## [13, 18, 23, 24]
We can answer the above question without using map by:
= [(1,3,4,5),(3,4,5,6),(7,6,5,5),(8,6,9,1)]
sale = []
seasonal for i in sale:
sum(i))
seasonal.append(
seasonal## [13, 18, 23, 24]
As you can see, without map we need a loop to address each element of the iterable.
For another example, let’s assume we have an iterable of strings (e.g. a list of names) and want them to be in lowercase:
= ['APPLE','ORANGE','PEACH','BANANA']
fruit list(map(str.lower, fruit))
## ['apple', 'orange', 'peach', 'banana']
Or we can use map to round a list of float numbers:
= [2.0678,3.9870,4.7869,5.3459]
flt list(map(round, flt, [2]*len(flt)))
## [2.07, 3.99, 4.79, 5.35]
Note that the above function has two iterables
(flt
and [2,2,2,2]
).
We can also use map to create new functions. For
instance, let’s define zip_diy
function that do same
operation as Python zip
function:
def zip_diy(x,y):
return map(lambda a, b: (a,b), x, y)
## Example
= [1,2,3]
list_1 = [3,2,1]
list_2 list(zip_diy(list_1, list_2))
## [(1, 3), (2, 2), (3, 1)]
list(zip(list_1, list_2))
## [(1, 3), (2, 2), (3, 1)]
As you noticed, map in zip_diy
includes a lambda function that generates
(a,b)
tuple for each element in given iterables,
x
and y
.
Filter is another important tool in function programming. As you probably guessed, filter lets us to filter an iterable. In general, filter function is:
filter(function, iterable)
When the function is required to return a boolean type
(True/False
) by testing each element of the
iterable. For an example let’s select numbers greater than
22 in a list in below:
= [88,22,11,33,2,99]
number list(filter(lambda x: x > 22, number))
## [88, 33, 99]
Traditionally we can use for
loop and if
condition to filter the list such that:
= [88,22,11,33,2,99]
number = []
select for i in number:
if i > 22:
select.append(i)
select## [88, 33, 99]
## Or
for i in number if i > 22]
[i ## [88, 33, 99]
Now let’s define a function to return numbers (integer, float and complex) from a list of numbers and strings:
def return_num(lis):
return filter(lambda x: isinstance(x, (int,float,complex)), lis)
## Example
= [2.05, 3, 'aaa', 4.23, 'bbb', 7j]
test list(return_num(test))
## [2.05, 3, 4.23, 7j]