高阶函数
高阶函数特点:
- 函数的名可以j进行赋值
- 函数名可以做为函数的参数,可以作为函数的返回值
函数本身是一个对象,函数的名字是一个变量,所以函数的名字可以赋值
现在我们用Python代码实现:
1 def f(): 2 print('ok') 3 4 def bar(a,b,func): 5 func() #此时的func接收了函数f 所以func() = f() 6 return 1 7 8 bar(1,2,f) #将函数名作为参数传入函数bar里面的形参func
函数可以作为函数的返回值
例子1;变量作为返回值
1 def foo2(): 2 x = 5 3 return x 4 5 print(foo2()) #5
例子2,因为上面说到函数的名字是一个变量,所以函数才可以作为返回值
1 def foo3(): 2 def inner(): 3 return 8 4 return inner 5 6 print(foo3()) #foo3() 接收的是innet函数对象指向的内存地址 <function foo3.<locals>.inner at 0x7fd263565730> 7 ret = foo3() 8 print(ret()) #执行foo3()函数里面的内容 8
如果上面都理解后可看下面实例,如果看不懂,请重现看前面的解释:
1 def f(n): 2 return n*n 3 4 def foo(a,b,func): 5 func(a)+func(b) 6 ret = func(a) + func(b) 7 return ret 8 foo(1,2,f) 9 print(foo(1,2,f))
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 def f(n): #1.定义一个函数f 2 return n*n #2.函数返回的是函数f的形参n接收实参的积 3 4 def foo(a,b,func): #3.定义一个函数foo 定义三个形参 5 func(a)+func(b) #5.因为在调用函数foo把实参1,2,f(f为函数的名字)分别传给a,b,func 6 #6.所以此时func(a)+func(b)相当于f(1)+f(2) 7 ret = func(a) + func(b) 8 return ret #7.返回的ret为 1*1 + 2+5 = 5 所以返回值应该 9 foo(1,2,f) #4.调用函数foo 把实参1,2,f(f为函数的名字)分别传给a,b,func 10 print(foo(1,2,f)) #8.此时的值应该是 1*1 + 2+5 = 5
函数的返回值
要想获取函数的执行结果,就可以用return语句把结果返回
注意:
函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以也可以理解为return语句代表这函数结束
如果未在函数中制定return,那这个函数的返回值为None
return可以返回多个对象,解释器会把多个对象组装成一个元组作为一个整体把结果输出
函数的作用域
Python中的作用域分为四种情况:
- L : local 局部作用域,即函数中定义的变量
- E : enclosing 嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的:
- G : globa 全局变量,就是模块级别定义的变量;
- B : built-in 系统固定模块里面的变量,比如int,bytearray等,搜索变量的优先级顺序依次是:作用域局部 > 外层作用域 > 当前模块中的全局 > Python内置的作用域,也就是LEGB
例子1:
1 x = int(2.9) # int built-in 2 g_conut = 0 # global 3 def outer(): 4 o_count = 1 # enclosing 5 def inner(): 6 i_count = 2 #local 7 print(o_count) 8 # print(i_count)找不到 9 inner() 10 outer() 11 12 # print(o.count)找不到
当然,local和enclosing是相对的,enclosing变量相对上层来说也是local。
作用域的产生
在Python中,只有模块,类以及函数才会引入型的作用域,其他的代码块,如if try for 是不会引入新的作用域的,如下代码
1 if 2 > 1: 2 x = 1 3 print(x) # 1
这个是没有问题的,if并没有引入一个新的作用域,x仍处当前的作用域中,后面的代码可以使用
1 def test(): 2 x = 1 3 print(x) #NameError: name 'x' is not defined
def class lambda是可以引入新的作用域的
变量的修改
1 x = 3 2 def f2(): 3 print(x) 4 x = 5 5 f2() # UnboundLocalError: local variable 'x' referenced before assignment 6 7 #错误原因在于print(x)时,解释器会在局部作用域朝,会找到x=5(函数已经加载到内存),但x使用在声明前了,所以报错: 8 #local variable 'x' referenced before assignment 如何证明找到了x=5?注释掉x = 5 返回值就为3 9 #同理 10 x = 6 11 def f2(): 12 x += 1 # UnboundLocalError: local variable 'x' referenced before assignment 13 f2()
global关键字
当局部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了,当修改的变量是在局部作用域(global作用域)上的,就要使用global先声明一下,代码如下:
1 count = 10 2 def outer(): 3 global count 4 print(count) # 10 5 count = 100 6 print(count) # 100 7 outer()
nonlocal关键字
global关键字声明的变量必须在全局作用域上,不能嵌套作用域上,当要修改作用域(enclosing组配用于,外层非全局作用域)中的变量怎么办呢,这是就需要nonlocal关键字
1 def outer(): 2 count = 10 3 def inner(): 4 nonlocal count 5 count = 20 6 print(count) #20 7 inner() 8 print(count) #20 9 outer()
小结
变量查找顺序:LEGB,局部作用域 > 外层作用域 > 当前模块总的全局 > Python内置作用域
只有模块,类,函数才能引入型的作用域
对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会使用外部作用域的变量
内部作用域要修改外部作用域变量值时,全局变量要使用global关键字,嵌套作用域变量要使用nonlocal关机中,nonlocal关键字是Python3中新增的关键字,有了这个关键字就能完美的闭包了
递归函数
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)
但凡可以用递归写出的都可以用循环解决,递归的效率,在很多时候会很低,你可以不用,但你得会!
实例1(阶乘)
1 def factorial(n): #根据下面调用函数传入的实参 此时 n = 5 2 result = n #result = n = 5 3 for i in range(1,n): #循环 i的值为1~5 不包括5 也就是1,2,3,4 4 result *= i #第一次循环为 5*1 第二次循环为5*2 第三次循环为10*3 最后一次循环为30*4 5 return result #第一次循环返回值为5 第二次循环值为10 第三次循环的值为30 最后一次返回值为120 6 print(factorial(5)) #打印结果为120 7 8 #------------递归---------- 9 def factorial_new(n): #根据下面调用函数传入的实参 此时n = 3 10 if n == 1: #判断n=1时 11 return 1 #返回1 12 #因为递归函数函数必须要有一个明确的结束条件 13 return n*factorial_new(n-1) #返回 3*(3-1) 14 print(factorial_new(3)) #6
实例2(斐波那契数列)
1 def fibo(n): #调用函数是传入的实参此时的 n = 3 2 before = 0 3 after = 1 4 for i in range(n - 1): #循环i的次数 3-1=2 5 ret = before + after #第一次循环 ret = 0 + 1 = 1 第二次循环 ret = 1 = 1 = 2 6 before = after #第一次循环 before = 1 第二次循环 berore = 2 7 after = ret #第一次循环 after = 1 第二次循环 after = 2 8 return ret #返回的是ret的值 所以为2 9 10 print(fibo(3)) #2 11 #-----------递归------------ 12 def fibo_new(n): #根据下面调用函数传入的实参 此时n = 3 13 if n <= 1: #判断n=1或<1时 14 return n #返回n 15 return (fibo_new(n - 1) + fibo_new(n - 2)) #返回 (3-1)*(3-2) 16 17 print(fibo_new(3)) #2
内置函数
重要的内置函数
1.filter(function,sequence)
1 def fun1(s): 2 if s != 'a': 3 return s 4 5 ret = filter(fun1,str) 6 print(list(ret)) #ret是一个迭代器对象
对sequence中的item一次执行function(item),并将结果为Ture的item做成一个filter object的得带去返回,可以看做是过滤函数
2.map(function,sequence)
1 str = [1,2,'a','b'] 2 3 def fun2(s): 4 return s , 'dai' 5 6 ret = map(fun2,str) 7 print(ret) #<map object at 0x7f496baa4898> 8 print(list(ret)) #[(1, 'dai'), (2, 'dai'), ('a', 'dai'), ('b', 'dai')]
对sequence中的item依次执行function(item),将结果组合成mao object迭代器返回,map也支持多个sequence,这就要求function特支持相应数量的参数输入
def add(x,y): return x+y print(list(map(add,range(10),range(10))))
3.reduce(function,sequence,strting_value)
1 from functools import reduce 2 def add1(x,y): 3 return x + y 4 5 print(reduce(add1,range(1,101))) #5050 (1+2+3....+100) 6 print(reduce(add1,range(1,101),20)) #5070 (1+2+3....+100+20)
对sequence中的item顺序迭代调用function,如果starting_value,还可以作为初始值使用
4.lambda
普通函数与匿名函数的对比
1 #普通函数 2 def add(a,b): 3 return a + b 4 5 print(add(2,3)) 6 7 #匿名函数 8 add = lambda a,b : a + b 9 print(add(2,3))
匿名函数的命名规则,用lambda关键字标示,冒号(:)左侧表示函数接收的参数(a,b),冒号(:)右侧表示函数的返回值(a + b)
因为lambda在创建时不需要命名,所以,叫匿名函数