zoukankan      html  css  js  c++  java
  • 高阶函数、函数嵌套和闭包

    函数对象部分

      函数对象:如果对象是函数的话,这个对象所有的别名都是可调用的。

    >>> def foo():
    ...     print('from foo...')
    ...
    >>> f = foo    # 函数foo对象
    >>> f()    # 别名可调用
    from foo...
    >>> bar = foo
    >>> bar()
    from foo...

      传递函数:函数是对象,也可以作为函数的参数传递给其他函数。

    >>> def bar():
    ...     print('from bar...')
    ...
    >>> def foo(func):    # 函数的参数是函数
    ...     func()
    ...
    >>> foo(bar)
    from bar…

      高阶函数:

               定义:函数接收的参数是一个函数名;

                     函数的返回值是一个函数名。

        满足上述条件的任意一个,都可称之为高阶函数。

    >>> def test(func):    # 函数接收的参数是一个函数名
    ...     func()
    ...
    >>> def foo():
    ...     print('from the foo...')
    ...
    >>> test(foo)
    from the foo...

    >>> def test(func):
    ...     return func    # 函数返回值是一个函数名
    ...
    >>> def foo():
    ...     print('from the foo...')
    ...
    >>> f = test(foo)    # 接收函数返回值
    >>> f()    # 通过变量名调用函数
    from the foo...


    嵌套函数部分

             嵌套函数:函数里面可以嵌套函数,即在函数内定义函数。在函数内调用函数不是函数嵌套。

    >>> def foo():
    ...     def bar():    # bar函数定义在foo函数内,即嵌套函数,bar函数称为内嵌函数
    ...         print('from bar...')
    ...     bar()
    ...     print('from foo...')
    ...
    >>> foo()
    from bar...
    from foo...


    闭包部分

      闭包:在计算机科学中,闭包(Closure),又称词法闭包或函数闭包,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

      内部函数的代码包含对外部作用域的引用,但一定不是对全局作用域的引用,闭包函数一定有__closure__方法。调用此方法,将打印结果是元组,元组个数代表的是闭包所引用的自由变量的个数。

      闭包的两个特点:

        自由变量未定义在本地作用域中的变量。例如定义在外层函数的作用域中的变量。

        闭包出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。

      看下面的例子:

    >>> def foo():
    ...     a = 3    # a是局部变量,定义在函数的内部
    ...     print(a)
    ...
    >>> a    # 要在函数的外面使用定义在函数内部的变量?
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'a' is not defined

      在某些特殊情况下,我们需要在函数外面使用函数里面的变量,该怎么办?

    >>> def foo():
    ...     a = 22     # 定义在函数内部的局部变量
    ...     def bar():    # 嵌套函数
    ...         return a    # 嵌套函数使用和它同作用域中的局部变量
    ...     return bar
    ...
    >>> fun = foo()
    >>> print(fun())    # 通过嵌套函数使用定义在函数内部的变量
    22

      用上面的方式就实现了在函数外面得到函数里面所定义的对象。这种写法的本质就是嵌套函数。

             在函数foo()里面,有a=22和另外一个函数bar(),它们两个都在函数foo()的环境里,但它们两个是互不统属的,所以变量a相对函数bar()是自由变量,并且在函数bar()中应用了这个自由变量——函数bar()就是我们所定义的闭包。

             闭包是一个函数,具有以下特点:

                      定义在另外一个函数内部(嵌套函数);

                      引用其所在函数环境的自由变量。

             从上述代码的效果上看,通过闭包能够在定义自由变量a=22的环境foo()之外的地方得到该自由变量所引用的对象,或者说foo()执行完毕,但a=22依然可以在fun()中存在,即bar()函数中存在,而没有被收回。

             再看一个关于抛物线的函数:

    >>> def parabola(a, b, c):
    ...     def para(x):
    ...         return a*x**2 + b*x + c
    ...     return para
    ...
    >>> p = parabola(2, 3, 4)
    >>> print(p(5))
    69

      上面的函数中,p = parabola(2, 3, 4)定义了一个抛物线的函数对象——形如y = 2x^2 + 3x + 4,如果要计算x=5时,该抛物线的值,只需要p(5)即可。

      可以利用上面的函数创建多个实例,也就是得到多个不同的抛物线函数对象。印证了闭包定义中所说的闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

      注意:对于闭包,需要注意的是,内部函数可以引用外层函数中定义的变量,但却不能直接在内部函数中修改外层函数中的变量,若要对外层函数的变量进行修改,则在内部函数中必须以nonlocal关键字对变量进行声明。

    内层函数中只是引用外层函数中的变量:
    >>> def foo():
    ...      count = 1    # 外层函数中的变量
    ...      def bar():
    ...          num = count + 1    # 内层函数中只是引用外层函数中的变量,不作修改
    ...          print('count=', count, ';num=', num)
    ...      return bar
    ... 
    >>> f = foo()
    >>> f()
    count= 1 ;num= 2
    内层函数中直接修改外层函数中的变量,不加nonlocal关键字:
    >>> def foo():
    ...      count = 1
    ...      def bar():
    ...          count += 1    # 直接修改外层函数中的变量
    ...          print('count=', count)
    ...      return bar
    ... 
    >>> f = foo()
    >>> f()
    Traceback (most recent call last):    # 报错,本地变量未定义
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in bar
    UnboundLocalError: local variable 'count' referenced before assignment
    内层函数中修改外层函数中的变量,加nonlocal关键字:
    >>> def foo():
    ...      count = 1
    ...      def bar():
    ...          nonlocal count    # 加nonlocal关键字对外层是中的变量进行声明
    ...          count += 1    # 先进行声明,在修改值
    ...          print('count=', count)
    ...      return bar
    ... 
    >>> f = foo()
    >>> f()    # 不报错
    count= 2
  • 相关阅读:
    线上六个性能问题案例分享
    通达OA 前台任意用户登录漏洞复现
    CVE-2019-11043-Nginx PHP 远程代码执行
    CVE-2019-10758-Mongo-express-远程代码执行
    CVE-2017-7529-Nginx越界读取缓存漏洞
    add_header被覆盖 -配置错误
    目录穿越漏洞 -配置错误
    CRLF注入漏洞 -配置错误
    CVE-2019-12409-Apache Solr JMX服务远程代码执行
    CVE-2017-12149-JBoss 5.x/6.x 反序列化
  • 原文地址:https://www.cnblogs.com/wgbo/p/9688339.html
Copyright © 2011-2022 走看看