Before we start this lesson, let us first study a math problem: how many groups of positive integer solutions does the equation below have?
You may already have thought of it: this problem is actually the same as dividing 8 apples into 4 groups, with at least one apple in each group. It is also the same as putting 3 dividers into the 7 gaps between 8 apples to split them into 4 groups. So the answer is
Based on what we learned before, we can use loops and repeated multiplication to calculate
"""
Read m and n, then compute C(m, n)
Version: 1.0
Author: Luo Hao
"""
m = int(input('m = '))
n = int(input('n = '))
# Calculate m!
fm = 1
for num in range(1, m + 1):
fm *= num
# Calculate n!
fn = 1
for num in range(1, n + 1):
fn *= num
# Calculate (m - n)!
fk = 1
for num in range(1, m - n + 1):
fk *= num
# Calculate C(m, n)
print(fm // fn // fk)I do not know whether you noticed it, but in the code above we did the factorial operation three times. Although the values of
In mathematics, functions are usually written in forms like
In Python, we can use the def keyword to define a function. Like variables, every function should also have a good name, and the naming rules are the same as the naming rules for variables. In the parentheses after the function name, we can set the function parameters, which are the independent variables we just talked about. After the function finishes running, we use the return keyword to return the result of the function, which is the dependent variable we just talked about. If there is no return statement in a function, the function returns None, which means an empty value. Also, a function can have no independent variables, which means no parameters, but the parentheses after the function name are still required. The things a function needs to do are placed after the function definition line by indentation, just like the code blocks in branching and loop structures, as shown below.
Now let us put the factorial operation from the earlier code into a function. In this way, we refactor the previous code. Refactoring means adjusting the structure of the code without changing the execution result. The refactored code is shown below.
"""
Read m and n, then compute C(m, n)
Version: 1.1
Author: Luo Hao
"""
def fac(num):
"""Return the factorial of a non-negative integer."""
result = 1
for n in range(2, num + 1):
result *= n
return result
m = int(input('m = '))
n = int(input('n = '))
# When calculating factorials, we do not need to write repeated code.
# We only need to call the function directly.
# The syntax for calling a function is to put parentheses after the function name and pass in parameters.
print(fac(m) // fac(n) // fac(m - n))You can feel that the code above is simpler and more elegant than the earlier version. More importantly, the factorial function fac that we defined can also be used again in other code that needs factorials. So, using functions can help us package code that is relatively independent in function and will be used repeatedly. When we need this code, instead of writing duplicate code again, we reuse existing code by calling the function.
In fact, the math module in Python's standard library already has a function named factorial that calculates factorials. We can directly import the math module with import math, and then use math.factorial to call the factorial function. We can also directly import the factorial function with from math import factorial, as shown below.
"""
Read m and n, then compute C(m, n)
Version: 1.2
Author: Luo Hao
"""
from math import factorial
m = int(input('m = '))
n = int(input('n = '))
print(factorial(m) // factorial(n) // factorial(m - n))In the future, the functions we use are either functions we define ourselves, or functions provided by Python's standard library or third-party libraries. If there is already a ready-to-use function, there is no need for us to define it again. "Reinventing the wheel" is a very bad thing. For the code above, if you think the name factorial is too long and not very convenient when writing code, we can also use the as keyword to give it an alias when importing the function. When calling the function, we can use the alias instead of the original name.
"""
Read m and n, then compute C(m, n)
Version: 1.3
Author: Luo Hao
"""
from math import factorial as f
m = int(input('m = '))
n = int(input('n = '))
print(f(m) // f(n) // f(m - n))Let us write another function. According to the lengths of three sides, it checks whether they can form a triangle. If they can form a triangle, it returns True; otherwise, it returns False, as shown below.
def make_judgement(a, b, c):
"""Return whether three sides can form a triangle."""
return a + b > c and b + c > a and a + c > bThe three parameters in the make_judgement function above are called positional parameters. When calling the function, we usually pass them from left to right, and the number of arguments passed in must be the same as the number of parameters when the function is defined, as shown below.
print(make_judgement(1, 2, 3)) # False
print(make_judgement(4, 5, 6)) # TrueIf we do not want to give the values of a, b, and c from left to right, we can also use keyword arguments, and pass the parameters by writing parameter_name=parameter_value, as shown below.
print(make_judgement(b=2, c=3, a=1)) # False
print(make_judgement(c=6, b=4, a=5)) # TrueWhen defining a function, we can use / in the parameter list to set positional-only parameters, and use * to set keyword-only parameters. So-called positional-only parameters are parameters that can only receive values by parameter position when calling the function. Keyword-only parameters can only be passed and received by writing parameter_name=parameter_value. Look at the example below.
def make_judgement(a, b, c, /):
return a + b > c and b + c > a and a + c > bNote: Positional-only parameters are a new feature introduced in Python 3.8. If you use a lower Python version, you need to pay attention to this.
def make_judgement(*, a, b, c):
return a + b > c and b + c > a and a + c > bPython allows function parameters to have default values. We can package the operation of rolling dice in the CRAPS game example we talked about before into a function, as shown below.
from random import randrange
def roll_dice(n=2):
"""Roll n dice and return the total."""
total = 0
for _ in range(n):
total += randrange(1, 7)
return total
print(roll_dice())
print(roll_dice(3))Let us look at a simpler example.
def add(a=0, b=0, c=0):
"""Add three numbers."""
return a + b + c
print(add()) # 0
print(add(1)) # 1
print(add(1, 2)) # 3
print(add(1, 2, 3)) # 6It should be noted that parameters with default values must be placed after parameters without default values. Otherwise, it will raise SyntaxError. The error message is non-default argument follows default argument, which means a parameter without a default value was placed after a parameter with a default value.
In Python, we can use star-expression syntax to make a function support variable-length arguments. Variable-length arguments mean that when calling a function, we can pass in 0 or any number of arguments. In the future, when we develop business projects in teams, we may design functions for other people to use, but sometimes we do not know how many arguments the caller will pass to the function. At that time, variable-length arguments are useful.
def add(*args):
total = 0
for val in args:
if type(val) in (int, float):
total += val
return total
print(add()) # 0
print(add(1)) # 1
print(add(1, 2, 3)) # 6
print(add(1, 2, 'hello', 3.45, 6)) # 12.45If we want to pass some parameters in the form of parameter_name=parameter_value, and we are also not sure how many of these parameters there will be, we can also add variable keyword arguments to the function and group the passed keyword arguments into a dictionary, as shown below.
def foo(*args, **kwargs):
print(args)
print(kwargs)
foo(3, 2.1, True, name='Luo Hao', age=43, gpa=4.95)Output:
(3, 2.1, True)
{'name': 'Luo Hao', 'age': 43, 'gpa': 4.95}No matter what programming language we use to write code, naming variables and functions can lead to the awkward situation of name conflicts. The simplest scene is defining two functions with the same name in one .py file:
def foo():
print('hello, world!')
def foo():
print('goodbye, world!')
foo()Of course, the situation above is easy to avoid, but in a team project with many programmers, several programmers may all define a function named foo. In this case, how do we solve the name conflict? The answer is actually very simple. In Python, each file represents one module. We can have functions with the same name in different modules. When using the function, we import the specified module with the import keyword and then use the fully qualified name module_name.function_name, and then we can tell which module's foo function we want to use.
module1.py
def foo():
print('hello, world!')module2.py
def foo():
print('goodbye, world!')test.py
import module1
import module2
module1.foo()
module2.foo()When importing a module, we can also use the as keyword to give the module an alias. In this way, we can use a shorter fully qualified name.
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()In the two code sections above, what we imported were the modules that define the functions. We can also directly import the functions we need from the module by using from...import....
from module1 import foo
foo()
from module2 import foo
foo()But if we directly import two functions with the same name from two different modules, the later import will replace the earlier import. If we want to use both foo functions at the same time in the code above, there is still a way. We can use the as keyword to give aliases to the imported functions.
from module1 import foo as f1
from module2 import foo as f2
f1()
f2()Python's standard library contains many useful modules and functions. We have already seen:
randomfor random numbers and samplingtimefor time-related operationsmathfor mathematical functions such as sine, cosine, exponentials, and logarithms
Python also has many built-in functions that require no import. Examples include:
| Function | Description |
|---|---|
abs |
absolute value |
bin |
convert an integer to binary |
chr |
convert a Unicode code point to a character |
hex |
convert an integer to hexadecimal |
input |
read one line of input |
len |
get the length of a string, list, etc. |
max |
return the maximum value |
min |
return the minimum value |
oct |
convert an integer to octal |
open |
open a file |
ord |
convert a character to its Unicode code point |
pow |
exponentiation |
print |
print output |
range |
construct a range sequence |
round |
round a number |
sum |
sum values in a sequence |
type |
return the type of an object |
A function is a package of code that is relatively independent in function and will be used repeatedly. After learning how to define and use functions, we can write better code. Of course, Python's standard library has already provided many modules and common functions for us. If we use these modules and functions well, we can do more things with less code. If these modules and functions still cannot meet our needs, then we may need to define our own functions, and then use modules to manage these custom functions.
