Python函数


Python中函数的用法非常多,80%的用法不常用,20%的用法常用。大家不要把精力浪费在背完所有用法上,而要把主要精力放到最常用的20%的用法和代码逻辑上,至于另外80%不常用的用法,边用边查就行。

1. 函数基础

Python中一个典型的函数定义包括以下部分:关键字def、函数名称、由0个或多个形参组成的列表以及函数体。

1.1 编写函数

我们来编写一个求阶乘的函数。例如:

1
2
3
4
5
def fact(n):
res = 1
for i in range(1, n + 1):
res *= i
return res

函数名称是fact,给它传入一个n,会返回n的阶乘。return语句负责结束函数并返回res的值。

1.2 调用函数

1
2
3
print("我们要计算5的阶乘,答案是:")
print(fact(5)) # 输出 120
print("计算结束啦!")

函数的调用完成两项工作:

  • 用实参初始化函数对应的形参
  • 将控制权转移给被调用的函数

此时,代码原本的执行顺序被暂时中断,被调函数开始执行。等被调用函数执行完后,再继续执行之前的代码。

1.3 形参和实参

实参指调用函数时传入的变量或常量,形参指定义函数时参数列表里的变量。

形参列表可以为空,例如:

1
2
3
4
def f():
print("Hello World")

f() # 输出 Hello World
1.3.1 形参的初始化方式

调用函数时会用实参去初始化形参,初始化的顺序有两种:

第一种是用位置实参来初始化形参。顾名思义,实参会按位置关系来初始化形参,第一个实参初始化第一个形参,第二个实参初始化第二个形参,依此类推。形参和实参的个数必须匹配。例如:

1
2
3
4
5
6
7
8
9
def f(a, b, c, d):
print("a =", a, end=", ")
print("b =", b, end=", ")
print("c =", c, end=", ")
print("d =", d)

f(1, True, "Python", 4.2) # 输出 a = 1, b = True, c = Python, d = 4.2
f(1, True, "Python", 4.2, 3) # 会报错,因为实参个数多于形参
f(1, True, "Python") # 会报错,因为实参个数少于形参

第二种是用关键字实参来初始化形参。此时实参不再按位置关系来初始化形参,而是按变量名初始化。例如:

1
2
# f()的定义如上所述
f(b=1, c=True, a="Python", d=4.2) # 输出 a = Python, b = 1, c = True, d = 4.2

两种方式也可以混合使用,但是位置实参一定要放到关键字实参之前。例如:

1
2
3
# f()的定义如上所述
f(1, 2, d="Python", c=4.2) # 输出 a = 1, b = 2, c = 4.2, d = Python
f(1, b=3, "Python", d=4.2) # 会报错,因为位置实参位于关键字实参后面了。
1.3.2 带默认值的形参

形参也可以设置默认值,但所有带默认值的形参必须是最后几个。当某些形参没有被初始化时,这些形参会使用默认值。例如:

1
2
3
4
5
6
7
8
9
def f(a, b, c=3, d="Python"):
print("a =", a, end=", ")
print("b =", b, end=", ")
print("c =", c, end=", ")
print("d =", d)


f(1, 2) # c和d没有被初始化,采用默认值。输出 a = 1, b = 2, c = 3, d = Python
f(1, b=2, d="AcWing") # c没有被初始化,采用默认值。输出 a = 1, b = 2, c = 3, d = AcWing
1.3.3 其它参数写法

其它参数写法用得不多,想了解的同学可以参考函数定义详解

1.4 变量的作用域

函数内定义的变量为局部变量,只能在函数内部使用。

当需要修改用全局变量时,需要用global关键字在函数内声明全局变量。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
x = 1


def f():
global x # 在函数内声明全局变量
x = 666
y = 777
print(x, y)


f() # 输出 666 777
print(x) # 会发现全局变量x也被修改了
print(y) # 会报错,因为y是局部变量,函数外无法使用

1.5 嵌套定义函数
函数内部也可以定义函数。例如:

1
2
3
4
5
6
7
8
def f():
def g(x): # 定义函数g()
x += 1
print(x)
g(5) # 调用函数g()


f() # 输出6

1.6 pass语句
当函数定义完但还不想实现时,可以用pass占位符,来避免出现语法错误。例如:

1
2
def f():
pass

2. 参数传递

2.1 值传递

intfloatbool、字符串等采用值传递。

将实参的初始值拷贝给形参。此时,对形参的改动不会影响实参的初始值。例如:

1
2
3
4
5
6
7
8
def f(y):
y = 5
print(y)


x = 10
f(x)
print(x) # 会发现x的值没变

2.2 引用传递

列表采用引用传递。

将实参的引用传给形参,此时对形参的修改会影响实参的初始值。例如:

1
2
3
4
5
6
7
8
def f(b):
for i in range(len(b)):
b[i] += 1


a = [0, 1, 2, 3, 4]
f(a)
print(a) # 会发现列表a中的每个数加了1

3.return语句

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方,并返回结果。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def f(x):
if x == 1:
return # 不写返回值时,会返回None
if x == 2:
return 3 # 返回一个变量
if x == 3:
return 4, 5 # 返回多个变量


a = f(1)
b = f(2)
c, d = f(3)
e = f(4) # 没写return时,也会返回None
print(a, b, c, d, e) # 输出 None 3 4 5 None

4.lambda表达式

lambda关键字可以创建匿名函数,目的是为了简化代码。可以对比下面两种写法,会发现lambda表达式的写法更短一些。

常与sort()函数配合使用,例如:

1
2
3
4
pairs = [[1, "one"], [2, "two"], [3, "three"], [4, "four"]]

pairs.sort(key=lambda pair: pair[1]) # 每个元素使用第二个变量比较大小
print(pairs) # 输出:[[4, 'four'], [1, 'one'], [3, 'three'], [2, 'two']]

等价于下面的写法:

1
2
3
4
5
6
7
8
9
pairs = [[1, "one"], [2, "two"], [3, "three"], [4, "four"]]


def compare(pair):
return pair[1]


pairs.sort(key=compare) # 每个元素使用第二个变量比较大小
print(pairs) # 输出:[[4, 'four'], [1, 'one'], [3, 'three'], [2, 'two']]

5. 函数递归

在一个函数内部,也可以调用函数自身。这种写法被称为递归。

写递归函数可以从集合的角度来思考。理解递归函数的执行顺序可以用树的形式来思考。

例如,求解斐波那契数列第 nn 项可以采用如下写法:

1
2
3
4
5
6
7
def fib(n):
if n <= 2:
return 1
return fib(n - 1) + fib(n - 2)


print(fib(6)) # 输出 8