zoukankan      html  css  js  c++  java
  • python中函数嵌套、函数作为变量以及闭包的原理

    嵌套函数:

    python允许创建嵌套函数。也就是说我们可以在函数里面定义函数,而且现有的作用域和变量生存周期依旧不变。

    例子:

    #encoding=utf-8

    def outer():
        name="python"

        def inner():#outer函数内部定义的函数
            print name
        return inner()#返回该内部函数

    outer()

    结果:

    理解:

    在inner函数中,python解析器需要找一个叫name的本地变量,查找失败后会继续在上层的作用域里面寻找,这个上层作用域定义在outer函数里,python函数可以访问封闭作用域。

    对于outer函数中最后一句,返回inner函数调用的结果,需要知道非常重要一点就是,inner也仅仅是一个遵循python变量解析规则的变量名,python解释器会优先在outer的作用域里面对变量名inner查找匹配的变量。

    把恰好是函数标识符的变量inner作为返回值返回回来,每次函数outer被调用的时候,函数inner都会被重新定义,如果它不被当做变量返回的话,每次执行过后它将不复存在。

    在python里,函数就是对象,它也只是一些普通的值而已。也就是说你可以把函数像参数一样传递给其他的函数或者说从函数了里面返回函数

    return内层函数时不加括号,只返回函数的地址:

    代码:

    #encoding=utf-8

    def outer():
        name="python"

        def inner():#outer函数内部定义的函数
            return name
        return inner#返回该内部函数

    print outer()

     结果:

    此时执行外层函数outer(),返回的是内层函数的函数引用(函数名称)--inner,想要执行内层函数,需要在outer()后边再加个括号,即outer()(),才会让内层函数执行

    代码:

    #encoding=utf-8

    def outer():
        name="python"

        def inner():#outer函数内部定义的函数
            return name
        return inner#返回该内部函数

    print outer()()

    结果:

    函数作为变量:

    例子:

    #encoding=utf-8

    def add(x,y):
        return x+y

    def sub(x,y):
        return x-y

    def apply(func,x,y):
        return func(x,y)

    print "apply(add,2,1):",apply(add,2,1)
    print "apply(sub,2,1):",apply(sub,2,1)
    结果:

    apply函数准备接收一个函数的变量,它也只是一个普通的变量而已,和其他变量一样。然后我们调用传进来的函数:“()代表着调用的操作,并且调用变量包含的值”。

    在函数外,我们也能看到传递函数并没有什么特殊的语法,函数的名称只是和其他变量一样的表标识符而已。

    闭包的理解:

    先看个例子

    #encoding=utf-8

    def outer():
        name="python"
        def inner():
            print name
        return inner

    res=outer()
    res()
    print res.func_closure#打印闭包里包含哪些外部变量
    结果:

    例中,inner作为一个函数被outer返回,保存在变量res中,并且还能够调用res()。为什么能调用呢?

    通过上面变量的作用域和生存周期我们不难明白,name是函数outer里的一个局部变量,也就是说只有当outer正在运行时,该变量才会存在。

    根据python的运行模式,我们是没法在函数outer执行退出之后还能继续调用inner函数的,并且在inner函数被调用时,变量name早已不存在了,但是为什么我们调用成功了呢?

    这就回到了我们的闭包这个问题上了,python支持一个叫函数闭包的特性。

    啥是闭包?

    如果一个函数定义在另一个函数的作用域内,并且引用了外层函数的变量,则该函数称为闭包。闭包是Python所支持的一种特性,它让在非global scope定义的函数可以引用其外围空间中的变量,这些外围空间中被引用的变量叫做这个函数的环境变量。环境变量和这个非全局函数一起构成了闭包。

    上例中的inner()函数就是一个闭包,它本身也是一个函数,而且还可以访问本身之外的变量。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如name,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)

    每次函数outer被调用时,inner函数都会被重新定义,上面每次返回的函数inner结果都一样,因为name没变。

    如下例所示,我们将函数稍微改动一下,结果就不一样了

    代码:

    #encoding=utf-8

    def outer(name):
        def inner():
            print name
        return inner

    res1=outer("python")#返回闭包
    res2=outer("java")#返回闭包
    res1()#执行函数
    res2()
    结果:

    分析:

    在之前的例子中

    def outer():
        name="python"
        def inner():
            print name
        return inner

    外层函数后运行后,返回一个函数+函数需要的变量

      name = "python"

      def inner() :

        print name

    上边三行是整体返回的内容

    如果在外层函数再加个外部的整形变量,在里面的函数中引用:

    #encoding=utf-8

    def outer():
        name="python"
        nbr=12
        def inner():
            print name
            print nbr
        return inner#不加括号就是返回函数对象,不是函数调用

    res=outer()
    res()#调用inner函数
    print res.func_closure#打印闭包里包含哪些外部变量
    结果:

    D:>python test.py
    python
    12
    (<cell at 0x0000000004E56CA8: str object at 0x0000000004E58760>, <cell at 0x0000000004E56F78: int object at 0x0000000004E362C0>)

    其中res = outer()是把inner函数对象赋值给res,因为没有括号,所以不是调用而是返回个函数对象

    res()是调用inner函数

    print res.func_closure是打印闭包里包含哪些外部变量,可以看到结果里有两个:即python和12

    (<cell at 0x0000000004E56CA8: str object at 0x0000000004E58760>, <cell at 0x0000000004E56F78: int object at 0x0000000004E362C0>)

    闭包特点:

    一个函数返回的函数对象,这个函数对象执行的话依赖非函数内部的变量值,这个时候,函数返回的实际内容如下:
    1 函数对象
    2 函数对象需要使用的外部变量和变量值

    以上就是闭包

    闭包必须嵌套在一个函数里,必须返回一个调用外部变量的函数对象,才是闭包

     

    在上边的例子中,相对于inner来说 ,outer函数就是它得全局变量,就好像你存粹写个函数会用到函数外面环境定义得全局变量一样 ,都是相对的概念

    通俗理解就是:里面函数执行  ,需要用到外面函数的一个变量 ,所以,就把外面变量和里面这个函数合到一块,合到一块的这两个东西就是闭包



  • 相关阅读:
    Ubuntu18.04下更改apt源为阿里云源
    sudo: pip:command not found问题解决
    CentOS8 中文输入法
    PyCharm2019 激活方式
    正则爬取京东商品信息并打包成.exe可执行程序
    学习springcloud bus+springcloud config实现刷新配置
    使用springcloud-config统一管理配置文件
    记录一下今天使用maven构建项目分多个模块时遇到的扫描properties问题
    spring+mybatis+thymeleaf
    回调地狱问题
  • 原文地址:https://www.cnblogs.com/xiaxiaoxu/p/9785687.html
Copyright © 2011-2022 走看看