Python lambda函数的作用是什么?

2022年7月31日 340点热度 0人点赞 0条评论
1 引言

匿名函数是没有名字的函数,Python 中使用 lambda 关键字创建匿名函数。

可是,匿名函数的作用,或者说应用场景是什么呢?


实际上,lambda 表达式是函数式编程语言中的概念。

TinyDB 中也多次使用到了 lambda 表达式,本文中将借此详细介绍 lambda 表达式的使用。


2 lambda 函数

2.1 定义

Python 中支持匿名函数,不过相较于普通函数,匿名函数有很多限制,具体限制将在下文中介绍。

Python lambdas are little, anonymous functions, subject to a more restrictive but more concise syntax than regular Python functions.


匿名函数是没有名字的函数,Python 中使用 lambda 关键字创建匿名函数。

Taken literally, an anonymous function is a function without a name. In Python, an anonymous function is created with the lambda keyword. More loosely, it may or not be assigned a name.


注意 lambda 句法只是语法糖,与 def 关键字一样,lambda 表达式也会创建函数对象。

Python 简单的句法限制了 lambda 函数的定义体只能使用纯表达式。换句话说,lambda 函数中不能赋值,也不能使用 while 和 try 等 Python 语句。

lambda 可用于简化函数定义的方式,不过主要用于高阶函数。


实际上,lambda 表达式是函数式编程语言中的概念。


2.2 函数式编程

函数式编程是一种抽象程度很高的编程范式,函数式编程的核心思想是 stateless。

函数式编程要求函数有输入有输出,而且如果输入相同时,输出也相同,不会因为运行中的状态信息的不同而发生变化。

函数内部只需要关心定义输入数据和输出数据之间的关系,数学表达式里面其实是在做一种映射(mapping)。


函数式编程的特点主要包括:

  • stateless,无状态,处理输入数据的过程中数据不变;

  • immutable,不改变输入数据,每个函数都返回新数据集。


优点主要包括:

  • 没有状态就没有伤害

  • 并行执行无伤害

  • Copy-Paste 重构代码无伤害

  • 函数的执行没有顺序上的问题

因此,代码可以随意 copy,由于没有状态,相互之间没有影响,并行执行也不用锁(维护状态的锁)。

劣势是数据复制比较严重,不过由于并发度高,因此实际上反倒有可能提升性能,比如 Erlang。


函数式编程中用到的技术主要包括:

  • first class function(头等函数),因此函数可以像变量一样被创建、修改,并当成变量一样传递、返回,或是在函数中嵌套函数;

  • map & reduce,函数式编程最常见的技术就是对一个集合做 Map 和 Reduce 操作。如果是传统过程式语言,需要使用 for/while 循环,并创建临时变量,因此函数式编程在代码上要更容易阅读;

  • pipeline(管道),将函数实例成一个一个的 action,然后将一组 action 放到一个数组或是列表中,再把数据传给这个 action list,数据就像一个 pipeline 一样顺序地被各个函数所操作,最终得到我们想要的结果;

  • recursing(递归),递归最大的好处就是简化代码,它可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓;

  • currying(柯里化),将一个函数的多个参数分解成多个函数, 然后将函数多层封装起来,每层函数都返回一个函数去接收下一个参数,这可以简化函数的多个参数;

  • higher order function(高阶函数),所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象满天飞一样。这个技术用来做 Decorator 很不错。


下面分别举两个例子,一个用于表明纯函数的作用,另一个用于表明嵌套函数调用在函数式编程中的应用。


如下所示,由于函数内部更新全局变量,因此变量线程不安全。

// 非函数式,不是pure funciton,有状态int cnt;void increment(){    cnt++;}

如果改成纯函数,代码在并行时候不用锁,原因是复制了原有的数据,并返回了新的数据。

// 函数式,pure function, 无状态int increment(int cnt){    return cnt+1;}

函数式编程中单个函数需要写成纯函数的行式,那么多个函数之间的关系是什么呢?

由于函数式编程的函数都有输入有输出,因此可以直接使用嵌套函数。


如下所示是一条数据表达式。

(1 + 2) * 3 - 4

按照传统的过程式编程的思想,可能会这样实现。

var a = 1 + 2;var b = a * 3;var c = b - 4;

按照函数式编程的思想,会这样实现。

var result = subtract(multiply(add(1,2), 3), 4);

原因是函数式编程要求将运算过程尽量写成一系列嵌套的函数调用。


因此,函数式编程的理念是:

  • 把函数当成变量来用,关注描述问题而不是怎么实现,这样可以让代码更易读;

  • 因为函数返回里面的这个函数,所以函数关注的是表达式,关注的是描述这个问题,而不是怎么实现这个事情。


函数式编程的思想看起来很简单,但是支持并行执行的优秀特性给了程序开发无限的想象力,并因此被广泛应用于分布式系统。比如分布式平台 Hadoop 的两大核心组件分布式存储与分布式计算分别基于 HDFS 与 MapReduce 实现,用于在分布式系统中开发与运行处理海量数据的应用程序。

其中 MapReduce 是并行计算框架,本质上一种编程模型,核心思想是“分而治之”。具体分为三步,包括文件切分(split)、并行计算与结果汇总(merge)。从而将分布式并行编程抽象为两个原语操作,即map 操作和 reduce 操作,开发人员只需要简单地实现相应的接口即可,完全不用考虑底层数据流、容错、程序的并行执行等细节。


Python 并不是函数式编程语言(Functional Programming),但是对函数式编程提供部分支持。

1994 年,Python 中引入 map()、filter()、reduce() 与 lambda operator。

其中,lambda 表达式有输入有输出,仅支持表达式,是函数式编程的典型应用。


下面,我们回到 lambda 表达式中,首先介绍语法。


2.2 语法

2.2.1 三要素

lambda 表达式的语法格式为:

lambda arguments: expression

如下所示是一个普通函数 identity(),接收参数 x 并返回。

>>> def identity(x):...     return x


接下来,将普通函数转换成匿名函数。

>>> lambda x: x


从中可以看到,匿名函数的三要素包括:

  • 关键字,lambda

  • 变量,bound variable,x

  • 函数体,x


注意变量 x 属于 bound variable,与闭包中的自由变量(free variable)有区别,前者是向 lambda 表达式传入的参数,后者是内部函数访问的外部函数中的变量。


2.2.2 dis(lambda & def)

其实,到目前为止,还很难看出 lambda 函数与普通函数的关系,即相同点与不同点。

因此分别创建匿名函数与普通函数,并使用 dis 模块查看对应的字节码。


匿名函数对应的字节码如下所示。

>>> import dis>>> add_lambda = lambda x, y: x + y>>> type(add_lambda)<class 'function'>>>> dis.dis(add_lambda)  1           0 LOAD_FAST                0 (x)              2 LOAD_FAST                1 (y)              4 BINARY_ADD              6 RETURN_VALUE>>> add_lambda<function <lambda> at 0x00000201B792D0D0>

普通函数对应的字节码如下所示。

>>> def add_def(x, y): return x + y>>> type(add_def)<class 'function'>>>> dis.dis(add_def)  1           0 LOAD_FAST                0 (x)              2 LOAD_FAST                1 (y)              4 BINARY_ADD              6 RETURN_VALUE>>> add_def<function add_def at 0x00000201B7927E18>

对比可见,匿名函数与普通函数对应的字节码完全相同。而两者函数名不同,匿名函数的函数名统一为 lambda。

这样有什么影响吗?比如将导致匿名函数抛出异常时不便于定位到具体函数。


如下所示,匿名函数抛出异常,Traceback 中打印的函数名为 lambda。

>>> div_zero = lambda x: x / 0>>> div_zero(2)Traceback (most recent call last):    File "<stdin>", line 1, in <module>    File "<stdin>", line 1, in <lambda>ZeroDivisionError: division by zero


对比普通函数抛出异常,Traceback 中打印的函数名为 div_zero。

>>> def div_zero(x): return x / 0>>> div_zero(2)Traceback (most recent call last):    File "<stdin>", line 1, in <module>    File "<stdin>", line 1, in div_zeroZeroDivisionError: division by zero


2.2.3 语法特性

Python 中函数是一等对象,因此可以赋值给变量,匿名函数也可以。

如下所示,lambda 表达式可以赋值给对象。

>>> add_one = lambda x: x + 1>>> add_one(2)3


当然,如果不赋值给对象,匿名函数也可以直接调用,表达式两边需要使用括号。不过通常不建议直接调用匿名函数。

Another pattern used in other languages like JavaScript is to immediately execute a Python lambda function. This is known as an Immediately Invoked Function Expression (IIFE, pronounce “iffy”).
>>> (lambda x: x + 1)(2)3

上面的匿名函数中有一个参数,实际上也可以没有参数或有多个参数。


如下所示,匿名函数中没有参数。

>>> hello = lambda : "hello world">>> hello()'hello world'

匿名函数中有多个参数,其中多个参数两边不使用括号。

>>> full_name = lambda first, last: f'Full name: {first.title()} {last.title()}'>>> full_name('guido', 'van rossum')'Full name: Guido Van Rossum'


2.2.4 语法限制

匿名函数的语法限制主要包括:

  • 不支持语句,比如赋值语句;

  • 不支持多条表达式,仅支持单条表达式;

  • 不支持类型注解。


首先明确下语句与表达式的区别:

  • expression,表达式,仅支持标识符、字面量与运算符,生成至少一个值,因此只能作为右值;

  • statement,语句,通常由表达式组成,用一行或多行代码完成特定功能,比如赋值语句、if 语句、return 语句。


如下所示是语句与表达式的使用示例。

>>> x + 2         # an expression>>> x = 1         # a statement >>> y = x + 1     # a statement>>> print(y)      # a statement (in 2.x)2


如下所示,lambda 表达式中包含 return 语句时与类型注解时均报错语法错误。

>>> (lambda x: return x)(2)  File "<input>", line 1    (lambda x: return x)(2)                    ^SyntaxError: invalid syntax>>> >>> lambda x: int: x + 1 -> int  File "<input>", line 1    lambda x: int: x + 1 -> int                          ^SyntaxError: invalid syntax

2.3 使用

常见的使用方法包括:

  • 将匿名函数赋值给变量,像普通函数调用;

  • 将 lambda 嵌套到普通函数中,lambda 函数本身做为 return 的值;

  • 将 lambda 函数作为参数传递给其他函数。

其中,函数返回函数与将函数传给函数均满足高阶函数的定义。

实际上,除了作为参数传给高阶函数之外,Python 很少使用匿名函数。由于语法限制,lambda 表达式要么难以阅读,要么难以写出。

高阶函数(higher-order functions)是接收函数作为参数,或者把函数作为结果返回的函数。

Lambda functions are frequently used with higher-order functions, which take one or more functions as arguments or return one or more functions.

2.3.1 普通函数

将匿名函数赋值给变量,像普通函数调用。

>>> f = lambda x, y, z: x * y * z>>> f(2, 3, 4)24

对应的普通函数如下所示,可见不是高阶函数。这里的匿名函数仅起到了简化函数定义的作用。

def f(x, y, z):    return x * y * z


2.3.2 高阶函数-函数返回函数

将 lambda 嵌套到普通函数中,lambda 函数本身做为 return 的值,实际上就是 currying(柯里化)技术。

将一个函数的多个参数分解成多个函数, 然后将函数多层封装起来,每层函数都返回一个函数去接收下一个参数。

>>> def add(n):...    return lambda x: x + n>>> f = add(1)>>> f(2)3

注意 lambda 函数中引用了外部函数中的变量 n,因此满足闭包的定义。


闭包是指绑定了自由变量的函数,可以使用普通函数实现闭包,同样也可以使用 lambda 函数实现闭包。

闭包的核心特性是 retain state between function calls。

闭包函数与嵌套函数的区别在于闭包函数中要求外部函数返回内部函数。


实际上,一开始 Python 的 lambda 表达式并不支持闭包,因此在博客圈的函数式编程极客群体中,这个特性的名声并不好。Python 2.2(2001年12月发布)修正了这个问题,但是博客圈的固有印象不会轻易转变。自此之后,仅仅有语法上的差异,lambda 表达式一直处于尴尬的境地。


2.3.3 高阶函数-函数接收函数

将 lambda 函数作为参数传递给其他函数,比如参数列表中很适合使用匿名函数。


如下所示,高阶函数 high_ord_func 接收两个参数,其中一个是参数是函数。

def high_ord_func(x, func: Callable):    return x + func(x)

可以将其转换成 lambda 函数。

>>> high_ord_func = lambda x, func: x + func(x)>>> high_ord_func(2, lambda x: x * x)6>>> high_ord_func(2, lambda x: x + 3)7

不过这种用法还是比较刻意,更常见的用法比如将 lambda 函数传给 filter 函数用于实现偶数筛选。

>>> list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6]))[2, 4, 6]

3 适用场景

lambda 函数的可读性较差,因此经常存在争议,不过部分场景下适合使用 lambda 函数。

主要包括:

  • Classic Functional Constructs,如 map()、filter()、functools.reduce();

  • Key Functions,如 sort()、sorted() 。


3.1 Classic Functional Constructs

lambda 函数被广泛应用于内置函数如 map()、filter()、functools.reduce() 中。

Lambda functions are regularly used with the built-in functions map() and filter(), as well as functools.reduce(), exposed in the module functools.

其中:

  • map(function, iterable, ...),根据提供的函数对指定序列做映射;

  • filter(function, iterable),根据提供的函数对指定序列做过滤;

  • reduce(function, iterable[, initializer]),根据提供的函数对指定序列中两两相邻元素做累积;


三者的的相同点是函数中都传入函数与可迭代对象,区别在于输出结果的类型不同,map 输入输出长度相同,filter 输出长度小于等于输入,reduce 输出长度可以认为等于1。


注意对于 map 与 filter 函数,Python 2.x 版本返回的是列表,Python 3.x 返回的是迭代器。

# Python 2.x>>> from collections import Iterable, Iterator>>> isinstance(map(lambda x: x.upper(), ['cat', 'dog', 'cow']), Iterable)True>>> isinstance(map(lambda x: x.upper(), ['cat', 'dog', 'cow']), Iterator)False
# Python 3.x>>> from collections import Iterable, Iterator>>> isinstance(map(lambda x: x.upper(), ['cat', 'dog', 'cow']), Iterable)True>>> isinstance(map(lambda x: x.upper(), ['cat', 'dog', 'cow']), Iterator)True

如下所示,分别调用 map、filter、reduce 函数。

# mapping list by function>>> list(map(lambda x: x.upper(), ['cat', 'dog', 'cow']))['CAT', 'DOG', 'COW']
# filter list by function>>> list(filter(lambda x: 'o' in x, ['cat', 'dog', 'cow']))['dog', 'cow']
# aggregate list by function>>> from functools import reduce>>> reduce(lambda acc, x: f'{acc} | {x}', ['cat', 'dog', 'cow'])'cat | dog | cow'

实际上,map、filter、reduce 函数通常可以转换成列表推导式或生成器表达式。

Higher-order functions like map(), filter(), and functools.reduce() can be converted to more elegant forms with slight twists of creativity, in particular with list comprehensions or generator expressions.

如下所示,将 map、filter 转换成列表推导式。

# convert to list comprehension>>> [x.upper() for x in ['cat', 'dog', 'cow']]['CAT', 'DOG', 'COW']>>> [x for x in ['cat', 'dog', 'cow'] if 'o' in x]['dog', 'cow']


当 reduce 中的函数是 sum()、min()、max() 时,通常可以转换成生成器表达式。

Generator expressions are especially useful with functions like sum(), min(), and max() that reduce an iterable input to a single value.
>>> reduce(lambda x, y: x + y, [3, 1, 2])6# convert to generator expression>>> sum(x for x in [3, 1, 2])6


列表推导式与生成器表达式的主要区别如下表所示。

类型

列表推导式

生成器表达式

语法

[]

()

返回值类型

iterable

lazy iterator

内存使用

creates all elements right away

creates a single element based on request

备注:生成器包括生成器表达式与生成器函数,其中生成器函数是包含 yield 关键字的函数。


使用示例如下所示。

>>> square_list = [n** 2 for n in range(5)]>>> square_list[0, 1, 4, 9, 16]>>> square_generator = (n** 2 for n in range(5))>>> square_generator<generator object <genexpr> at 0x00000286B8671F90>


3.2 Key Functions

Python 中的 Key Functions 指的是接收命名参数 key 的高阶函数,Key Functions 可以是 lambda 函数。

Key functions in Python are higher-order functions that take a parameter key as a named argument. key receives a function that can be a lambda.

常见的 Key Functions 如 sort()、sorted() 。

其中:

  • sort(key, reverse),根据提供的函数对调用列表做排序;

  • sorted(iterable[, key[, reverse]]),根据提供的函数对指定序列做排序。


两个函数的参数都是 key 与 reverse,其中:

  • key 用于指定进行比较的元素,只有一个参数,用于指定可迭代对象中的一个元素进行排序;

  • reverse 用于指定排序规则,reverse = True 降序 , reverse = False 升序(默认)。


注意 sort 是 list 的一个方法,用于进行原地排序,sorted 可以对所有可迭代对象排序,并返回新的可迭代对象。

如下所示,分别使用 sort 与 sorted 函数对 ss 序列根据 age 字段进行排序。

>>> ss = [(10, "Tom", ), (8, "Jack"), (5, "David")]# order by age (default desc)>>> ss.sort(key=lambda x: x[1])>>> ss[(5, 'David'), (8, 'Jack'), (10, 'Tom')]>>> sorted(ss, key=lambda x: x[1], reverse=True)[(10, 'Tom'), (8, 'Jack'), (5, 'David')]

4 TinyDB

TinyDB 的 Query 模块用于处理查询条件,其中多次使用 lambda 函数。

在具体介绍 lambda 语句之前,首先介绍下查询流程,便于理解使用 lambda 表达式的作用。


4.1 查询流程

数据查询有两个变量,包括表中的数据与查询条件,然后判断数据是否满足查询条件。

search(cond) 方法中遍历指定表的全部记录,依次执行 cond(doc) 函数进行条件过滤。

cond(doc) 函数中 cond 对应查询条件,doc 对应数据。


因此,可以将执行流程分为三步:

  • 解析查询条件

  • 获取数据

  • 判断数据是否满足查询条件


以如下查询语句为例。

>>> table.search(User.name == 'shawn')


具体实现是将一条语句的执行拆分为多次函数调用,完整的查询操作对应的流程图如下所示。

图片


函数调用包括:

  • 将查询条件 User.name == 'shawn' 转换成查询函数 lambda value: value == 'shawn',并将其保存在 QueryInstance 对象中。注意现在还并不知道具体表的数据;

  • 获取要查询的数据表,然后调用  cond(doc) 方法判断是否满足查询条件;

  • 由于 QueryInstance 类实现了 __call__() 方法,因此实际是传入表中数据并调用查询函数 lambda value: value == 'shawn'(doc.name)。


因此,TinyDB 的 Query 模块中将查询函数的多个参数分解成多个函数,每层函数都返回一个函数去接收下一个参数,正是 currying(柯里化)技术的应用。比如一个参数接收查询条件,一个参数用于接收表中数据。要将参数分解的原因是将执行过程拆解,按照顺序传入参数。


下面分别介绍其中的 __eq__() 与 _generate_test() 方法。


4.2 实现

4.2.1 __eq__

__eq__() 方法中调用 _generate_test() 方法并返回,其中声明匿名函数进行等值判断。

而 _generate_test() 是高阶函数,其中接收函数作为参数。

class Query(QueryInstance):    def __eq__(self, rhs: Any):        return self._generate_test(            lambda value: value == rhs,            ('==', self._path, freeze(rhs))        )


如下所示,高阶函数 _generate_test 参数中的匿名函数 lambda value: value == rhs 可以使用普通函数即 __eq__() 方法中的嵌套函数替代。

def __eq__(self, rhs: Any):    def temp(value):        return value == rhs
return self._generate_test( temp, ('==', self._path, freeze(rhs)) )

嵌套函数的核心特性是内部函数可以在外部函数返回后访问到外部函数中的局部变量,因此 lambda 函数可以访问到 __eq__() 方法中的局部变量 rhs。当外部传入 value 后,就可以判断指定记录是否满足查询条件了。


4.2.2 _generate_test

_generate_test() 方法中输入查询函数,返回 QueryInstance 对象,其中声明一个匿名函数用于调用嵌套函数 runner()。

而 runner() 函数中最终调用传入的 test() 函数进行条件判断。

def _generate_test(        self,        test: Callable[[Any], bool],        hashval: Tuple,        allow_empty_path: bool = False) -> QueryInstance:        def runner(value):        ...        # Perform the specified test        return test(value)
return QueryInstance( lambda value: runner(value), (hashval if self.is_cacheable() else None) )

5 结论

匿名函数是没有名字的函数,Python 中使用 lambda 关键字创建匿名函数。

lambda 表达式是函数式编程语言中的概念。


普通的匿名函数仅起到简化函数定义的作用,因此 Python 中匿名函数主要用于作为参数传给高阶函数。

实际上,由于语法限制,lambda 表达式要么难以阅读,要么难以写出。

高阶函数(higher-order functions)是接收函数作为参数,或者把函数作为结果返回的函数。


匿名函数的使用示例如:

  • Classic Functional Constructs,如 map()、filter()、functools.reduce(),不过通常可以转换成列表推导式或生成器表达式;

  • Key Functions,如 sort()、sorted() 。


TinyDB 的 Query 模块用于处理查询条件,其中多次使用 lambda 函数。

将数据查询操作抽象成 cond(doc) 函数,其中包括两个变量,cond 对应查询条件,doc 对应数据。


通过将查询函数的多个参数分解成多个函数,每层函数都返回一个函数去接收下一个参数,正是 currying(柯里化)技术的应用。比如一个参数接收查询条件,一个参数用于接收表中数据。要将参数分解的原因是将执行过程拆解,按照顺序传入参数。


6 待办

  • 函数式编程


7 小技巧

7.1 函数式编程

函数式编程是一种抽象程度很高的编程范式,函数式编程的核心思想是 stateless

函数式编程要求函数有输入有输出,而且如果输入相同时,输出也相同,不会因为运行中的状态信息的不同而发生变化。

函数内部只需要关心定义输入数据和输出数据之间的关系,数学表达式里面其实是在做一种映射(mapping)。


函数式编程的思想看起来很简单,但是支持并行执行的优秀特性给了程序开发无限的想象力,并因此被广泛应用于分布式系统。比如分布式平台 Hadoop 中的并行计算框架 MapReduce,本质上一种编程模型,核心思想是“分而治之”。具体分为三步,包括文件切分(split)、并行计算与结果汇总(merge)。


注意函数式编程是一种编程思想,比如将一个函数的多个参数分解成多个函数(currying), 然后将函数多层封装起来,每层函数都返回一个函数去接收下一个参数,这可以简化函数的多个参数。


比如之前介绍嵌套函数的使用时举例 generate_power() 函数,实际上就是 currying 技术的应用,

def generate_power(exponent):    def power(base):        return base ** exponent    return power

generate_power() 函数返回 power() 函数,因此可以用 generate_power() 函数构造各种版本的 generate_power 函数,根据闭包机制其中可以保存对应的自由变量。


当然,也可以将嵌套函数中的 power() 函数转换成匿名函数。

def generate_power_lambda(exponent):    return lambda base: base ** exponent


7.2 map 在线程池中的应用

Python 中线程池  concurrent.futures.ThreadPoolExecutor 获取执行结果的方式有三种,包括 直接调用 result()、调用 as_completed()、调用 map()。

其中 map 方法与 Python 标准库中的 map 含义相同,都是给可迭代对象中的每个元素都执行同一个函数。


map 方法与 as_completed 方法的相同点在于返回值都是一个迭代器。

区别主要有以下三点:

  • map 方法中自动调用 submit 方法,因此无需提前使用 submit 方法;

  • map 方法中自动调用 result 方法,返回值是 result 的迭代器,而 as_completed 方法的返回值是 Future 实例对象的迭代器;

  • map 方法中任务返回的顺序与任务提交的顺序一致,而 as_completed 方法中任务返回的顺序与任务提交的顺序可能不一致。


三种方式的使用示例分别如下所示。

直接调用 result() 时主线程阻塞直到任务执行结束。

from concurrent.futures import ThreadPoolExecutorfrom my_thread.tasks import get_html
def main(): # 线程池初始化 executor = ThreadPoolExecutor(max_workers=2) # 通过 submit 函数提交要执行的函数到线程池中,submit函数立即返回,不阻塞 task1 = executor.submit(get_html, 3) task2 = executor.submit(get_html, 2) task3 = executor.submit(get_html, 2) # 主线程阻塞直到任务执行结束 print(task1.result()) print(task2.result()) print(task3.result())

调用 as_completed() 方法返回一个 Future 实例对象的迭代器,可以一次取出所有任务的结果。

from concurrent.futures import as_completed
def main(): pool = ThreadPoolExecutor(max_workers=2) urls = [3, 2, 4] all_task = [pool.submit(get_html, url) for url in urls]
for future in as_completed(all_task): data = future.result() print("in main thread, result={}".format(data))

调用 map() 时,自动调用 submit() 与 result() 方法,因此代码是最简洁的。

def main():    pool = ThreadPoolExecutor(max_workers=2)    urls = [3, 2, 4]    for result in pool.map(get_html, urls):        print(result)

可见通过 map() 方法可以简化并行代码的实现


实际上 map() 方法中显式调用 submit() 方法,并调用 result() 方法异步返回执行结果。

def map(self, fn, *iterables, timeout=None, chunksize=1):    """Returns an iterator equivalent to map(fn, iter).    """
fs = [self.submit(fn, *args) for args in zip(*iterables)]
def result_iterator(): try: # reverse to keep finishing order fs.reverse() while fs: # Careful not to keep a reference to the popped future if timeout is None: yield fs.pop().result() else: yield fs.pop().result(end_time - time.time()) finally: for future in fs: future.cancel() return result_iterator()

参考教程

  • How to Use Python Lambda Functions

https://realpython.com/python-lambda/#alternatives-to-lambdas

  • Lambda Function in Python – How and When to use?

https://www.machinelearningplus.com/python/lambda-function/#3.-What-is-the-need-for-Lambda-Functions-in-Python?

  • 左耳听风:编程范式游记(4)- 函数式编程

https://time.geekbang.org/column/article/2711

  • 阮一峰:函数式编程初探

https://www.ruanyifeng.com/blog/2012/04/functional_programming.html

  • python中lambda的用法

https://blog.csdn.net/qq_40089648/article/details/89022804

  • Python 之 lambda 函数完整详解 & 巧妙运用

https://blog.csdn.net/PY0312/article/details/88956795

74270Python lambda函数的作用是什么?

这个人很懒,什么都没留下

文章评论