前言
这是 “Python 基础”系列的第 03 篇文章 ,共 20 篇 。 01 Python 基础:Python入门必看之语法基础 02 Python 基础:列表及字典内置函数&方法内容梳理 03 Python 基础:Python函数及递归函数知识点梳理 04 Python 基础:讲解迭代、过滤、匿名函数、排序算法四大知识点 05 Python 基础:高阶函数学习实践 06 Python 基础:难点装饰器的学习介绍及实现赌博收益小案例 07 Python 基础:重点知识点函数的参数难点解答 08 Python 基础:面试问你类与实例及其属性还不会吗 09 Python 基础:手把手带你梳理对象、继承与多态知识点 10 Python 基础:如何定制类,这里有答案 11 Python 基础:知识巩固,实现一个简易学生管理系统 12 Python 基础:如何优化代码质量,错误、调试和测试你必须要懂 13 Python 基础:模块的概念及使用方法并着重介绍两个常用模块 14 Python 基础:重点知识点--IO编程 15 Python 基础:程序猿必懂知识之正则表达式 16 Python 基础:重点知识点--Pygame的基础知识梳理 17 Python 基础:重点知识点--Pygame实现儿时经典游戏坦克大战 18 Python 基础:重点知识点--进程和线程概念、应用知识梳理 19 Python 基础:重点知识点--网络通信基础知识讲解 20 Python 基础:重点知识点--网络通信进阶知识讲解
3 Python 基础: Python函数及递归函数知识点梳理,共有 2 部分:
Python函数
函数的英文是function,所以,通俗地来讲,函数就是功能的意思。函数是用来封装特定功能的,比如,在Python里面,len()是一个函数,len()这个函数实现的功能是返回一个字符串的长度,所以说len()这个函数他的特定功能就是返回长度,再比如,我们可以自己定义一个函数,然后编写这个函数的功能,之后要使用的时候再调用这个函数。所以函数分为两种类型,一种是系统自带的不用我们编写其功能系统自己就有的,比如len()这种函数,另一种函数是我们自定义的,需要我们编写其功能的,这种函数自由度高,叫做自定义函数,需要使用的时候直接调用该函数。
Python里函数的定义
在Python中要想使用自定义函数,就得首先定义一个函数,定义一个函数包括两个部分的含义,第一个含义是申明这个指定的部分是函数,而不是其他的对象,第二个含义是要定义这个函数所包含的功能,也就是要编写这个函数的功能。
def 函数名():
函数内容;函数内容
函数内容;函数内容
### 1+2+3+5+6+...+n
#1+2+3...+10
allNum = 0
for i in range(1,11):
allNum = allNum + i;
print(allNum)
#1+2+3...+100
allNum = 0
for i in range(1,101):
allNum = allNum + i;
print(allNum)
#写一个可以调用的函数,只要传入一个参数N,就可以返回1+2+3+5+6+...+n的结果
def addNum(n):
allNum = 0
for i in range(1,n+1):
allNum = allNum + i;
print(allNum)
return allNum
#return None
addNum(200)
addNum(100)
bb = addNum(10)
print(bb)
#def 函数名(函数的参数):
#缩进一个TAB按键的代码块
#结果为
55
5050
20100
5050
55
55
形参与实参
参数的传递
在Python中函数在调用的过程中参数的传递使用顺序的。
关键字参数
关键字参数有两大好处。首先,它们清晰地指出了参数值,有助于提高程序的可读性;其次,关键字参数的顺序无关紧要。对于包含大量参数的函数来说,这两点都很有帮助,因为很难记住这些函数的参数的顺序和含义。
3、全局变量与局部变量
什么是作用域
Python中一个变量的是在一定的范围内起作用的,在其起作用的这个范围我们称之为作用域。
全局变量与局部变量两者的本质区别就是在于作用域
用通俗的话来理解的话,
全局变量是在整个py文件中声明,全局范围内都可以访问
局部变量是在某个函数中声明的,只能在该函数中调用它,如果试图在超出范围的地方调用,程序就爆掉了
如果在函数内部定义与某个全局变量一样名称的局部变量,就可能会导致意外的效果,可能不是你期望的。因此不建议这样使用,这样会使得程序很不健全
直接来看几个例子来理解全局变量和局部变量的区别吧:
Demo1:
def fun(x):
y=2
print("乘法的运行结果:",x*y)
num1=1
print("初始num1=",num1)
fun(num1)
print("y的值是:",y)
运行结果:
报错的原因是因为试图访问局部变量,但是访问的地方不在该变量y的作用域中
Demo2:
def fun():
num1=2
print("函数内修改后num1=",num1)
num1=1
print("初始num1=",num1)
fun()
print("运行完函数后num1=",num1)
运行结果:
可以看到在函数内部对全局变量的修改后,在函数执行完毕,修改的结果是无效的,全局变量并不会受到影响
再看:
Demo3:
def fun():
num1*=2
print("函数内修改后num1=",num1)
num1=1
print("初始num1=",num1)
fun()
print("运行完函数后num1=",num1)
运行结果:
报错了。这是因为在fun()函数使用了局部变量num1,它只是个跟全局变量同名的局部变量,使用前还是要赋值,因此再次强调不要这样使用
global关键字
如果真的想要在函数体内修改全局变量的值,就要使用global关键字
Demo4:
def fun():
global num1
num1=2
print("函数内修改后num1=",num1)
num1=1
print("初始num1=",num1)
fun()
print("运行完函数后num1=",num1)
运行结果:
使用global关键字就是告诉python编译器这个变量不是局部变量而是全局变量,其实有点像是"引用"的意思
nonlocal关键字
再看看另一个跟变量相关的关键字nonlocal,字面意思就是指当前的这个变量不是局部变量。nonlocal是Python3.0中新增的关键字,python2.x不支持
先来看看下面这段代码
def fun():
num2=3
def fun2():
num2*=2
print("num2=",num2)
return fun2()
fun()
运行结果:
错误的原因跟前面的差不多,就是使用了未定义的局部变量,然而num2也不是全局变量,只是fun2函数的外层变量,强行使用global定义num2的话同样会报错(不信你试试)
这时候需要使用nonlocal关键字:
def fun():
num2=3
def fun2():
nonlocal num2
num2*=2
print("num2=",num2)
return fun2()
fun()
运行结果:
如此,程序就能正常执行
4、函数的使用与返回值
函数的返回值
在Python中有的函数是有返回值的,有的函数是没有返回值的。而有返回值的函数,我们让函数可以返回一个值,也可以让函数返回多个值。
5、文档字符串
Python 递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
举个例子,我们来计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示,可以看出:
fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n
所以,fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理。
于是,fact(n)用递归的方式写出来就是:
如果我们计算fact(5),可以根据函数定义看到计算过程如下:
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000):
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
上面的fact(n)函数由于return n * fact(n - 1)引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:
可以看到,return fact_iter(num - 1, num product)仅返回递归函数本身,num - 1和num product在函数调用前就会被计算,不影响函数调用。
fact(5)对应的fact_iter(5, 1)的调用如下:
尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。
小结
使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。