一、什么是函数
Python 中的函数,可以理解成一段可以重复使用的代码。
例如下面这些代码虽然计算的范围不同,但是整体逻辑其实完全一样:
python
sum = 0
for i in range(1, 101):
sum += i
print(sum)
如果这种代码在程序里出现很多次,最直接的方法就是复制粘贴,但这种方式并不利于维护。假如以后计算逻辑发生变化,就可能需要修改很多份代码。
更合理的办法是把重复的部分提取出来:
python
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
return sum
之后直接调用:
python
print(calcSum(1, 100))
print(calcSum(300, 400))
print(calcSum(1, 1000))
这样重复代码就被消掉了。
二、函数的定义和调用
Python 使用 def 定义函数:
python
def 函数名(形参列表):
函数体
return 返回值
比如:
python
def hello():
print("hello")
注意一个很重要的地方:
定义函数并不会执行函数内部的代码。
只有真正调用函数时,函数体才会执行。
python
def hello():
print("hello")
hello()
调用一次,执行一次;调用两次,就执行两次。
而且函数必须先定义,再使用。如果在定义之前就调用,程序会出问题。
三、函数参数
函数可以通过参数接收数据。
定义函数时写的是形参 ,调用函数时传进去的是实参。
例如:
python
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
return sum
这里:
text
beg、end
就是形参。
而:
python
calcSum(1, 100)
里面的:
text
1、100
就是实参。
调用:
python
calcSum(1, 100)
可以理解成函数内部接收到:
text
beg = 1
end = 100
再进行计算。
一个函数可以有多个参数,也可以没有参数。
例如:
python
def test():
print("hello")
也可以:
python
def test(a, b, c):
print(a, b, c)
调用时,参数数量必须匹配。
四、Python函数参数不需要写类型
Python 是动态类型语言,所以定义函数的时候,不需要像 C++ 那样写参数类型。
比如:
python
def test(a):
print(a)
这个函数可以传:
python
test(10)
test('hello')
test(True)
不需要额外指定:
text
int
string
bool
等类型。
五、函数返回值
函数参数可以理解成函数的输入,而 return 返回的结果可以理解成函数的输出。
比如:
python
def calcSum(beg, end):
sum = 0
for i in range(beg, end + 1):
sum += i
return sum
然后:
python
result = calcSum(1, 100)
print(result)
这里函数只负责计算,至于结果怎么处理,由调用者决定。
我觉得这种写法非常重要。
函数里面最好主要负责自己的逻辑,不要把计算和用户交互全部混在一起。
比如不太推荐:
python
def calcSum(beg, end):
...
print(sum)
更推荐:
python
def calcSum(beg, end):
...
return sum
这样以后结果是保存到文件,还是打印到控制台,甚至用于其他计算,都可以由外部决定。
六、return会立即结束函数
一个函数可以有多个 return。
例如判断奇数:
python
def isOdd(num):
if num % 2 == 0:
return False
return True
如果 num 是偶数,执行:
python
return False
函数就直接结束,不会继续往后执行。
所以看到 return 的时候,可以直接想到:
text
结束当前函数
返回结果
回到调用的位置
七、函数可以返回多个值
Python 支持:
python
def getPoint():
x = 10
y = 20
return x, y
调用:
python
a, b = getPoint()
这里实际上可以一次得到两个结果。
如果某个返回值暂时不需要,可以使用 _ 忽略:
python
_, b = getPoint()
八、变量作用域
函数里面的变量,并不一定和外面的同名变量是同一个。
例如:
python
x = 20
def test():
x = 10
print(x)
test()
print(x)
输出时,函数内部和函数外部的 x 是两个不同的变量。
函数内部的变量叫做局部变量 ,函数外部的变量叫做全局变量。
如果函数内部没有找到对应的局部变量,就会继续到全局作用域中寻找。
如果确实需要在函数内部修改全局变量,可以使用:
python
global x
例如:
python
x = 20
def test():
global x
x = 10
test()
print(x)
还有一个容易和 C++ 思维混淆的地方:
if、while、for 这些代码块不会产生新的变量作用域。
也就是说,在循环或者条件语句中创建的变量,出了这些代码块之后仍然可以使用。
九、函数调用过程
调用函数时,程序会进入函数内部执行。
函数执行结束,或者遇到 return,就会回到函数调用的位置继续执行。
例如:
python
def test():
print("函数内部代码")
print("1111")
test()
print("2222")
test()
print("3333")
执行顺序就是:
text
1111
函数内部代码
2222
函数内部代码
3333
调试的时候,还可以在 PyCharm 中下断点,通过 Debug 和 Step Into 一步一步观察函数的执行过程。
十、链式调用和嵌套调用
如果把一个函数的返回值直接作为另一个函数的参数,这种操作叫做链式调用。
例如:
python
print(isOdd(10))
就比:
python
result = isOdd(10)
print(result)
更简洁。
函数内部还可以调用其他函数,这叫做嵌套调用。
比如:
python
def a():
print("函数 a")
def b():
a()
print("函数 b")
b()
执行 b() 的时候,会先进入 b,然后调用 a,a 执行完以后再回到 b。
十一、函数调用栈
函数之间互相调用的时候,Python 会使用一个叫做函数调用栈的数据结构来记录当前调用关系。
每调用一次函数,调用栈中就会增加一个栈帧。
每个函数自己的局部变量,都保存在对应的栈帧中。
十二、递归
递归其实就是一种特殊的嵌套调用:
函数调用自己。
例如计算阶乘:
python
def factor(n):
if n == 1:
return 1
return n * factor(n - 1)
调用:
python
print(factor(5))
执行过程大致是:
text
factor(5)
↓
5 * factor(4)
↓
4 * factor(3)
↓
3 * factor(2)
↓
2 * factor(1)
到了:
python
n == 1
满足结束条件,开始一层层返回。
递归最重要的一点就是:
必须有结束条件,而且每次递归都要逐渐接近这个条件。
否则就会出现无限递归。
递归的优点是代码往往比较短,而且很像数学中的递推过程。
但是它也有缺点:
text
代码不容易理解
容易出现无限递归
可能导致栈溢出
有些问题改成循环后效率更高
所以实际写代码时,递归不能无脑使用。
十三、参数默认值
Python 可以给函数参数设置默认值。
例如:
python
def add(x, y, debug=False):
if debug:
print(f'调试信息: x={x}, y={y}')
return x + y
这样:
python
add(10, 20)
不传第三个参数时:
text
debug = False
也可以:
python
add(10, 20, True)
需要注意:
有默认值的参数要放在没有默认值的参数后面。
十四、关键字参数
正常情况下,传参是按照参数顺序来的:
python
def test(x, y):
print(x, y)
test(10, 20)
也可以直接指定参数名称:
python
test(x=10, y=20)
test(y=100, x=200)
这种方式叫做关键字参数。
十五、总结
函数是 Python 中非常核心的一部分。
现在最需要掌握的就是:
text
函数定义
函数调用
函数参数
返回值
变量作用域
递归
默认参数
关键字参数
以后写稍微复杂一点的程序,基本不可能一直把所有代码堆在一起。
把重复逻辑封装成函数,不仅代码更短,也更方便后续修改和复用。