Python Functions in Practice
Example 1: Random Verification Code
Design a function that generates random verification codes. The verification code consists of digits and uppercase and lowercase English letters, and the length can be set through parameters.
import random
import string
ALL_CHARS = string.digits + string.ascii_letters
def generate_code(*, code_len=4):
"""
Generate verification code of specified length
:param code_len: Length of verification code (default 4 characters)
:return: Random verification code string composed of uppercase and lowercase English letters and digits
"""
return ''.join(random.choices(ALL_CHARS, k=code_len))
Note 1: The
digitsin thestringmodule represents a string composed of digits 0 to 9'0123456789', and theascii_lettersin thestringmodule represents a string composed of uppercase and lowercase English letters'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.Note 2: Both the
sampleandchoicesfunctions in therandommodule can implement random sampling.sampleimplements sampling without replacement, which means the sampled elements are not repeated;choicesimplements sampling with replacement, which means some elements may be selected repeatedly. The first parameter of both functions represents the population for sampling, and the parameterkrepresents the sample size. It should be noted that the parameterkof thechoicesfunction is a keyword-only argument, and the parameter name must be specified when passing arguments.
You can use the following code to generate 5 sets of random verification codes to test the above function.
for _ in range(5):
print(generate_code())
Output:
59tZ
QKU5
izq8
IBBb
jIfX
Or:
for _ in range(5):
print(generate_code(code_len=6))
Output:
FxJucw
HS4H9G
0yyXfz
x7fohf
ReO22w
Note: The parameter of the
generate_codefunction we designed is a keyword-only argument. Since it has a default value, you can call it without passing a value, using the default value 4. If you need to pass a parameter to the function, you must specify the parameter namecode_len.
Example 2: Prime Number Checking
Design a function to determine whether a given positive integer greater than 1 is a prime number. A prime number is a positive integer (greater than 1) that can only be divided by 1 and itself. If a positive integer $\small{N}$ greater than 1 is a prime number, it means there are no factors of it between 2 and $\small{N-1}$.
def is_prime(num: int) -> bool:
"""
Determine if a positive integer is a prime number
:param num: Positive integer greater than 1
:return: Returns True if num is prime, otherwise returns False
"""
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
return False
return True
Note 1: The
: intafter the parameternumin theis_primefunction above is used to annotate the parameter type. Although it has no effect on the code execution result, it greatly enhances code readability. Similarly, the-> boolafter the parameter list is used to annotate the function return value type. It also doesn’t affect code execution results, but it clearly tells us that calling the function will get a boolean value, eitherTrueorFalse.Note 2: The loop above doesn’t need to go from 2 to $\small{N-1}$, because if the loop reaches $\small{\sqrt{N}}$ and no factor of $\small{N}$ has been found, then no factor of $\small{N}$ will appear after $\small{\sqrt{N}}$. You can think about why this is.
Example 3: Greatest Common Divisor and Least Common Multiple
Design functions to calculate the greatest common divisor and least common multiple of two positive integers. The greatest common divisor of $\small{x}$ and $\small{y}$ is the largest integer that can divide both $\small{x}$ and $\small{y}$. If $\small{x}$ and $\small{y}$ are coprime, their greatest common divisor is 1; the least common multiple of $\small{x}$ and $\small{y}$ is the smallest positive integer that can be divided by both $\small{x}$ and $\small{y}$. If $\small{x}$ and $\small{y}$ are coprime, their least common multiple is $\small{x \times y}$. It should be noted that calculating the greatest common divisor and least common multiple are two different functions, and should be designed as two functions rather than putting both functions into one function.
def lcm(x: int, y: int) -> int:
"""Calculate least common multiple"""
return x * y // gcd(x, y)
def gcd(x: int, y: int) -> int:
"""Calculate greatest common divisor"""
while y % x != 0:
x, y = y % x, x
return x
Note: Functions can call each other. The
lcmfunction for calculating the least common multiple above calls thegcdfunction for calculating the greatest common divisor, calculating the least common multiple through $\frac{x \times y}{ gcd(x, y)}$.
Example 4: Data Statistics
Assuming sample data is stored in a list, design functions to calculate descriptive statistical information of the sample data. Descriptive statistical information usually includes: arithmetic mean, median, range (difference between maximum and minimum values), variance, standard deviation, coefficient of variation, etc. The calculation formulas are as follows.
Sample mean:
$$ \bar{x} = \frac{\sum_{i=1}^{n}x_{i}}{n} = \frac{x_{1}+x_{2}+\cdots +x_{n}}{n} $$
Sample variance:
$$ s^2 = \frac {\sum_{i=1}^{n}(x_i - \bar{x})^2} {n-1} $$
Sample standard deviation:
$$ s = \sqrt{\frac{\sum_{i=1}^{n}(x_i - \bar{x})^2}{n-1}} $$
Coefficient of sample variation:
$$ CV = \frac{s}{\bar{x}} $$
def ptp(data):
"""Range (peak to peak)"""
return max(data) - min(data)
def mean(data):
"""Arithmetic mean"""
return sum(data) / len(data)
def median(data):
"""Median"""
temp, size = sorted(data), len(data)
if size % 2 != 0:
return temp[size // 2]
else:
return mean(temp[size // 2 - 1:size // 2 + 1])
def var(data, ddof=1):
"""Variance"""
x_bar = mean(data)
temp = [(num - x_bar) ** 2 for num in data]
return sum(temp) / (len(temp) - ddof)
def std(data, ddof=1):
"""Standard deviation"""
return var(data, ddof) ** 0.5
def cv(data, ddof=1):
"""Coefficient of variation"""
return std(data, ddof) / mean(data)
def describe(data):
"""Output descriptive statistical information"""
print(f'Mean: {mean(data)}')
print(f'Median: {median(data)}')
print(f'Range: {ptp(data)}')
print(f'Variance: {var(data)}')
print(f'Standard deviation: {std(data)}')
print(f'Coefficient of variation: {cv(data)}')
Note 1: The median is the middle number when data is arranged in ascending or descending order, describing the medium level of the data. The calculation of the median is divided into two cases: when the data size $n$ is odd, the median is the element at position $\frac{n + 1}{2}$; when the data size $\small{n}$ is even, the median is the mean of the elements at positions $\frac{n}{2}$ and $\frac{n}{2} + 1$.
Note 2: The functions for calculating variance and standard deviation have a parameter called
ddof, which represents the adjustable degrees of freedom, with a default value of 1. When calculating sample variance and sample standard deviation, degrees of freedom correction is needed; if you want to calculate population variance and population standard deviation, you can assign theddofparameter a value of 0, meaning no degrees of freedom correction is needed.Note 3: The
describefunction assembles the statistical functions encapsulated above together to output descriptive statistical information of the data. In fact, Python’s standard library has a module calledstatisticsthat has already encapsulated functions for obtaining descriptive statistical information. Interested readers can learn about it on their own.
Example 5: Double Color Ball Random Number Selection
We’ll refactor the double color ball random number selection example we talked about before (Lesson 09: Common Data Structures - Lists-2) using functions, encapsulating the functionality of generating random numbers and outputting a set of numbers into two functions respectively, and then implementing the function of randomly selecting N sets of numbers by calling the functions.
"""
Double color ball random number selection program
Author: Luo Hao
Version: 1.3
"""
import random
RED_BALLS = [i for i in range(1, 34)]
BLUE_BALLS = [i for i in range(1, 17)]
def choose():
"""
Generate a set of random numbers
:return: List storing random numbers
"""
selected_balls = random.sample(RED_BALLS, 6)
selected_balls.sort()
selected_balls.append(random.choice(BLUE_BALLS))
return selected_balls
def display(balls):
"""
Format output of a set of numbers
:param balls: List storing random numbers
"""
for ball in balls[:-1]:
print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
print(f'\033[034m{balls[-1]:0>2d}\033[0m')
n = int(input('Generate how many sets of numbers: '))
for _ in range(n):
display(choose())
Note: Look at the line of code
display(choose()). Here we first get a set of random numbers through thechoosefunction, then pass the return value of thechoosefunction as a parameter to thedisplayfunction, which displays the selected random numbers. The logic of the refactored code is very clear, and the code readability is stronger. If someone encapsulates these two functions for you and you’re just the function caller, you actually don’t need to care about the internal implementation of thechooseanddisplayfunctions. You only need to know that calling thechoosefunction can generate a set of random numbers, and calling thedisplayfunction with a list can output this set of numbers. In the future, when we use various Python third-party libraries, we also don’t care about their underlying implementation. What we need to know is just which function to call to solve the problem.
Summary
When writing code, especially when developing commercial projects, you must be conscious of encapsulating relatively independent and reusable functionality into functions. This way, whether it’s yourself or other team members, you can use these functions by calling them, reducing repetitive and tedious work.