高阶函数
在Python中,函数其实也是一种数据类型。
def test(): return 'hello world' print(type(test)) # <class 'function'>
函数对应的数据类型是 function
,可以把它当做是一种复杂的数据类型。
既然同样都是一种数据类型,我们就可以把它当做数字或者字符串来处理。
定义一个变量指向函数
在Python中,我们还可以定义一个变量,让它来指向一个函数,相当于给函数起了一个别名。
def test(): return 'hello wrold' fun = test # 定义了一个变量fun,让它指向了 test 这个函数 print(fun()) # 使用fun()可以直接调用test这个函数 print(id(fun)) # 1819677672040 print(id(test)) # 1819677672040
注意:在定义一个变量表示一个函数时,函数后面不能加括号!加括号表示的是调用这个函数。
def test(): return 'hello world' result = test() # 这种写法是调用test函数,并把函数的返回值赋值给result变量 print(result()) # 这里会报错 TypeError: 'str' object is not callable fun = test # 这种写法是给test函数起了一个别名,注意,这里的test后面不能加() fun() # 可以使用别名调用这个函数
高阶函数
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,同样,我们还可以把一个函数当做另一个函数的返回值。这种函数的使用方式我们称之为高阶函数。
函数做为另一个函数的参数
def test(age, action): if age < 18: print('您还没满十八岁,请退出') action() # 把参数action直接当做一个函数来调用 def smoke(): print('我已经年满十八岁了,我想抽烟') my_action = smoke # 定义一个变量my_action,让它指向smoke函数 test(21, my_action) # 将my_action传给test函数作为它的参数 test(21, smoke) # 还可以不再定义一个新的变量,直接传入函数名
函数作为另一个函数的返回值
def test(): print('我是test函数里输入的内容') def demo(): print('我是demo里输入的内容') return test # test 函数作为demo函数的返回值 result = demo() # 我是demo里输入的内容 调用 demo 函数,把demo函数的返回值赋值给 result print(type(result)) # <class 'function'> result 的类型是一个函数 result() # 我是demo里输入的内容 我是test函数里输入的内容 既然result是一个函数,那么就可以直接使用() 调用这个函数 demo()() # 我是demo里输入的内容 我是test函数里输入的内容
闭包
函数只是一段可执行代码,编译后就“固化”了,每个函数在内存中只有一份实例,得到函数的入口点便可以执行函数了。函数还可以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。
函数嵌套
在函数里面还可以定义函数,可以嵌套多层,执行需要被调用。
def outer(): print('outer----hello') def inner(): # inner这个函数是在outer函数内部定义的 print('inner----hello') inner() # inner函数只在outer函数内部可见 outer() # inner() 这里会报错,在outer函数外部无法访问到inner函数
什么是闭包
闭包是由函数及其相关的引用环境组合而成的实体(即:闭包=函数块+引用环境)。
def outer(n): num = n def inner(): return num+1 return inner print(outer(3)()) # 4 print(outer(5)()) # 5
在这段程序中,函数 inner 是函数 outer 的内嵌函数,并且 inner 函数是outer函数的返回值。我们注意到一个问题:内嵌函数 inner 中引用到外层函数中的局部变量num,Python解释器会这么处理这个问题呢?先让我们来看看这段代码的运行结果,当我们调用分别由不同的参数调用 outer 函数得到的函数时,得到的结果是隔离的(相互不影响),也就是说每次调用outer函数后都将生成并保存一个新的局部变量num,这里outer函数返回的就是闭包。如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。
修改外部变量的值
闭包里默认不能修改外部变量。
def outer(n): num = n def inner(): num = num + 1 return num return inner print(outer(1)())
上述代码运行时会报错!
UnboundLocalError: local variable 'num' referenced before assignment
原因分析
在python里,只要看到了赋值语句,就会认为赋值语句的左边是一个局部变量。num = num + 1
这段代码里,num
在=
的左边,python解析器会认为我们要修改inner
函数里num
这个局部变量,而这个变量使用之前是未声明的,所以会报错。
解决方案
我们分析过,报错的原因在于当我们在闭包内修改外部变量时,会被python解析器误会为内部函数的局部变量。所以,解决方案就在于,我们需要想办法,让解析器知道我们不是要修改局部变量,而是要修改外部变量。
解决方法:使用 nonlocal
关键字
def outer(n): num = n def inner(): nonlocal num # 修改前使用nonlocal关键字对 num 变量进行说明 num = num + 1 return num return inner print(outer(2)())