python中的闭包函数

闭包函数初探

通常我们定义函数都是这样定义的

def foo():
 pass

其实在函数式编程中,函数里面还可以嵌套函数,如下面这样

def foo():
 print("hello world in foo")

 def bar():
 print("hello world in bar")

此时我们调用foo函数,执行结果会是什么样子的呢??

hello world in foo

结果如上所示,只会执行foo函数的第一层函数,bar函数是不会被执行的。为什么呢

实际上来说,不管函数写在哪个部分,那都只是定义了一个函数,只有这个函数被调用,函数内部的语句才会被执行

在上面的例子中,bar函数虽然在foo函数内部定义了,但是并没有被执行,所以bar函数是不会被执行的这样说来,定义在一个函数内部的函数就没什么作用了吗??其实不是这样的。

来看下面的例子,把bar函数作为一个值返回给foo函数,来看执行过程

def foo():
 print("hello world in foo")

 def bar():
 print("hello world in bar")
 return bar
f1=foo()
print(f1)

此时,由于bar函数作为一个返回值被返回给了foo,所以foo函数执行结果是有返回值的

此时定义一个变量f1来接收foo函数的执行返回结果,然后打印f1

返回的结果如下

hello world in foo
<function foo.<locals>.bar at 0x0000000002941A60>

可以看到首先打印了foo函数中定义的一个print语句,接着打印的是foo函数中包含的bar函数的内存地址

既然是一个函数的内存地址,当然可以加括号来执行这个函数

def foo():
 print("hello world in foo")
 def bar():
 print("hello world in bar")
 return bar
f1=foo()
f1()

此时,这段代码的执行结果为:

hello world in foo
hello world in bar

两个print语句都被打印出来了。

在上面的例子里,首先定义了一个函数foo,接着在foo函数内部又嵌套定义了一个函数bar,然后返回函数bar的函数名,这就是闭包函数的定义方式。

其实,闭包的定义就是一个函数内部又嵌套了一个函数

来看下面的这段代码

 def foo():
 print("hello world in foo")
 name="python"
 def bar():
  print(name)
  print("hello world in bar")
 return bar

 f1=foo()
 f1()

在上面的例子里,在外层函数中定义了一个变量name,然后在内层函数中打印这个变量name

此时执行上面的代码,在打印name这个变量的时候,会先在bar函数内部查找name这个变量,但是bar函数里面是没有name这个变量的,

此时根据python查找变量的LEGB法则,会到bar函数的外面一层去继续查找name这个变量,此时可以找到name这个变量

所以这里打印的foo函数中定义的name的值

执行上面的代码,打印结果如下

hello world in foo
python
hello world in bar

这里要记住很重要的一点就是:

内层函数引用了外层函数的局部变量

来分析下上面的例子中程序的执行过程:

首先运行foo函数,foo函数的执行结果是返回bar的函数名,此时又把foo函数的执行结果定义给了变量f1,
所以此时f1就等于bar这个函数的内存地址,然后f1加括号运行就表示运行了bar函数。
在执行bar函数的过程中,bar函数访问到了外层foo函数中定义的变量,这就是一个典型的闭包函数
那使用闭包函数有什么好处呢??在上面的例子里,f1的值是bar函数的内存地址,f1加括号运行就是在运行bar函数。

又由于f1是一个全局变量,这意味着可以在整个程序的任意位置都可以运行f1函数,此时再定义一个函数,在这个函数内部调用f1函数,

 def foo():
 print("hello world in foo")
 name = "python"

 def bar():
  print(name)
  print("hello world in bar")
 return bar

 f1 = foo()

 def func():
 name = "aaaaa"
 f1()
 func()

来分析一下程序的执行过程:

1.运行func函数,程序会先在内存中申请一块空间以保存name变量的值,然后运行f1函数,f1是在全局中定义的变量,所以一定可以找到f1函数的内存地址

2.f1加括号运行,就是在执行一个闭包函数,这个闭包函数内部引用了name这个变量

3.name这个变量在bar函数的外部已经定义了,所以在func函数内部调用f1函数,也就是bar函数时,其引用的变量依然是foo函数内部定义的name变量,而不是func函数内部定义的name变量,

4.因为f1函数的内部已经包含了name这个函数的值,所以就算在func函数内部也定义了name这个变量,程序执行的结果打印的依然是foo函数内部定义的name的值

程序执行结果

hello world in foo
python
hello world in bar

怎样验证一个函数是闭包函数

首先,闭包函数都有一个特有的属性:closure

在上面的例子里,打印f1的__closure__属性

 def foo():
 name = "python"
 def bar():
  print(name)
  print("hello world in bar")
 return bar
 f1 = foo()
 print(f1.__closure__)

打印结果如下:

(<cell at 0x0000000001DF5708: str object at 0x0000000001E79688>,)

可以看到__closure__属性的打印结果是一个元组形式的,其值就是f1函数的外层函数作用域

此时可以调用__closure__返回的元组的元素的cell_contents方法打印出name变量的值

 def foo():
 name = "python"

 def bar():
  print(name)
  print("hello world in bar")
 return bar

 f1 = foo()
 print(f1.__closure__[0].cell_contents)

打印结果如下:

python

可以看到程序已经打印出name变量的值了

即然__closure__的返回结果是一个元组,那么这个元组中一定是可以包含多个值的,看下面的例子

在foo函数内部定义多个变量,然后在bar函数内部打印几个变量的值,

然后运行这个闭包函数,打印闭包函数的__closure__方法

 def foo():
 print("hello world in foo")
 name1 = "python1"
 name2 = "python2"
 name3 = "python3"
 name4 = "python4"
 def bar():
  print(name1)
  print(name2)
  print(name3)
  print(name4)
  print("hello world in bar")
 return bar
 f1 = foo()
 print(f1.__closure__)

程序执行结果

(<cell at 0x0000000002145708: str object at 0x00000000021C9260>,
<cell at 0x0000000002145A08: str object at 0x00000000021C93B0>,
<cell at 0x0000000002145768: str object at 0x000000000295BE30>,
<cell at 0x0000000002145C18: str object at 0x0000000002963880>)

由于在foo函数内部定义了4个变量,而且在bar函数内部引用了这4个变量,所以打印这个闭包函数的__closure__方法,返回的元组中就有4个元素

现在可以分别打印返回的元组中的这4个字符串对象的值了

 def foo():
 name1 = "python1"
 name2 = "python2"
 name3 = "python3"
 name4 = "python4"

 def bar():
  print(name1)
  print(name2)
  print(name3)
  print(name4)
  print("hello world in bar")
 return bar
 f1 = foo()
 print(f1.__closure__[0].cell_contents)
 print(f1.__closure__[1].cell_contents)
 print(f1.__closure__[2].cell_contents)
 print(f1.__closure__[3].cell_contents)

程序执行结果

python1
python2
python3
python4

那么现在还剩下最后一个问题了,那就是闭包函数的内层函数一定要返回吗??

来看下面一个例子

 def foo():
 name = "python1"
 def bar():
  print(name)
 print(bar.__closure__)
 foo()

定义了一个嵌套函数,然后这个嵌套函数的内层函数没有被返回,而是直接打印内层函数的__closure__方法,然后直接调用外层函数。

程序执行结果

(<cell at 0x0000000002155708: str object at 0x00000000021D9688>,)

依然打印出了内层函数的引用的变量对象

这说明闭包函数的内层函数还一定要返回

闭包函数的内层函数可以调用全局变量吗??

把外层函数内部定义的变量改为全局变量,然后在内层函数中引用这个变量

 name = "python1"
 def foo():
 def bar():
  print(name)
 print(bar.__closure__)
 f=foo()
 print(f)

程序执行结果

None
None

可以看到,程序的执行结果是两个None,嵌套函数的内层函数的__closure__函数的值为None

这说明foo函数的内层嵌套函数bar调用的全局变量没有成功,所以上面的例子不是一个闭包函数

关于闭包函数的一些总结:

闭包的定义为:

在函数内部定义的函数,称为内部函数
    内部函数调用了外部函数的局部变量
    即使内部函数返回了,还是可以使用局部变量
    通常闭包函数的内层函数都要被返回给外部函数
    闭包函数的外部函数可以在任何地方被调用,而不再受函数定义时层级的限制

闭包函数的作用

1.闭包函数自带函数作用域

正常意义上的函数,在函数执行过程中查找变量的顺序是一层一层向外找,符合LEGB(Local->Enclose->Global->Built in)法则的,

但是对闭包函数来说,查找变量只会找内部函数外面的那一层,因为闭包函数本身就自带一层作用域,这样才符合"闭包"两个字的意思

2.延迟计算(也叫惰性计算)

看下面的例子

 def func():
 name="python"
 def bar():
  print(name)
 return bar
 f=func()
 print(f.__closure)

在上面的例子里,执行foo()函数的返回结果是一个包含自带的某种状态的函数,实际上这个函数并没有执行,

以后想执行这个自带状态的函数时,把func()返回结果所赋值的那个变量加括号就可以执行了,

3.要想让一个函数始终保持一种状态,就可以使用闭包

例子:

 name="python"
 def func():
 print("I like %s" % name)
 func()

上面的代码执行结果会打印一行:"I like python"

但是我们知道,在不同的地方调用func函数,打印的结果很大可能是不一样的

那么如果我想不管在什么地方调用func函数,打印的结果都是"I like python"时,

就可以使用闭包了。

 def func1():
 name="python"
 def func():
  print("I like %s" % name)
 return func
 func=func1()
 func()

如上图所示,在func函数外面再包含一层函数func1,执行func1函数,再把func1函数的返回结果赋值给func这个变量

此时func就是一个闭包函数了,把func函数加括号就可以执行了

而且我们一定知道,此时func函数的执行结果一定会打印"I like python"这句话,而且不管func函数在程序的哪个位置被调用,执行结果都是一样的

(0)

相关推荐

  • 实例讲解Python的函数闭包使用中应注意的问题

    昨天正当我用十成一阳指功力戳键盘.昏天暗地coding的时候,正好被人问了一个问题,差点没收好功,洪荒之力侧漏震伤桌边的人,废话不多说,先上栗子(精简版,只为说明问题): from functools import wraps from time import sleep def retry(attempts=3, wait=2): if attempts < 0 or attempts > 5: retry_times = 3 else: retry_times = attempts if

  • Python闭包函数定义与用法分析

    本文实例分析了Python闭包函数定义与用法.分享给大家供大家参考,具体如下: python的闭包 首先python闭包的作用,一个是自带作用域,另一个是延迟计算. 闭包是装饰器的基础. 闭包的基本形式: def 外部函数名(): 内部函数需要的变量 def 内部函数名() 引用外部的变量 return 内部函数 需要注意的是: 函数的作用域关系在函数定义阶段就已经固定,与调用位置无关. 无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系. 例子: # -*- coding:utf-8

  • Python 闭包,函数分隔作用域,nonlocal声明非局部变量操作示例

    本文实例讲述了Python 闭包,函数分隔作用域,nonlocal声明非局部变量操作.分享给大家供大家参考,具体如下: 实例对象也可以实现闭包的功能,不过实例对象消耗的资源(内存)比闭包多. demo.py(闭包): # 闭包,分割作用域. 外层函数内部嵌套内部函数,外层函数分割变量作用域,并返回内部函数的引用. # 外层函数负责分割作用域,内层函数才是闭包提供的功能. 外层函数返回内层函数的引用,供外部使用. def my_line(k, b): # k,b只在my_line函数以及creat

  • python中函数总结之装饰器闭包详解

    1.前言 函数也是一个对象,从而可以增加属性,使用句点来表示属性. 如果内部函数的定义包含了在外部函数中定义的对象的引用(外部对象可以是在外部函数之外),那么内部函数被称之为闭包. 2.装饰器 装饰器就是包装原来的函数,从而在不需要修改原来代码的基础之上,可以做更多的事情. 装饰器语法如下: @deco2 @deco1 def func(arg1,arg2...): pass 这个表示了有两个装饰器的函数,那么表示的含义为:func = deco2(deco1(func)) 无参装饰器语法如下:

  • 基于Python函数的作用域规则和闭包(详解)

    作用域规则 命名空间是从名称到对象的映射,Python中主要是通过字典实现的,主要有以下几个命名空间: 内置命名空间,包含一些内置函数和内置异常的名称,在Python解释器启动时创建,一直保存到解释器退出.内置命名实际上存在于一个叫__builtins__的模块中,可以通过globals()['__builtins__'].__dict__查看其中的内置函数和内置异常. 全局命名空间,在读入函数所在的模块时创建,通常情况下,模块命名空间也会一直保存到解释器退出.可以通过内置函数globals()

  • Python函数中的函数(闭包)用法实例

    本文实例讲述了Python闭包的用法.分享给大家供大家参考,具体如下: Python函数中也可以定义函数,也就是闭包.跟js中的闭包概念其实差不多,举个Python中闭包的例子. def make_adder(addend): def adder(augend): return augend + addend return adder p = make_adder(23) q = make_adder(44) print(p(100)) print(q(100)) 运行结果是:123和144.

  • Python中用函数作为返回值和实现闭包的教程

    函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax 但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数! def lazy_sum(*args): def sum(): ax = 0 for n in args:

  • Python闭包之返回函数的函数用法示例

    闭包(closure)不是什么可怕的东西.如果用对了地方,它们其实可以很强大.闭包就是由其他函数动态生成并返回的函数,通俗地讲,在一个函数的内部,还有一个"内层"的函数,这个"内层"的函数是被返回的,它可以访问其创建者的局部命名空间中的变量. 下面是一个非常简单的例子: # 定义一个函数 def make_closure(a): # 在函数内部再定义一个函数,其实这个里面的函数就被认为是闭包 def closure(): # 这里打印一下传递进来的数字是什么 pri

  • Python函数基础实例详解【函数嵌套,命名空间,函数对象,闭包函数等】

    本文实例讲述了Python函数基础用法.分享给大家供大家参考,具体如下: 一.什么是命名关键字参数? 格式: 在*后面参数都是命名关键字参数. 特点: 1.约束函数的调用者必须按照Kye=value的形式传值. 2.约束函数的调用者必须用我们指定的Key名. def auth(*args,name,pwd): print(name,pwd) auth(pwd='213',name='egon') def register(name,age): print(type(name),type(age)

  • python中闭包Closure函数作为返回值的方法示例

    前言 首先看看闭包的概念:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外.所以,闭包是由函数和与其相关的引用环境组合而成的实体. 一.函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. >>> def lazy_sum(*args): ... def sum(): ... ax = 0 ... for n in args: ...

  • 详解python函数的闭包问题(内部函数与外部函数详述)

    python函数的闭包问题(内嵌函数) >>> def func1(): ... print ('func1 running...') ... def func2(): ... print ('func2 running...') ... func2() ... >>> func1() func1 running... func2 running... 内部函数func2作用域都在外部函数func1作用域之内 如果试图在外部函数的外部调用内部函数将会报错 >>

随机推荐