Python 函数是可重复使用的代码片段,用于封装逻辑。文章详解函数定义语法、调用规则及先定义后使用的要求。阐述形参与实参的概念匹配,区分局部变量与全局变量的作用域,说明 global 关键字修改全局变量的方法。介绍函数返回值机制,支持多值返回。解析函数执行流程、栈帧结构及调试断点使用。涵盖链式调用、嵌套调用、递归原理(含阶乘示例)及递归优缺点。最后讲解参数默认值设置规范与关键字传参方式,帮助开发者掌握函数复用与模块化编程技巧。
CloudNative22 浏览
1 ~> 什么是函数?
1.1 函数的概念
我们编程中的函数其实和数学中的函数有一定的相似之处:
数学上的函数,比如 y=sinx,x 取不同的值,y 就会得到不同的结果
编程中的函数,指的是 一段可以被重复使用的代码片段。
1.2 代码示例
1.2.1 代码示例:求数列的和,不使用函数
# 1. 求 1 - 100 的和sum = 0for i inrange(1, 101):
sum += i
print(sum)
# 2. 求 300 - 400 的和sum = 0for i inrange(300, 401):
sum += i
print(sum)
# 3. 求 1 - 1000 的和sum = 0for i inrange(1, 1001):
sum += i
print(sum)
# 判定是否是奇数defisOdd(num):
if num % 2 == 0:
returnFalseelse:
returnTrue
result = isOdd(10)
print(result)
实际上也可以简化一下,写作——
print(isOdd(10))
7.1.2 概念
把一个函数的返回值,作为另一个函数的参数,这种操作称为 链式调用。
这是一种比较常见的写法。
7.1.3 最佳实践
# 函数的链式调用和嵌套调用 (1)# 链式调用defisOdd(num):
if num % 2 == 0:
returnFalsereturnTruedefadd(x, y):
return x + y
# result = isOdd(10)# print(result)print(isOdd(add(5, 5)))
# 局部变量和函数栈帧# 这几个变量虽然同名,但是是不同变量,属于不同的函数作用域# 每个变量也是保存在各自的栈帧中的 (每个栈帧也是保存在内存中)# 变量本质是一块内存空间defa():
num = 10print('函数 a')
defb():
num = 20
a()
print('函数 b')
defc():
num = 30
b()
print('函数 c')
defd():
num = 40
c()
print('函数 d')
d()
9 ~> 函数递归
9.1 函数递归的概念
递归是嵌套调用中的一种特殊情况:即 一个函数嵌套调用自己。
9.2 示例:递归计算 5!
deffactor(n):
if n == 1:
return1return n * factor(n - 1)
result = factor(5)
print(result)
上述代码中,就属于典型的递归操作。在 factor 函数内部,又调用了 factor 自身。
注意:递归代码务必要保证——
(1)存在递归结束条件:比如 if n == 1 就是结束条件,当 n 为 1 的时候,递归就结束了;
(2)每次递归的时候,要保证函数的实参是逐渐逼近结束条件的。
如果上述条件不能满足,就会出现'无限递归',这是一种典型的代码错误。
deffactor(n):
return n * factor(n - 1)
result = factor(5)
print(result)
正如前面所描述的那样,函数调用时会在函数调用栈中记录每一层函数调用的信息。
但是函数调用栈的空间不是无限大的,如果调用层数太多,就会超出栈的最大范围,导致出现问题。
9.3 最佳实践
# 函数递归# 写一个函数,来求 n 的阶乘 (n 是正整数)# 用循环的方式来写# def factor(n):# result = 1# for i in range(1, n + 1):# result *= i# return result# print(factor(5))# 用递归的方式来写# n! => n * (n - 1)# 1! => 1deffactor(n):
if n == 1:
return1return n * factor(n - 1)
print(factor(5))
9.4 递归的优缺点
9.4.1 递归的优点
(1)递归类似于'数学归纳法',明确初始条件,和递推公式,就可以解决一系列的问题;
(2)递归代码往往代码量非常少。
9.4.2 递归的缺点
(1)递归代码往往难以理解,很容易超出掌控范围;
(2)递归代码容易出现栈溢出的情况;
(3)递归代码往往可以转换成等价的循环代码,并且通常来说循环版本的代码执行效率要略高于递归版本。
9.4.3 基于递归的优缺点,使用递归的结论
在我们实际开发的时候,使用递归要慎重!
10 ~> 参数默认值
10.1 概念
Python 中的函数,可以给形参指定默认值。
带有默认值的参数,可以在调用的时候不传参。
10.2 最佳实践
10.2.1 代码示例:计算两个数字的和
defadd(x, y, debug=False):
if debug:
print(f'调试信息:x={x}, y={y}')
return x + y
print(add(10, 20))
print(add(10, 20, True))
defadd(x, debug=False, y):
if debug:
print(f'调试信息:x={x}, y={y}')
return x + y
print(add(10, 20))
带有默认值的参数如果没有放到没有默认值的参数的后面就会报错——
10.2.3 实践
# 函数形参的默认值# 通过这样的默认值,就可以让函数的设计更灵活!# debug:形参的默认值# 带有默认值的形参就可以在调用函数的时候,不必传参# 参数越多会提高使用者的成本defadd(x, y, debug=False):
if debug:
print(f'x = {x}, y = {y}')
return x + y
# result = add(10, 20) # 不开启调试信息的情况,只传了两个实参
result = add(10, 20, True)
# result = add(10, 20, False)print(result)
# 像默认值这样的语法,在编程界是存在争议的!# C++ 也支持形参默认参数,Java 就不支持,但是 Python 还是引入默认参数的# 带有默认值的形参得在形参列表的后面,而不能在前面 / 中间!# 多个带有默认值的形参,这些都得在形参列表的后面# 报错:SyntaxError: parameter without a default follows parameter with a default