• python学习之路(18)


    返回函数


    函数作为返回值

    高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

    我们来实现一个可变参数的求和。通常情况下,求和的函数是这样定义的:

    >>> def a(*args):
        ax=0
        for n in args:
            ax =ax+n
        return ax
    
    >>> a([1,2,4])
    Traceback (most recent call last):
      File "<pyshell#6>", line 1, in <module>
        a([1,2,4])
      File "<pyshell#5>", line 4, in a
        ax =ax+n
    TypeError: unsupported operand type(s) for +: 'int' and 'list'
    >>> a(1,2,3,4)
    10

    这里不能用list传参 注意是*args

    但是,如果不需要立刻求和,而是在后面的代码中,根据需要再计算怎么办?可以不返回求和的结果,而是返回求和的函数!

    >>> def a(*args):
        def sum():
            ax=0
            for n in args:
                ax=ax+n
            return ax
        return sum

    当我们调用a()时,返回的并不是求和结果,而是求和函数:调用函数s时,才真正计算求和的结果:

    >>> s=a(1,2)
    >>> s
    <function a.<locals>.sum at 0x05D14C00>
    >>> s()
    3

    在这个例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。

    请再注意一点,当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数:

    >>> s1=a(1,2)
    >>> s2=a(1,2)
    >>> s1==s2
    False
    

     s1()和s2()的调用结果互不影响。

    闭包

    注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用,所以,闭包用起来简单,实现起来可不容易。

    另一个需要注意的问题是,返回的函数并没有立刻执行,而是直到调用了f()才执行。我们来看一个例子:

    >>> def p():
        fs=[]
        for i in range(1,4):
            def f():
                return i*i
            fs.append(f)
        return fs
    
    >>> f1,f2,f3 =count()
    Traceback (most recent call last):
      File "<pyshell#39>", line 1, in <module>
        f1,f2,f3 =count()
    NameError: name 'count' is not defined
    >>> f1,f2,f3 =p()
    >>> f1()
    9
    >>> f2()
    9
    >>> f3()
    9

    全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9

    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

    >>> def count():
        fs=[]
        for i in range(1,4):
            def f(j):
                def g():
                    return j*j
                return g
            fs.append(f(i))
        return fs
    
    >>> f1=count()
    >>> f1()
    Traceback (most recent call last):
      File "<pyshell#33>", line 1, in <module>
        f1()
    TypeError: 'list' object is not callable
    >>>  f1, f2, f3 = count()
    SyntaxError: unexpected indent
    >>> f1, f2, f3 = count()
    >>> f1()
    1
    >>> f2()
    4
    >>> f3()
    9
    对于@塔塔gogo的例子,说白了就是i是公共的,j是私有的。也就是
    i=1 j=i #f1可使用这个j,以及公共的i i=2 j=i #f2可使用这个j,以及公共的i i=3 j=i #f3可使用这个j,以及公共的i

    虽然表面上看有3个j,然而这3个j在内存中具有不同的地址,而公共的i只有一个地址。

    最后计算f1()时,虽然此时i=3,但是f1中的j为1,从而得到结果1;同理f2()为4,f3()为9.

    下面将这个程序稍作修改,结果显而易见。

    count()中存放的是三个f函数,当调用f1,f2,f3 = count()时,此时的for循环结束了,i=3.再执行f1()的时候相当于执行下面: i=3 def f(): return i*i 这个时候的值为9.

    其实count()执行后,是返回一个这样的list【g(j1),g(j2),g(j3)】,而不是【f(j1),f(j2),f(j3)】。然后>>> f1, f2, f3 = count()分别指向list中的每个元素。

    lambda简化函数

    f1, f2, f3 = map(lambda y : (lambda : y * y), range(1,4))
  • 相关阅读:
    JavaSE 基础 第51节 定义自己的异常
    JavaSE 基础 第50节 Java中的异常链
    JavaSE 基础 第49节 手动抛出异常
    JavaSE 基础 第48节 Java中的异常声明
    JavaSE 基础 第47节 获取异常信息
    JavaSE 基础 第46节 异常的分类
    JavaSE 基础 第45节Java异常快速入门
    JavaSE 基础 第44节 引用外部类的对象
    JavaSE 基础 第43节 静态内部类
    通用爬虫
  • 原文地址:https://www.cnblogs.com/-zhong/p/10880407.html
Copyright © 2020-2023  润新知