Python Functions in Practice

by Pyrastra Team
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 digits in the string module represents a string composed of digits 0 to 9 '0123456789', and the ascii_letters in the string module represents a string composed of uppercase and lowercase English letters 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.

Note 2: Both the sample and choices functions in the random module can implement random sampling. sample implements sampling without replacement, which means the sampled elements are not repeated; choices implements sampling with replacement, which means some elements may be selected repeatedly. The first parameter of both functions represents the population for sampling, and the parameter k represents the sample size. It should be noted that the parameter k of the choices function 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_code function 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 name code_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 : int after the parameter num in the is_prime function 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 -> bool after 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, either True or False.

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 lcm function for calculating the least common multiple above calls the gcd function 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 the ddof parameter a value of 0, meaning no degrees of freedom correction is needed.

Note 3: The describe function assembles the statistical functions encapsulated above together to output descriptive statistical information of the data. In fact, Python’s standard library has a module called statistics that 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 the choose function, then pass the return value of the choose function as a parameter to the display function, 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 the choose and display functions. You only need to know that calling the choose function can generate a set of random numbers, and calling the display function 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.