Python(5)高阶函数之返回函数和匿名函数

简介: Python(5)高阶函数之返回函数和匿名函数

一、返回函数


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


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]


目录
相关文章
|
28天前
|
Python
Python之函数详解
【10月更文挑战第12天】
Python之函数详解
|
29天前
|
存储 数据安全/隐私保护 索引
|
18天前
|
测试技术 数据安全/隐私保护 Python
探索Python中的装饰器:简化和增强你的函数
【10月更文挑战第24天】在Python编程的海洋中,装饰器是那把可以令你的代码更简洁、更强大的魔法棒。它们不仅能够扩展函数的功能,还能保持代码的整洁性。本文将带你深入了解装饰器的概念、实现方式以及如何通过它们来提升你的代码质量。让我们一起揭开装饰器的神秘面纱,学习如何用它们来打造更加优雅和高效的代码。
|
20天前
|
弹性计算 安全 数据处理
Python高手秘籍:列表推导式与Lambda函数的高效应用
列表推导式和Lambda函数是Python中强大的工具。列表推导式允许在一行代码中生成新列表,而Lambda函数则是用于简单操作的匿名函数。通过示例展示了如何使用这些工具进行数据处理和功能实现,包括生成偶数平方、展平二维列表、按长度排序单词等。这些工具在Python编程中具有高度的灵活性和实用性。
|
23天前
|
Python
python的时间操作time-函数介绍
【10月更文挑战第19天】 python模块time的函数使用介绍和使用。
27 4
|
24天前
|
存储 Python
[oeasy]python038_ range函数_大小写字母的起止范围_start_stop
本文介绍了Python中`range`函数的使用方法及其在生成大小写字母序号范围时的应用。通过示例展示了如何利用`range`和`for`循环输出指定范围内的数字,重点讲解了小写和大写字母对应的ASCII码值范围,并解释了`range`函数的参数(start, stop)以及为何不包括stop值的原因。最后,文章留下了关于为何`range`不包含stop值的问题,留待下一次讨论。
18 1
|
24天前
|
安全 数据处理 数据安全/隐私保护
python中mod函数怎么用
通过这些实例,我们不仅掌握了Python中 `%`运算符的基础用法,还领略了它在解决实际问题中的灵活性和实用性。在诸如云计算服务提供商的技术栈中,类似的数学运算逻辑常被应用于数据处理、安全加密等关键领域,凸显了基础运算符在复杂系统中的不可或缺性。
18 0
|
3天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
3天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
3天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!