'python_practice'

对于函数的理解

[TOC]

函数的定义

通过写一串代码块,来定义出自己想到的解决问题的方法,不要将函数都写到主函数中!

关于传参

在函数中参数分为形参和实参,一般的形参没有任何意义{()里面的参数名},实参是传进去的一个实际变量。

函数递归

函数的递归本质来说就是在自己定义的函数中继续调用自己,每调用一层就意味着要进入一层新的循环。而且递归函数都有其自己的结束条件,也就是在遇到阻碍自己的障碍物之前会一直进行递推。那么该阻碍递推的障碍物就是其结束条件。因此递归有两个要素:递归关系 和 结束条件。

特别的 递归的时候,每次调用一个函数,计算机都会为这个函数分配新的空间,这就是说,当被调函数返回的时候,调用函数中的变量依然会保持原先的值,否则也不可能实现反向输出。

斐波那契数列

因为斐波那契数列是第三个数等于前两个数之和,那么我们就有两种思路,一种就是根据正常的函数写法,一个就是递归思想。这里第一种方法只给出脚本,重点在于递归函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
def fib(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
yield a
'''
yield 关键字在python中是一种定义生成器的方式,将一个普通函数改造成生成函数。
简单理解来说yield就是return返回一个值,并且记住返回值的位置,下次直接从这个位置开始。
'''

for val in fib(20):
print(val)

递归函数:

根据斐波那契数列的定义f(n) = f(n-1)+f(n-2)。

这里的函数就是在自己定义的函数中不断进行调用自己,直到遇到自己的结束条件停止。就是工作原理如下图所示。

65f2ab6278d63d6da80a27eb4a033c59.png

然后思路就非常明确了,就是我们可以在函数中进行调用自己,通过函数递归就能够实现这行代码

1
2
3
4
def fib(n):
return 1 if n <= 2 else fib(n - 1) + fib(n - 2)
print(int(fib(5)))
#这串代码是通过函数递归得到第n位的斐波那契值

函数递归的特点

1
2
3
4
5
6
1. 每一级函数调用时都有自己的变量,但是函数代码并不会得到复制,如计算5的阶乘时每递推一次变量都不同;
2. 每次调用都会有一次返回,如计算5的阶乘时每递推一次都返回进行下一次;
3. 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序;
4. 递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反;
5. 递归函数中必须有终止语句。

总而言之:递归就是进行一次自我调用且拥有完成体。

对于函数递归来说因为是不断调用函数的内存,也就是说是一种栈的应用:

效率

1.系统栈(也叫核心栈、内核栈)
是内存中属于操作系统空间的一块区域,其主要用途为: (1)保存中断现场,对于嵌套中断,被中断程序的现场信息依次压入系统栈,中断返回时逆序弹出; (2)保存操作系统子程序间相互调用的参数、返回值、返回点以及子程序(函数)的局部变量。

2.用户栈
是用户进程空间中的一块区域,用于保存用户进程的子程序间相互调用的参数、返回值、返回点以及子程序(函数)的局部变量。
我们编写的递归程序属于用户程序,因此使用的是用户栈。

3.栈溢出
函数调用的参数是通过栈空间来传递的,在调用过程中会占用线程的栈资源。而递归调用,只有走到最后的结束点后函数才能依次退出,而未到达最后的结束点之前,占用的栈空间一直没有释放,如果递归调用次数过多,就可能导致占用的栈资源超过线程的最大值,从而导致栈溢出,导致程序的异常退出。

因此我们可以知道每次函数递归,对于函数本身来说需要保存的内容就包括(局部变量,形参,调用的函数地址,返回值),因此被调用n次函数就会产生n次函数值,那么运行效率也就不如直接写循环了。

循环能干的事,递归都能干;递归能干的循环不一定能干!!!


'python_practice'
http://example.com/2024/03/13/python-practice/
作者
John Doe
发布于
2024年3月13日
许可协议