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:
- 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."
body
return result
For example:
def mult(a, b):
"Two numbers multiplication."
m = a * b
return m
mult(12, 13)
## 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.
"""
result = 0
while b > 0:
result += a
b -= 1
return result
mult_itr(12, 13)
## 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)
mult_rec(12, 13)
## 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)
fact(6)
## 720
def fact_itr(n):
"""
Compute n factorial by iteration.
Note that n! is equal to n(n-1)(n-2)...1.
"""
result = 1
while n > 1:
result *= n
n -= 1
return result
fact_itr(6)
## 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)
fact_rec(6)
## 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.
"""
phi = 5**0.5
if n < 2:
return n
else:
return ((1+phi)**n - (1-phi)**n)/(2**n * phi)
[fib(x) for x in range(12)]
## [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 = []
a, b = 0, 1
while len(series) < n:
series.append(a)
a, b = b, a + b
return a
[fib_itr(x) for x in range(12)]
## [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)
[fib_rec(x) for x in range(12)]
## [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):
fib_rec(5)
_______|________
| |
fib_rec(4) fib_rec(3)
___|___ ___|___
| | | |
fib(3) fib(2) fib(2) 1
__|__ __|__ __|__
| | | | | |
fib(2) 1 1 0 1 0
__|__
| |
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:
name = lambda par1, par2, par3, ...: body
For example:
cube = lambda x: x**3
cube(2)
## 8
Which is same as:
def cube(x):
return x**3
cube(2)
## 8
For another example, let’s find Euclidean norm of a vector by a lambda function:
pnorm = lambda v, p = 2: sum([abs(x)**p for x in v])**(1/p)
## Example
v = [2,3,4]
pnorm(v)
## 5.385164807134504
pnorm(v,1)
## 9.0
Or for instance, let’s replace an string in a mathematical formula with a number and find the answer:
re = lambda f, x, z: eval(f.replace(str(x), str(z)))
## Example
f = '2*x + 5'
re(f,'x',2)
## 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)
f = lambda x: x**3
deriv2nd(f, 2)
## 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
x = sympy.symbols('x')
f = x**3
ff = f.diff(x,2)
ff
## 6*x
ff.subs({x:2})
## 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:
sale = [(1,3,4,5),(3,4,5,6),(7,6,5,5),(8,6,9,1)]
seasonal = list(map(sum, sale))
seasonal
## [13, 18, 23, 24]
We can answer the above question without using map by:
sale = [(1,3,4,5),(3,4,5,6),(7,6,5,5),(8,6,9,1)]
seasonal = []
for i in sale:
seasonal.append(sum(i))
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:
fruit = ['APPLE','ORANGE','PEACH','BANANA']
list(map(str.lower, fruit))
## ['apple', 'orange', 'peach', 'banana']
Or we can use map to round a list of float numbers:
flt = [2.0678,3.9870,4.7869,5.3459]
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
list_1 = [1,2,3]
list_2 = [3,2,1]
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:
number = [88,22,11,33,2,99]
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:
number = [88,22,11,33,2,99]
select = []
for i in number:
if i > 22:
select.append(i)
select
## [88, 33, 99]
## Or
[i for i in number if i > 22]
## [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
test = [2.05, 3, 'aaa', 4.23, 'bbb', 7j]
list(return_num(test))
## [2.05, 3, 4.23, 7j]
Copyright 2018-2023, Ashkan Mirzaee | Content is available under CC BY-SA 3.0 | Sourcecode licensed under GPL-3.0