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:
We can use Python to define any function that we want. A function should include:
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:
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.
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 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]