前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3 Python 基础: Python函数及递归函数知识点梳理

3 Python 基础: Python函数及递归函数知识点梳理

原创
作者头像
野原测试开发
修改2019-07-24 10:40:55
1K0
修改2019-07-24 10:40:55
举报
文章被收录于专栏:技术探究

Python函数

函数的英文是function,所以,通俗地来讲,函数就是功能的意思。函数是用来封装特定功能的,比如,在Python里面,len()是一个函数,len()这个函数实现的功能是返回一个字符串的长度,所以说len()这个函数他的特定功能就是返回长度,再比如,我们可以自己定义一个函数,然后编写这个函数的功能,之后要使用的时候再调用这个函数。所以函数分为两种类型,一种是系统自带的不用我们编写其功能系统自己就有的,比如len()这种函数,另一种函数是我们自定义的,需要我们编写其功能的,这种函数自由度高,叫做自定义函数,需要使用的时候直接调用该函数。

Python里函数的定义

在Python中要想使用自定义函数,就得首先定义一个函数,定义一个函数包括两个部分的含义,第一个含义是申明这个指定的部分是函数,而不是其他的对象,第二个含义是要定义这个函数所包含的功能,也就是要编写这个函数的功能。

代码语言:txt
复制
def 函数名():

    函数内容;函数内容

    函数内容;函数内容
代码语言:txt
复制
### 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:

代码语言:txt
复制
def fun(x):  
    y=2  
    print("乘法的运行结果:",x*y)  

num1=1  
print("初始num1=",num1)  
fun(num1)  
print("y的值是:",y)  

运行结果:

报错的原因是因为试图访问局部变量,但是访问的地方不在该变量y的作用域中

Demo2:

代码语言:txt
复制
def fun():  
    num1=2  
    print("函数内修改后num1=",num1)  

num1=1  
print("初始num1=",num1)  
fun()  

print("运行完函数后num1=",num1)

运行结果:

可以看到在函数内部对全局变量的修改后,在函数执行完毕,修改的结果是无效的,全局变量并不会受到影响

再看:

Demo3:

代码语言:txt
复制
def fun():  
    num1*=2  
    print("函数内修改后num1=",num1)  

num1=1  
print("初始num1=",num1)  
fun()  

print("运行完函数后num1=",num1)  

运行结果:

报错了。这是因为在fun()函数使用了局部变量num1,它只是个跟全局变量同名的局部变量,使用前还是要赋值,因此再次强调不要这样使用

global关键字

如果真的想要在函数体内修改全局变量的值,就要使用global关键字

Demo4:

代码语言:txt
复制
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不支持

先来看看下面这段代码

代码语言:txt
复制
def fun():  
    num2=3  
    def fun2():  
        num2*=2  
        print("num2=",num2)  
    return fun2()  
  
fun()

运行结果:

错误的原因跟前面的差不多,就是使用了未定义的局部变量,然而num2也不是全局变量,只是fun2函数的外层变量,强行使用global定义num2的话同样会报错(不信你试试)

这时候需要使用nonlocal关键字:

代码语言:txt
复制
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标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档