目录
  • 一、返回函数
    • 1、闭包
    • 2、nonlocal
  • 二、匿名函数——lambda

    此文章继续上篇高阶函数,地址:python函数式编程以及高阶函数

    一、返回函数

    高阶函数的特性,除了可以接受函数作为参数之外,高阶函数还可以返回函数下面来看几个案例:

    1、定义一个求和的函数,可以这样写
    # -*- coding: utf-8 -*-
    def test_1(*args):
        i = 0
        for n in args:
            i = i + n
        return i
    
    print(test_1(10,20,30))
    
    #输出
    60
    
    但是如果不需要立即求和,而是需要在后面的代码中再进行计算改怎么办,当出现这种情况时,就可以不返回求和的结果,而是返回求和的参数,修改后可以这样写:
    # -*- coding: utf-8 -*-
    def test_1(*args):
        def test_sum():
            i = 0
            for n in args:
                i = i + n
            return i
        return test_sum
    
    f = test_1(10,20,30)
    print(f)
    print(f())
    
    #输出
    <function test_1.<locals>.test_sum at 0x0000020483CAE7A0>
    60
    
    可以看出,当把函数的结果赋值给f时,直接输出f,返回的是函数,只有在调用 f( ) 时,才会返回结果

    看过上面的案例,还可以发现一件事,就是函数内部定义函数是可以直接调用最外层函数的参数的,而在函数内部定义的函数,这种函数又叫内部函数,最外层的函数叫外部函数

    1、闭包

    内部函数可以引用外部函数的参数和局部变量,当外部函数返回内部函数时,相关的参数和变量都保存在返回的内部函数中,这种程序结构又称为“闭包(Closure)”上面的内部函数test_sum就引用了局部函数args

    需要注意的是每次调用外部函数test_1()时,每次调用都会生成一个新的函数,即便传入相同的参数:

    # -*- coding: utf-8 -*-
    def test_1(*args):
        def test_sum():
            i = 0
            for n in args:
                i = i + n
            return i
        return test_sum
    
    f1 = test_1(10,20,30)
    f2 = test_1(10,20,30)
    print(f1)
    print(f1())
    print(f2)
    print(f2())
    if f1 == f2 :
        print("yes")
    else:
        print("error")
    
    #输出
    <function test_1.<locals>.test_sum at 0x000001F27E2AE7A0>
    60
    <function test_1.<locals>.test_sum at 0x000001F27E2AE8C0>
    60
    error
    
    可以看到,就算参数相同、返回的值相同,但是每次调用函数返回的函数是不一样的

    还需要注意的是,如果只是把返回的函数赋值给变量,那么这个函数是不会执行的,直到调用函数才会执行:

    # -*- coding: utf-8 -*-
    def count():
        fs = []
        for i in range(1, 4):
            def f():
                 return i*i
            fs.append(f)
        return fs
    
    f1, f2, f3 = count()
    print(f1())
    print(f2())
    print(f3())
    
    #输出:
    9
    9
    9
    
    可以发现,连续把count函数赋值了三次变量后,引用变量时,返回的值全部都是9,这是因为返回函数 f( ) 中调用了局部变量i,而i是for循环引用的函数,在赋值 count( ) 函数到变量时,因为并没有直接调用函数,所以内部函数 f( ) 其实是没有执行的,只是进行了循环,而赋值三次后,变量i已经循环到了3,这时候调用了函数,内部函数 f( ) 在这个时候执行了,所以三次的结果都是9
    

    注意:在使用闭包特性时要记住,返回函数(内部函数)不要引用任何循环变量或后续会发送变化的变量,如果一定要使用循环变量怎么办,可以再创建一个函数例如:

    # -*- coding: utf-8 -*-
    def count():
        def f(j):
            def g():
                return j * j
            return g
        fs = []
        for i in range(1,4):
            fs.append(f(i))
        return fs
    
    f1,f2,f3 = count()
    print(f1())
    print(f2())
    print(f3())
    
    #输出:
    1
    4
    9
    
    这样写,在函数中就调用了函数
    

    2、nonlocal

    使用闭包,即内部函数调用了外部函数的局部变量,如果只是读取外层函数变量的值,可以看到返回的闭包函数调用一切正常:

    # -*- coding: utf-8 -*-
    def inc():
        x = 0
        def fn():
            # 可以看到这里只是读取了x的值:
            return x + 1
        return fn
    
    f = inc()
    print(f()) 
    print(f()) 
    
    #输出
    1
    1
    

    但是如果要在内部函数去修改外部函数变量的值时,会发生报错

    # -*- coding: utf-8 -*-
    def inc():
        x = 0
        def fn():
            # 这里在内部函数修改了外部函数变量的值
            return x = x + 1
        return fn
    
    f = inc()
    print(f()) 
    print(f()) 
    
    #输出,这里直接就报错了
      File "c:\Users\RZY\Desktop\work\py\test.py", line 5
        return x = x + 1
                 ^
    SyntaxError: invalid syntax
    

    上面的原因是因为x作为局部变量是没有初始化的,所以直接修改x变量是不行的,但是可以使用nonlocal声明把x变量初始化,从而可以正常调用函数

    # -*- coding: utf-8 -*-
    def inc():
        x = 0
        def fn():
            # 先声明x变量不是fn函数的局部变量
            nonlocal x
            x = x + 1
            return x
        return fn
    
    f = inc()
    print(f()) 
    print(f()) 
    
    #输出
    1
    2
    

    注意:使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量,从而时函数正常调用

    引用一个示例:

    - 利用闭包返回一个计数器函数,每次调用它返回递增整数
    # -*- coding: utf-8 -*-
    def createCounter():
        x = 0 
        def counter():
            nonlocal x 
            x = x + 1
            return x 
        return counter
    
    # 测试:
    counterA = createCounter()
    print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
    counterB = createCounter()
    if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
        print('测试通过!')
    else:
        print('测试失败!')
        
    #输出
    1 2 3 4 5
    测试通过!
    
    #解析
    其实和上面类似,利用nonlocal声明之后可以使内部函数修改外部函数的变量,然后返回一个函数,从而实现每次调用递增
    

    二、匿名函数——lambda

    • 有些时候在传入函数时,并不需要显式的定义函数,直接传入匿名函数更方便
    • 而python中,对匿名函数提供了支持,以map()为例,在计算f(x)=x*x时,除了可以定义一个函数f之外,还可以直接传入匿名函数:
    #使用匿名函数:
    >>> list(map(lambda x:x * x,[1,2,3,4,5,6]))  
    [1, 4, 9, 16, 25, 36]
    
    #定义函数:
    >>> def f(x):
    ...     return x * x
    ... 
    >>> list(map(f,[1,2,3,4,5,6]))   
    [1, 4, 9, 16, 25, 36]
    
    #虽然两种方法都可以达到效果,但是可以看出匿名函数比较简洁
    

    从上面的例子可以看出,lambda关键字就表示匿名函数,而前面的x就表示函数的参数匿名函数有一个限制,就是只能有一个表达式,不需要写return返回,返回的值为表达式的结果。因为匿名函数不需要定义函数名称,所以也不用担心函数名会冲突,并且匿名函数也是一个函数对象,也就是说匿名函数也可以赋值给一个变量,通过变量来调用函数,其实这个特性在之前的案例中也使用到了:

    >>> f = lambda  x : x*x
    >>> f
    <function <lambda> at 0x0000020CE841E7A0>
    >>> f(22) 
    484
    
    #匿名函数也可以作为函数的返回值
    >>> def f(x,y):
    ...     return lambda: x * y    
    ... 
    >>> a = f(5,6) 
    >>> a()
    30
    

    引用一个案例

    - 利用匿名函数改造下面代码,使之更为简洁
    # -*- coding: utf-8 -*-
    def is_odd(n):
        return n % 2 == 1
    
    L = list(filter(is_odd, range(1, 20)))
    
    print(L)
    #输出:
    [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
    
    - 改造成匿名函数后:
    # -*- coding: utf-8 -*-
    L = list(filter(lambda x:x % 2 ==1, range(1, 20)))
    
    print(L)
    #输出:
    [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
    

    提示:Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。