第5.5节 函数递归、嵌套及样例

一、    函数递归及嵌套简介
1.    函数支持递归,递归就是函数可以在函数内调用自己,这种情况在C语言等语言就已经支持,不单独介绍;
2.    Python支持函数内再定义函数,这种方式称为函数嵌套。函数内的函数称为局部函数,其上层函数称为封闭函数:
1)    在默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭函数内有效;
2)    封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数,此时返回的函数在调用方使用时就可以等同于普通函数一样使用;
3)    在局部函数中如果需要访问封闭函数的变量,需要使用nonlocal进行声明;
4)    局部函数大多数情况下可以与封闭函数使用两个函数实现,但局部函数在某些特定场景下使用可以简化程序的实现方式。相关内容在后面章节再介绍。
  
二、    函数其他的知识
1.    要判断某个对象是否为函数可调用,可使用内置函数callable,该函数参数为需要判断的对象如函数名,返回值为布尔值,为True就表示可调用;
2.    所有函数都是 function 对象,这意味着可以把函数本身赋值给变量,就像把整数、浮点数、列表、元组赋值给变量一样。举例:

>>> def f(n1,n2,*n):
    print('n1=',n1,',n2=',n2,',numbers=',n)
>>> add=f
>>>type(add)  #结果为:<class 'function'>


3.    可将函数作为其他函数的形参,通过使用函数作为参数可以在调用函数时动态传入函数,实际上就可动态改变被调用函数的部分代码;
4.    Python 还支持使用函数作为其他函数的返回值;
5.    如果函数需要有多个返回值,则既可将多个值包装成容器的元素如列表之后返回,也可直接返回多个值。如果 函数直接返回多个值,Python 会自动将多个返回值封装成元组。六、    案例
1.    实现完整的可变参数的计算函数
该函数输入一串至少2个数字的不限数量的数字,然后调用对应的运算符进行连续运算(如连加、连减、连乘等),如:cal(‘+’,1,2,3) 、cal(‘*’,1,2,3,4)结果为24。不多说,直接上代码:

\>>> def cal2var(number1,number2,calmethod): #实现2个数的运算,第三个参数为运算符
    if calmethod=='+':return  number1+number2;
    elif  calmethod=='-':return  number1-number2;
    elif  calmethod=='*':return  number1*number2;
    elif  calmethod=='/':return  number1/number2;
    else: raise ValueError('运算符必须是+-*/中的一个')
\>>> def cal(number1,number2,*numbers,calmethod='+'):
 #实现n个数的运算,第1个和第2个参数必须有且是整数,后面还可以跟随不限量的多个整数,最后一个参数为运算符
   print('number1=',number1,',number2=',number2,',numbers=',numbers,', calmethod=',calmethod) 
   calmethod=calmethod.strip() #去掉运算符前后的空格
   if len(calmethod)!=1: raise ValueError('运算符长度必须为1个字符') 
   if '+-*/'.find(calmethod)==-1 :raise ValueError('运算符必须是+-*/中的一个') 
   if not isinstance(number1,int): raise TypeError('第1个参数必须为整数') 
   if not isinstance(number2,int): raise TypeError('第2个参数必须为整数') 
   result=cal2var(number1,number2,calmethod)
   for i,n in enumerate(numbers): #注意使用了enumerate函数,请参见前面的章节《几个与迭代相关的函数》内容
       if  isinstance(n,int): #判断n是否为整数
           result=cal2var(result,n,calmethod)
       else:raise TypeError('第'+str(i+3)+'个参数必须为整数') 
   return result

\>>> cal(1,2,3,4,5,6)
number1= 1 ,number2= 2 ,numbers= (3, 4, 5, 6) , calmethod= +
21
>>> cal(1,2,3,4,5,6,'*') #调用方式对吗?看结果
number1= 1 ,number2= 2 ,numbers= (3, 4, 5, 6, '*') , calmethod= +
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    cal(1,2,3,4,5,6,'*') #调用方式对吗?看结果
  File "<pyshell#3>", line 12, in cal
    else:raise TypeError('第'+str(i+3)+'个参数必须为整数')
TypeError: 第7个参数必须为整数
\>>> cal(1,2,3,4,5,6,calmethod='*')
number1= 1 ,number2= 2 ,numbers= (3, 4, 5, 6) , calmethod= *
720
\>>> cal(1,2,3,4,5,6,7)
number1= 1 ,number2= 2 ,numbers= (3, 4, 5, 6, 7) , calmethod= +
28


    说明:
1)    上述实现先定义了函数cal2var,实现两个数的运算,因为cal函数中需要先实现第一个和第二个参数的运算,后面才是可变个数的参数参与运算,为了简化代码,就定义了cal2var,该函数的定义可以直接放到函数cal函数中作为局部函数,使用效果是相同的;
2)    看了cal函数的第一个print语句的输出就知道收集参数怎么使用;
3)    cal函数的calmethod有缺省值,因此放在最后,如果不使用缺省值,就可以放在第一个,这样后面调用时就不用关键字参数方式调用,本例特地这样使用,实际上由于收集参数后面必须是关键字参数的形式,因此这个缺省值没有用,主要是为了演示收集参数的实参和形参匹配过程;
4)    这个函数其实结合后面章节的动态执行方法有个更简单的实现方式,到时再演示。

2.    使用递归函数和局部函数再次实现上述案例

def cal(number1,number2,*numbers,calmethod='+'): 
#实现n个数的运算,第1个和第2个参数必须有且是整数,
#后面还可以跟随不限量的多个整数,最后一个参数为运算符
   def cal2var(number1,number2,calmethod): #局部函数实现2个数的运算,第三个参数为运算符
       if calmethod=='+':return  number1+number2;
       elif  calmethod=='-':return  number1-number2;
       elif  calmethod=='*':return  number1*number2;
       elif  calmethod=='/':return  number1/number2;
       else: raise ValueError('运算符必须是+-*/中的一个') 
   #由于递归调用传递参数时,将收集参数的数据(此时保存在元组内)作为参数传递,
   #导致实际numbers可能是包含一个元组元素的元组,因此需要将这种情况的元素去掉一层元组
   if len(numbers)==1:numbers=numbers[0]    
   print('number1=',number1,',number2=',number2,',numbers=',numbers,'(len=',len(numbers),'), calmethod=',calmethod) 
   calmethod=calmethod.strip() #去掉运算符前后的空格
   if len(calmethod)!=1: raise ValueError('运算符长度必须为1个字符') 
   if '+-*/'.find(calmethod)==-1 :raise ValueError('运算符必须是+-*/中的一个') 
   if not isinstance(number1,int): raise TypeError('第1个参数必须为整数') 
   if not isinstance(number2,int): raise TypeError('第2个参数必须为整数') 
   result = cal2var(number1,number2,calmethod) 
   if not len(numbers): return result
   ops=list(numbers)
   number=ops.pop(0)
   return cal(result,number,tuple(ops),calmethod='+')  
   return result


执行截图:
 
本节详细介绍了函数函数递归、函数嵌套以及函数的作用域相关内容,并进行了计算函数的实现。
老猿Python(https://blog.csdn.net/LaoYuanPython)系列文章用于逐步介绍老猿学习Python后总结的学习经验,这些经验有助于没有接触过Python的程序员可以很容易地进入Python的世界。
欢迎大家批评指正,谢谢大家关注!

posted on 2019-05-29 14:22  老猿Python  阅读(211)  评论(0编辑  收藏  举报