Python--函数(下)
目录
1.变量作用域
2.函数执行过程
2.1链式调用
2.2嵌套调用
2.3函数递归
3.参数默认值
4.关键字参数
1.变量作用域
观察以下代码:
def getPoint(): x = 10 y = 20 return x, y x, y = getPoint()在这个代码中,函数内部存在x, y,函数外部也有x, y.
但是这两组x, y不是相同的变量,而只是恰好有一样的名字.
变量只能在所在的函数内部生效.
在函数getPoint()内部定义的x, y只是在函数内部生效.一旦出了函数的范围,这两个变量就不再生效了.
def getPoint(): x = 10 y = 20 return x, y getPoint() print(x, y)在不同的作用域中,允许存在同名的变量
虽然名字相同,实际上是不同的变量.
x = 20 def test(): x = 10 print(f'函数内部 x = {x}') test() print(f'函数外部 x = {x}')注意:
在函数内部的变量,也称为"局部变量"
不在任何函数内部的变量,也称为"全局变量"
如果函数内部尝试访问的变量在局部不存在,就会尝试去全局作用域中查找
x = 20 def test(): global x x = 10 print(f'函数内部 x = {x}') test() print(f'函数外部 x = {x}')如果此处没有global,则函数内部的x = 10就会被视为是创建一个局部变量x,这样就和全局变
量x不相关了.
if / while / for等语句块不会影响到变量作用域
换而言之,在if / while / for中定义的变量,在语句外面也可以正常使用.
for i in range(1, 10): print(f'函数内部 i = {i}') print(f'函数外部 i = {i}')2.函数执行过程
调用函数才会执行函数体代码.不调用则不会执行.
函数体执行结束(或者遇到return语句),则回到函数调用位置,继续往下执行.
def test(): print("执行函数内部代码") print("执行函数内部代码") print("执行函数内部代码") print("1111") test() print("2222") test() print("3333")2.1链式调用
前面的代码很多都是写作
# 判定是否是奇数 def isOdd(num): if num % 2 == 0: return False else: return True result = isOdd(10) print(result)实际上也可以简化写作
print(isOdd(10))把一个函数的返回值,作为另一个函数的参数,这种操作称为链式调用.
这是一种比较常见的写法
2.2嵌套调用
函数内部还可以调用其他的函数,这个动作称为"嵌套调用" .
def test(): print("执行函数内部代码") print("执行函数内部代码") print("执行函数内部代码")test函数内部调用了print函数, 这里就属于嵌套调用
一个函数里面可以嵌套调用任意多个函数.
函数嵌套的过程是非常灵活的
def a(): print("函数 a") def b(): print("函数 b") a() def c(): print("函数 c") b() def d(): print("函数 d") c() d()如果把代码稍微调整,打印结果则可能发生很大变化.
def a(): print("函数 a") def b(): a() print("函数 b") def c(): b() print("函数 c") def d(): c() print("函数 d") d()函数之间的调用关系,在Python中会使用一个特定的数据结构来表示,称为函数调用栈.每次函数调用,
都会在调用栈里新增一个元素,称为栈帧.
可以通过PyCharm调试器看到函数调用栈和栈帧.
在调试状态下, PyCharm左下角一般就会显示出函数调用栈
每个函数的局部变量,都包含在自己的栈帧中
def a(): num1 = 10 print("函数 a") def b(): num2 = 20 a() print("函数 b") def c(): num3 = 30 b() print("函数 c") def d(): num4 = 40 c() print("函数 d") d()选择不同的栈帧,就可以看到各自栈帧中的局部变量
2.3函数递归
递归是 嵌套调用 中的一种特殊情况,即一个函数嵌套调用自己.
代码示例:递归计算5!
def factor(n): if n == 1: return 1 return n * factor(n - 1) result = factor(5) print(result)上述代码中,就属于典型的递归操作.在factor函数内部,又调用了factor自身.
注意:递归代码务必要保证存在递归结束条件.比如if n == 1就是结束条件.当n为1的时候,递归就结束了.每次递归的时候,要保证函数的实参是逐渐逼近结束条件的.如果上述条件不能满足,就会出现"无限递归" .这是一种典型的代码错误
def factor(n): return n * factor(n - 1) result = factor(5) print(result)递归的优点
递归类似于"数学归纳法" ,明确初始条件,和递推公式,就可以解决一系列的问题.
递归代码往往代码量非常少.
递归的缺点
递归代码往往难以理解,很容易超出掌控范围
递归代码容易出现栈溢出的情况
递归代码往往可以转换成等价的循环代码.并且通常来说循环版本的代码执行效率要略高于递归版
本.
实际开发的时候,使用递归要慎重!
3.参数默认值
Python中的函数,可以给形参指定默认值.
带有默认值的参数,可以在调用的时候不传参.
代码示例:计算两个数字的和
def add(x, y, debug=False): if debug: print(f'调试信息: x={x}, y={y}') return x + y print(add(10, 20)) print(add(10, 20, True))此处debug=False即为参数默认值.当我们不指定第三个参数的时候,默认debug的取值即为False.
带有默认值的参数需要放到没有默认值的参数的后面
def add(x, debug=False, y): if debug: print(f'调试信息: x={x}, y={y}') return x + y print(add(10, 20))4.关键字参数
在调用函数的时候,需要给函数指定实参.一般默认情况下是按照形参的顺序,来依次传递实参的.
但是我们也可以通过关键字参数,来调整这里的传参顺序,显式指定当前实参传递给哪个形参
def test(x, y): print(f'x = {x}') print(f'y = {y}') test(x=10, y=20) test(y=100, x=200)形如上述test(x=10, y=20)这样的操作,即为关键字参数
