目录
1.函数的作用域
def getPoint(): x=10 y=20 return x,y x,y=getPoint() print(x,y)
变量的作用域:一个变量名有效范围是一定的,只是在一个固定的范围内生效。
函数内部的变量名,只能在函数内部生效,出了函数就无效了。
函数内部和函数外部是可以使用同名的变量的,虽然变量名相同,但是是不同的变量。
x=10 def test(): print(f'x={x}') test()
在函数体里面找不到x就会在全局变量中查找
可以用global关键字声明全局变量x
#使用这个函数,把全局变量x改成20 def test(): x=20 test() print(f'x={x}')
#使用这个函数,把全局变量x改成20 def test(): global x x=20 test() print(f'x={x}')
没有global,此时就会把x=20,当作是函数内部创建了一个局部变量x
而实际上是要修改全局变量x,为了让函数知道里面的x是个全局变量,就使用global关键字声明一下。
if,else,while,for这些关键字也会引入代码块,但是这些代码块不会对变量的作用域产生影响,在上述语句代码块内部定义的变量,可以在外面被访问。
例子:
for i in range(1,11): print(i) print('--------------------------') print(i)
if True: x=10 print(x)
2.函数的执行过程
执行到函数调用的时候,就会跳转到函数体内来进行执行。
当函数内部执行完毕(运行完了/遇到return),就回到之前调用的位置,继续往下执行。
代码调试:调试执行,相比于正常的运行,最大的区别就是可以随时停下来,方便程序员观察程序的中间过程。
step into 就是单步运行,并且遇到咱们自己的函数,能进入到函数里面。
3.链式调用
用一个函数的返回值作为一个函数的参数。
链式调用:先执行()里面的函数,后执行外面的函数,调用一个函数,就需要先对他的参数求值。
def isOdd(num): if num%2==0: return False return True def add(x,y): return x*y #result =isOdd(10) #print(result) print(isOdd(add(5,5)))
4.嵌套调用(一个函数体内部调用其他函数)
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()
4.局部变量的函数栈帧
调试器的左下角,能够看到函数之间的调用栈
每一层的调用关系就称为“函数的栈帧”,每个函数的局部变量就在这个栈帧中体现。
每一层的栈帧,你选中之后,都能看到里面的局部变量,每个函数的局部变量都保存在对应的栈帧中。
调用函数,则生成对应的栈帧。函数结束,则对应的栈帧消亡(里面的局部变量也就没了)
def a(): num=10 print('函数 a') def b(): num=20 a() print('函数 b') def c(): num=30 b() print('函数 c') def d(): num=40 c() print('函数 d') d()
这几个num虽然同名,但不是一个变量。
每个变量也是保存在各自的栈帧中的,每个栈帧也是保存在内存上的。
变量本质就是一块内存空间。
5.函数的递归
函数递归:一个函数自己调用自己
写一个函数求n的阶乘:
#写一个函数,来求n的阶乘(n是正整数) #循环的方法 def factor(n): result =1 for i in range(1,n+1): result *=i return result #递归的方法 n! =>n*(n-1)! #1! =>1 def factor(n): if n==1: return 1 return n*factor(n-1) print(factor(5))
递归代码的两个要素:
1.递归的结束条件
2.递归的递推公式
递归的缺点:
1.执行过程非常复杂,难以理解。
2.递归代码容易出现“栈溢出”的情况。
代码不小心写错了,导致每次递归,参数不能正确的接近递归的结束条件,出现无限递归的情况。
3.递归代码一般都是可以转换成等价的循环代码的,并且,循环的版本通常运行速度要比递归版本有优势。(函数调用也是有开销的)
递归的优点:
代码非常简洁!(处理一些问题本身就是通过递归方式定义的)
6. 函数形参的默认值
在函数内部加上打印信息,方便我们进行调试
但是调试信息我们希望在正式发布的时候不要有,加上一个布尔类型debug
def add(x,y,debug): if debug: print(f'x={x},y={y}') return x+y #开启打印 result =add(10,20,True) print(result)
def add(x,y,debug=False): if debug: print(f'x={x},y={y}') return x+y #开启打印 result =add(10,20) print(result)
def add(x,y,debug=False): if debug: print(f'x={x},y={y}') return x+y #开启打印 result =add(10,20,True) print(result)
debug=False
形参的默认值,带有默认值的形参,就可以在调用函数的时候,不必传参。
要求带有默认值的形参,得在形参列表的后面,而不能在前面/中间。
7.函数的关键字参数
关键字传参:按照形参得名字来进行传参!
def test(x,y): print(f'x={x}') print(f'y={y}') test(x=10,y=20)
非常明显的告诉程序员,你的参数要传给谁!
另外可以无视形参和实参的顺序!
位置参数和关键字参数还能混着用,只不过混着用的时候要求位置参数在前,关键字参数在后。
一个函数,可以提供很多的参数,来实现对这个函数内部功能做出一些调整设定,为了降低调用者的使用成本,就可以把大部分参数设定出默认值。
当调用者需要调整其中一部分参数的时候,就可以搭配关键字参数来进行操作。
函数小结:
函数的定义
函数的调用
函数的参数传递流程
函数的执行流程