Lecture 2.3
Guoliang Ma
The Chow Institute, 2026
Five use cases of Python functions.
The Eighth Python Enhancement Proposal.
AoL 3 (M)
Before we start decorators, let’s review what we’ve learned.
Write a function to compute the average of t1.
Write a function to compute the standard deviation of t1.
Write a function to compute the skewness of t1.
t1 = (1, 2, 3, 4, 5)
AoL 3 (M)
If you encountered an error when we tried to pass t2 to our function. The right thing to do is to make sure there is no invalid values in the arguments passed to our function. Here we introduce the assert statement.
assert is a claim, we assert an expression (what is an expression?). If the value of the expression is True, nothing happens and the program proceeds. But if the value is False the assertion fails, and an error occurs.
Apply your average function to t2.
t2 = (1, 2, 3, None, 5)
AoL 3 (M)
Examples of assert statements
1. Use assert to improve your functions.
assert None in [1, 2, 3], "All values look good!"
assert None not in [1, 2, 3], "Contains None!"
Although assert is handy, if we have written so many functions, it is hard for us to modify all of them. In other cases, if someone else defines the function for us, we cannot modify the function ourselves.
So when we want to extend the functionality of a function, what we can do is to define a higher-order function, and use the function we want to modify as its argument. Do whatever we want, and then send out the new function.
AoL 2 (H)
Syntax template
# to define a decorator as a higher order function
def decor(func):
def wrapper(*args, **kwargs):
...
return func(*args, **kwargs)
...
return wrapper
# to decorate a function
@decor
def func():
pass
AoL 3 (M)
Write decorators to:
Print a line “<function name> function is being called." before the execution and print a line after the execution. "finish calling <function name> function.
Record return values of previous function calls. Every time a function is called, record the return value so next time the function is called, we don't have to wait the function to run.
In[]: print(1)
Out[]: print function is being called.
finish calling print function.
A function is recursive when it calls itself, directly or indirectly. Recursion does not require passing the function as an argument.
We’ll rewrite two functions we have already written and introduce three more examples:
Revisit Newton’s method
Revisit the sums function
Finding square root
Fibonacci sequence
Factorial function
Suppose we want to find the root of . Start from .5.
By Newton’s method,
.
def newton(x):
return x-(x**2-3)/(2*x)
x = 0.5
for _ in range(10):
x = newton(x)
print(x)
Pay attention to the relation
Is it the same as
for _ in range(10):
x = newton(x)
newton(newton(newton(newton(newton(newton(newton(newton(newton(newton(0.5))))))))))
We can simplify the manual recursive calls
This unfinished starting point recurses without making progress. Add an update and a stopping condition before running it.
Summary:
A boundary condition is used to …
A recursion body is used to …
The difference between recursion and loops include …
def newton_recursion(x):
return newton_recursion(x) # unfinished: no update or stopping condition
AoL 2 (H)
Based on what we learned from the previous example, can you see how the sum_recursion function works?
def sum_recursion(x):
if len(x) == 0:
return 0
else:
return x[0] + sum_recursion(x[1:])
AoL 2 (H)
We have relied on the Newton’s method to find the square root of a number. There is another method.
Mathematically, if we want to find the root of , the root must satisfy
If the relation is satisfied, then we will see
Or,
AoL 3 (M)
is a recursive relation
Why? If we add superscripts you’ll see
Write a root_recursion function to find the square root of a number.
Hint: Start from finding the square root of 7 and then generalize your function.
AoL 3 (M)
Recursive relation is fundamental in guiding the implementation of a recursion function. You’re (or should be) extremely familiar with it
请推导斐波那契数列的通项公式。它的递推公式为:
Please implement a Python function to compute the values of the Fibonacci sequence.
Hint: you can choose either approach.
AoL 3 (M)
As a last exercise, we work on an easier one.
Please write a function to compute the factorial of some number.
AoL 2 (H)
Let’s start from the familiar max function. You already know the output of the max function:
If you try max(“hello”, “world”, “Python”), you’ll see Python can also compute the maximum. But what does it mean?
max((1, 2, 3))
max(1, 2, 3, 4)
If we’re not satisfied with the default behavior, we can change it by defining a helper function that
takes the elements for comparison as parameters
returns the criterion by which we want to sort
def helper(s):
return len(s)
max("hello", "world", "Python", key=helper)
Is it unnecessary to define the helper function every time we want to find the maximum of some elements according to some standards. We can abandon the name of the function and just use it.
This is known as anonymous functions
The syntax is …
max("hello", "world", "Python", key=lambda x: len(x))
Now let’s turn to three useful reducing functions.
The map function applies a function to every element of a sequence.
The filter function selects the elements according to a standard. Similar to the helper function in the map function. We need to set the standard as a function. But this time, the return value of the helper function has to be Boolean.
map(len, ("hello", "world", "Python"))
def long_string(x):
return len(x) > 5
filter(long_string, ("hello", "world", "Python"))
AoL 3 (M)
Replace the helper function with an anonymous function.
The last technique is the reduce function. Unlike the other two, we need to import it from the functools module.
The reduce function requires a helper function. The helper function for reduce must
take two parameters
return an accumulator that can be combined with the next item
Search the web and explain what functional programming means.
from functools import reduce
reduce(lambda x, y: x + " " + y, ("hello", "world", "Python"))
AoL 3 (M)
We have learned iterators (what is an iterator?). Generators are a special way to create iterators. There are several ways we can make generators.
We can create our own generators in two ways:
through generator expression and
through a generator function.
List examples of functions that output generators.
AoL 3 (M)
We learned the idea of comprehensions in Chapter 1. The parentheses enclosed comprehensions were not "tuple comprehensions." They are generators.
Make a generator to help compute squares (1, 4, 9, 16, ...). What is necessary for you to define a generator like this?
type((x for x in range(5)))
Generator functions are almost the same as regular functions, except that we use the word yield. Wherever in a function there is an yield, the function becomes a generator function.
Yield means to give up right to someone else. The yield signs we see on road tell us to give up road right to others.

In a computer program, when a generator function sees yield, it does the same thing. The generator function is suspended and other functions can use the computing resources. When we resume the generator with next() or iteration, it regains the computing resources until it sees yield next time.
AoL 2 (H)
Example 2.4.1 generator function
import time
def g():
print(f'g() sleeping: gi_state: {g1.gi_running}')
time.sleep(3)
print(f'g() sees yield: gi_state: {g1.gi_running}')
yield 1
g1 = g()
next(g1)
print("after, gi_state:", g1.gi_running)
Now we see how generator function works, and we can define a generator with it. Suppose we want to compute the squares of integers from 1 to infinity. This is impossible if we are using a list (why?). But with a generator, we don't need to store all squares in computer. Just compute them on-the-fly.
def squares():
i = 1
while True:
yield i ** 2
i += 1
AoL 2 (H)
We introduce error message reading and handling here.
To handle an error, we rely on the try-except control flow.
The following code shows you how to print a hand-written number:
with open("../data/numbers/number1.csv", "r") as f:
header = f.readline()
data = f.readline()
import matplotlib.pyplot as plt
import numpy as np
pixels = np.array(data.split(","), dtype='uint8').reshape((28, 28))
plt.imshow(pixels)
plt.imsave("./mnist1.png", pixels)
AoL 3 (M)
You can see that the flow is interrupted because of an error. But all other pictures are good. Checking all data before we plot and save the pictures is a daunting task. So we need to rely on the try-except control flow. The syntax is as follows:
Plot and save all numbers in the numbers folder.
try:
...
except KeyError as e:
...
else:
...
finally:
...
The Python Enhancement Proposals (PEPs) are a set of documents aiming at improving the Python programming language.
PEP 8 is a style guide for coding in Python.
It is not required.
It is recommended.
AoL 5 (H)
Mastering Functional Programming with Python, Steven Lott, 2015