函数式编程通常在 Python 代码中扮演相当小的角色。但熟悉它是件好事。至少,在阅读其他人编写的代码时,您可能会时不时地遇到它。您甚至可能会发现在您自己的代码中使用 Python 的函数式编程功能是有利的。
Python 对函数式编程的支持程度如何?
为了支持函数式编程,如果功能 在给定的编程语言中具有两种能力:
将另一个函数作为参数
将另一个函数返回给其调用者
Python 在这两方面都表现得很好。正如您在本系列之前所了解到的,Python 程序中的一切都是对象 。 Python 中的所有对象都具有或多或少相同的地位,函数也不例外。
在Python中,函数是一等公民 。这意味着函数与值具有相同的特征,例如字符串 和数字 。您希望能够使用字符串或数字执行的任何操作也可以使用函数执行。
例如,您可以将函数分配给变量。然后,您可以像使用函数本身一样使用该变量:
>>> 1 >>> def func ():
2 ... print ( "I am function func()!" )
3 ...
4
5 >>> func ()
6 I am function func()!
7
8 >>> another_name = func
9 >>> another_name ()
10 I am function func()!
分配another_name = func
第 8 行创建了一个新的引用func()
命名的another_name
。然后您可以通过任一名称调用该函数,func
或者another_name
,如第 5 行和第 9 行所示。
您可以使用以下命令向控制台显示函数打印() ,将其作为元素包含在复合数据对象中,例如列表 ,或者甚至将其用作字典 钥匙:
>>> >>> def func ():
... print ( "I am function func()!" )
...
>>> print ( "cat" , func , 42 )
cat <function func at 0x7f81b4d29bf8> 42
>>> objects = [ "cat" , func , 42 ]
>>> objects [ 1 ]
<function func at 0x7f81b4d29bf8>
>>> objects [ 1 ]()
I am function func()!
>>> d = { "cat" : 1 , func : 2 , 42 : 3 }
>>> d [ func ]
2
在这个例子中,func()
出现在与值相同的上下文中"cat"
和42
,解释器处理得很好。
笔记: 在 Python 中你能或不能对任何对象做什么在某种程度上取决于上下文。例如,有些操作适用于某些对象类型,但不适用于其他对象类型。
您可以添加两个整数对象或连接 两个带有加号运算符的字符串对象 (+
)。但加号运算符没有为函数对象定义。
就目前而言,重要的是 Python 中的函数满足上面列出的有利于函数式编程的两个标准。您可以将一个函数作为参数传递给另一个函数:
>>> 1 >>> def inner ():
2 ... print ( "I am function inner()!" )
3 ...
4
5 >>> def outer ( function ):
6 ... function ()
7 ...
8
9 >>> outer ( inner )
10 I am function inner()!
这是上面示例中发生的情况:
9号线呼叫通过inner()
作为一个论点outer()
.
之内outer()
, Python 绑定inner()
到函数参数function
.
outer()
然后可以打电话inner()
直接通过function
.
这被称为功能组合 .
技术说明: Python 提供了一种快捷表示法,称为装饰者 以便于将一个函数包装在另一个函数中。欲了解更多信息,请查看Python 装饰器入门 .
当您将一个函数传递给另一个函数时,传入的函数有时被称为打回来 因为一个打回来 到内部函数 可以修改外部函数的行为。
Python 函数就是一个很好的例子排序() 。通常,如果将字符串值列表传递给sorted()
,然后按词汇顺序对它们进行排序:
>>> >>> animals = [ "ferret" , "vole" , "dog" , "gecko" ]
>>> sorted ( animals )
['dog', 'ferret', 'gecko', 'vole']
然而,sorted()
需要一个可选的key
指定可用作排序键的回调函数的参数。因此,例如,您可以按字符串长度排序:
>>> >>> animals = [ "ferret" , "vole" , "dog" , "gecko" ]
>>> sorted ( animals , key = len )
['dog', 'vole', 'gecko', 'ferret']
sorted()
还可以采用一个可选参数来指定按相反顺序排序。但是您可以通过定义自己的回调函数来管理同样的事情,该函数反转了len()
:
>>> >>> animals = [ "ferret" , "vole" , "dog" , "gecko" ]
>>> sorted ( animals , key = len , reverse = True )
['ferret', 'gecko', 'vole', 'dog']
>>> def reverse_len ( s ):
... return - len ( s )
...
>>> sorted ( animals , key = reverse_len )
['ferret', 'gecko', 'vole', 'dog']
您可以查看如何在Python中使用sorted()和.sort() 有关在 Python 中对数据进行排序的更多信息。
正如您可以将一个函数作为参数传递给另一个函数一样,函数也可以指定另一个函数作为其返回值:
>>> 1 >>> def outer ():
2 ... def inner ():
3 ... print ( "I am function inner()!" )
4 ...
5 ... # Function outer() returns function inner()
6 ... return inner
7 ...
8
9 >>> function = outer ()
10 >>> function
11 <function outer.<locals>.inner at 0x7f18bc85faf0>
12 >>> function ()
13 I am function inner()!
14
15 >>> outer ()()
16 I am function inner()!
本例中发生的情况如下:
第 2 至 3 行: outer()
定义局部函数inner()
.
6号线: outer()
通过inner()
返回作为其返回值。
9号线: 返回值来自outer()
被分配给变量function
.
之后,您可以致电inner()
间接通过function
,如第 12 行所示。您还可以使用返回值间接调用它outer()
没有中间赋值,如第 15 行所示。
正如您所看到的,Python 已经具备了很好地支持函数式编程的功能。不过,在进入函数式代码之前,还有一个概念将有助于您探索:拉姆达表达式 .
定义匿名函数lambda
函数式编程就是调用函数并传递它们,因此它自然涉及定义很多函数。您始终可以使用通常的方式定义函数def 正如您在本系列之前的教程中看到的那样。
但有时,能够定义一个匿名函数 即时运行,无需为其命名。在 Python 中,您可以使用lambda
表达。
技术说明: 期限拉姆达 来自拉姆达演算 ,一种数学逻辑的形式系统,用于表达基于函数抽象和应用的计算。
的语法lambda
表达式如下:
lambda < parameter_list > : < expression >
下表总结了a的各个部分lambda
表达:
Component
Meaning
lambda
The keyword that introduces a lambda
expression
<parameter_list>
An optional comma-separated list of parameter names
:
Punctuation that separates <parameter_list>
from <expression>
<expression>
An expression usually involving the names in <parameter_list>
的值lambda
表达式是一个可调用函数,就像用def
关键词。它接受参数,如指定的那样<parameter_list>
,并返回一个值,如<expression>
.
这是第一个快速示例:
>>> 1 >>> lambda s : s [:: - 1 ]
2 <function <lambda> at 0x7fef8b452e18>
3
4 >>> callable ( lambda s : s [:: - 1 ])
5 True
第 1 行的语句只是lambda
本身的表达。在第 2 行,Python 显示了表达式的值,您可以看到它是一个函数。
内置Python函数可调用() 回报真的 如果传递给它的参数看起来是可调用的并且错误的 否则。第 4 行和第 5 行显示返回的值lambda
表达式实际上是可调用的,就像函数应该的那样。
在这种情况下,参数列表由单个参数组成s
。后续的表达式s[::-1]
切片语法是以相反顺序返回 s 中的字符 。所以这lambda
expression 定义一个临时的、无名的函数,它接受一个字符串参数并返回字符反转的参数字符串。
由 a 创建的对象lambda
表达式是一等公民,就像标准函数或 Python 中的任何其他对象一样。您可以将其分配给变量,然后使用该名称调用该函数:
>>> >>> reverse = lambda s : s [:: - 1 ]
>>> reverse ( "I am a string" )
'gnirts a ma I'
这在功能上——没有双关语——相当于定义reverse()
与def
关键词:
>>> 1 >>> def reverse ( s ):
2 ... return s [:: - 1 ]
3 ...
4 >>> reverse ( "I am a string" )
5 'gnirts a ma I'
6
7 >>> reverse = lambda s : s [:: - 1 ]
8 >>> reverse ( "I am a string" )
9 'gnirts a ma I'
上面第 4 行和第 8 行的调用行为相同。
但是,没有必要将变量分配给lambda
调用之前的表达式。您还可以调用由lambda
直接表达:
>>> >>> ( lambda s : s [:: - 1 ])( "I am a string" )
'gnirts a ma I'
这是另一个例子:
>>> >>> ( lambda x1 , x2 , x3 : ( x1 + x2 + x3 ) / 3 )( 9 , 6 , 6 )
7.0
>>> ( lambda x1 , x2 , x3 : ( x1 + x2 + x3 ) / 3 )( 1.4 , 1.1 , 0.5 )
1.0
在这种情况下,参数是x1
, x2
, 和x3
,表达式为x1 + x2 + x3 / 3
。这是匿名的lambda
函数计算三个数的平均值。
作为另一个例子,回想一下上面定义的reverse_len()
作为回调函数sorted()
:
>>> >>> animals = [ "ferret" , "vole" , "dog" , "gecko" ]
>>> def reverse_len ( s ):
... return - len ( s )
...
>>> sorted ( animals , key = reverse_len )
['ferret', 'gecko', 'vole', 'dog']
你可以使用lambda
函数也在这里:
>>> >>> animals = [ "ferret" , "vole" , "dog" , "gecko" ]
>>> sorted ( animals , key = lambda s : - len ( s ))
['ferret', 'gecko', 'vole', 'dog']
A lambda
表达式通常会有一个参数列表,但这不是必需的。您可以定义一个lambda
不带参数的函数。返回值不依赖于任何输入参数:
>>> >>> forty_two_producer = lambda : 42
>>> forty_two_producer ()
42
请注意,您只能使用以下命令定义相当基本的函数lambda
。来自 a 的返回值lambda
表达式只能是一个表达式。 Alambda
表达式不能包含诸如赋值或返回 ,也不能包含控制结构,例如for , 尽管 , if , 别的 , 或者def .
您在之前的教程中了解到定义 Python 函数 定义了一个函数def
可以有效地返回多个值。如果一个return
函数中的语句包含多个逗号分隔的值,然后 Python 将它们打包并将它们作为元组 :
>>> >>> def func ( x ):
... return x , x ** 2 , x ** 3
...
>>> func ( 3 )
(3, 9, 27)
这种隐式元组打包不适用于匿名lambda
功能:
>>> >>> ( lambda x : x , x ** 2 , x ** 3 )( 3 )
<stdin>:1: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
NameError : name 'x' is not defined
但是你可以从 a 返回一个元组lambda
功能。您只需用括号明确表示元组即可。您还可以从返回列表或字典lambda
功能:
>>> >>> ( lambda x : ( x , x ** 2 , x ** 3 ))( 3 )
(3, 9, 27)
>>> ( lambda x : [ x , x ** 2 , x ** 3 ])( 3 )
[3, 9, 27]
>>> ( lambda x : { 1 : x , 2 : x ** 2 , 3 : x ** 3 })( 3 )
{1: 3, 2: 9, 3: 27}
A lambda
表达式有自己的局部名称空间 ,因此参数名称不会与全局命名空间中的相同名称冲突。 Alambda
表达式可以访问全局命名空间中的变量,但不能修改它们。
最后还有一个奇怪的现象需要注意。如果您发现需要包含lambda
表达式为格式化字符串文字(f 字符串) ,那么您需要将其括在显式括号中:
>>> >>> print ( f "--- { lambda s : s[::-1] } ---" )
File "<stdin>" , line 1
( lambda s )
^
SyntaxError : f-string: invalid syntax
>>> print ( f "--- { ( lambda s : s [:: - 1 ]) } ---" )
--- <function <lambda> at 0x7f97b775fa60> ---
>>> print ( f "--- { ( lambda s : s [:: - 1 ])( 'I am a string' ) } ---" )
--- gnirts a ma I ---
现在您知道如何定义匿名函数了lambda
。如需进一步阅读lambda
功能,查看如何使用 Python Lambda 函数 .
接下来,是时候深入研究 Python 中的函数式编程了。你会看到如何lambda
编写函数代码时,函数特别方便。
Python提供了两个内置函数,地图() 和筛选() ,符合函数式编程范式。三分之一,减少() ,不再是核心语言的一部分,但仍然可以从名为functools
。这三个函数中的每一个都将另一个函数作为其参数之一。
将函数应用于可迭代对象map()
待办事项中的第一个功能是map()
,这是一个 Python 内置函数。和map()
,您可以将函数应用于可迭代的 依次,并且map()
将返回一个迭代器 从而产生结果。这可以允许一些非常简洁的代码,因为map()
语句通常可以代替显式循环。
呼唤map()
使用单个可迭代对象
调用语法map()
在单个可迭代上看起来像这样:
map(<f>, <iterable>)
返回迭代器,产生应用函数的结果<f>
对每个元素<iterable>
.
这是一个例子。假设你已经定义了reverse()
,一个接受字符串参数并返回其相反值的函数,使用你的老朋友[::-1]
字符串切片机制:
>>> >>> def reverse ( s ):
... return s [:: - 1 ]
...
>>> reverse ( "I am a string" )
'gnirts a ma I'
如果你有一个字符串列表,那么你可以使用map()
申请reverse()
列表中的每个元素:
>>> >>> animals = [ "cat" , "dog" , "hedgehog" , "gecko" ]
>>> iterator = map ( reverse , animals )
>>> iterator
<map object at 0x7fd3558cbef0>
但要记住,map()
不返回列表。它返回一个迭代器 称为地图对象 。要从迭代器获取值,您需要迭代 它或使用list()
:
>>> >>> iterator = map ( reverse , animals )
>>> for i in iterator :
... print ( i )
...
tac
god
gohegdeh
okceg
>>> iterator = map ( reverse , animals )
>>> list ( iterator )
['tac', 'god', 'gohegdeh', 'okceg']
迭代iterator
产生原始列表中的项目animals
,每个字符串反转reverse()
.
在这个例子中,reverse()
是一个非常短的函数,除了这个用途之外你可能不需要这个函数map()
。您可以使用匿名函数,而不是用一次性函数使代码变得混乱lambda
函数代替:
>>> >>> animals = [ "cat" , "dog" , "hedgehog" , "gecko" ]
>>> iterator = map ( lambda s : s [:: - 1 ], animals )
>>> list ( iterator )
['tac', 'god', 'gohegdeh', 'okceg']
>>> # Combining it all into one line:
>>> list ( map ( lambda s : s [:: - 1 ], [ "cat" , "dog" , "hedgehog" , "gecko" ]))
['tac', 'god', 'gohegdeh', 'okceg']
如果可迭代对象包含不适合指定函数的项,则 Python 会引发例外 :
>>> >>> list ( map ( lambda s : s [:: - 1 ], [ "cat" , "dog" , 3.14159 , "gecko" ]))
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
File "<stdin>" , line 1 , in <lambda>
TypeError : 'float' object is not subscriptable
在这种情况下,lambda
函数需要一个字符串参数,它会尝试对其进行切片。列表中的第二个元素,3.14159
, 是一个float
对象,不可切片。所以一个类型错误 发生。
这是一个更真实的示例:在教程部分内置字符串方法 ,你遇到了str.join()
,它连接来自可迭代对象的字符串,并用指定的字符串分隔:
>>> >>> "+" . join ([ "cat" , "dog" , "hedgehog" , "gecko" ])
'cat+dog+hedgehog+gecko'
如果列表中的对象是字符串,则此方法可以正常工作。如果不是,那么str.join()
提出一个TypeError
例外:
>>> >>> "+" . join ([ 1 , 2 , 3 , 4 , 5 ])
Traceback (most recent call last):
File "<stdin>" , line 1 , in <module>
TypeError : sequence item 0: expected str instance, int found
解决这个问题的一种方法是使用循环。用一个for
循环中,您可以创建一个新列表,其中包含原始列表中数字的字符串表示形式。然后你可以将新列表传递给.join()
:
>>> >>> strings = []
>>> for i in [ 1 , 2 , 3 , 4 , 5 ]:
... strings . append ( str ( i ))
...
>>> strings
['1', '2', '3', '4', '5']
>>> "+" . join ( strings )
'1+2+3+4+5'
然而,因为map()
依次将函数应用于列表中的每个对象,通常可以消除对显式循环的需要。在这种情况下,您可以使用map()
申请str()
在加入对象之前先列出对象:
>>> >>> "+" . join ( map ( str , [ 1 , 2 , 3 , 4 , 5 ]))
'1+2+3+4+5'
map(str, [1, 2, 3, 4, 5])
返回一个生成字符串对象列表的迭代器["1", "2", "3", "4", "5"]
,然后您可以成功将该列表传递给.join()
.
虽然map()
达到了上面例子中想要的效果,那就更Pythonic 使用一个列表理解 在这种情况下替换显式循环。
呼唤map()
具有多个可迭代对象
还有另一种形式map()
这需要多个可迭代参数:
map ( < f > , < iterable ₁ > , < iterable ₂ > , ... , < iterableₙ > )
map(<f>, <iterable
1
>, <iterable
2
>, ..., <iterable
n
>)
applies <f>
to the elements in each <iterable
i
>
in parallel and returns an iterator that yields the results.
The number of <iterable
i
>
arguments specified to map()
must match the number of arguments that <f>
expects. <f>
acts on the first item of each <iterable
i
>
, and that result becomes the first item that the return iterator yields. Then <f>
acts on the second item in each <iterable
i
>
, and that becomes the second yielded item, and so on.
一个例子应该有助于澄清:
>>> >>> def f ( a , b , c ):
... return a + b + c
...
>>> list ( map ( f , [ 1 , 2 , 3 ], [ 10 , 20 , 30 ], [ 100 , 200 , 300 ]))
[111, 222, 333]
在这种情况下,f()
需要三个参数。相应地,有三个可迭代的参数map()
: 列表[1, 2, 3]
, [10, 20, 30]
, 和[100, 200, 300]
.
返回的第一项是申请的结果f()
到每个列表中的第一个元素:f(1, 10, 100)
。返回的第二项是f(2, 20, 200)
,第三个是f(3, 30, 300)
,如下图所示:
返回值来自map()
是一个产生列表的迭代器[111, 222, 333]
.
再次在这种情况下,因为f()
是如此之短,您可以轻松地将其替换为lambda
函数代替:
>>> >>> list (
... map (
... ( lambda a , b , c : a + b + c ),
... [ 1 , 2 , 3 ],
... [ 10 , 20 , 30 ],
... [ 100 , 200 , 300 ]
... )
... )
此示例使用额外的括号lambda
功能和隐式续行 。两者都不是必需的,但它们有助于使代码更易于阅读。
从可迭代对象中选择元素filter()
filter()
允许您根据给定函数的评估从可迭代对象中选择或过滤项目。其调用方式如下:
filter(<f>, <iterable>)
应用功能<f>
对每个元素<iterable>
并返回一个迭代器,该迭代器产生所有项目<f>
是真实的 。相反,它会过滤掉所有符合以下条件的项目:<f>
是假的。
在下面的示例中,greater_than_100(x)
是真的,如果x > 100
:
>>> >>> def greater_than_100 ( x ):
... return x > 100
...
>>> list ( filter ( greater_than_100 , [ 1 , 111 , 2 , 222 , 3 , 333 ]))
[111, 222, 333]
在这种情况下,greater_than_100()
对于项目来说是真实的111
, 222
, 和333
,所以这些项目仍然存在,而1
, 2
, 和3
被丢弃。与前面的示例一样,greater_than_100()
是一个短函数,您可以将其替换为lambda
表达式代替:
>>> >>> list ( filter ( lambda x : x > 100 , [ 1 , 111 , 2 , 222 , 3 , 333 ]))
[111, 222, 333]
下一个示例的特点范围() . range(n)
产生一个迭代器,它产生整数0
到n - 1
。下面的例子使用filter()
从列表中仅选择偶数并过滤掉奇数:
>>> >>> list ( range ( 10 ))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> def is_even ( x ):
... return x % 2 == 0
...
>>> list ( filter ( is_even , range ( 10 )))
[0, 2, 4, 6, 8]
>>> list ( filter ( lambda x : x % 2 == 0 , range ( 10 )))
[0, 2, 4, 6, 8]
这是使用内置字符串方法的示例:
>>> >>> animals = [ "cat" , "Cat" , "CAT" , "dog" , "Dog" , "DOG" , "emu" , "Emu" , "EMU" ]
>>> def all_caps ( s ):
... return s . isupper ()
...
>>> list ( filter ( all_caps , animals ))
['CAT', 'DOG', 'EMU']
>>> list ( filter ( lambda s : s . isupper (), animals ))
['CAT', 'DOG', 'EMU']
还记得上一篇教程中的字符串方法 那s.isupper()
回报True
如果所有字母字符s
是大写的并且False
否则。
将可迭代对象减少为单个值reduce()
reduce()
一次将函数应用于可迭代的两个项目,逐步组合它们以产生单个结果。
reduce()
曾经是Python中的内置函数。吉多·范罗苏姆 显然相当不喜欢reduce()
并主张将其从语言中完全删除。以下是他对此的看法:
所以现在reduce()
。这实际上是我一直最讨厌的,因为,除了几个涉及的例子之外+
或者*
,几乎每次我看到reduce()
当我使用一个不平凡的函数参数进行调用时,我需要拿起笔和纸来绘制出实际输入到该函数中的内容,然后才能理解该函数的含义。reduce()
应该做的。所以在我看来,适用性reduce()
几乎仅限于关联运算符,在所有其他情况下,最好明确地写出累积循环。 (来源 )
吉多实际上主张消除所有这三个reduce()
, map()
, 和filter()
来自Python。人们只能猜测他的理由。碰巧的是,前面提到的列表理解 涵盖了所有这些函数提供的功能以及更多。您可以通过阅读了解更多信息何时在 Python 中使用列表推导式 .
正如你所看到的,map()
和filter()
保留 Python 中的内置函数。reduce()
不再是内置函数,但可用于进口 来自标准库模块,如下所示。
使用reduce()
,您需要从名为的模块导入它functools
。这可以通过多种方式实现,但以下是最简单的:
from functools import reduce
接下来,口译员将reduce()
进入全局命名空间 并使其可供使用。您将在下面看到的示例假设情况确实如此。
呼唤reduce()
有两个参数
最直接的reduce()
call 需要一个函数和一个可迭代对象,如下所示:
reduce(<f>, <iterable>)
用途<f>
,它必须是一个只接受两个参数的函数,以逐步组合中的元素<iterable>
。开始,reduce()
调用<f>
的前两个元素<iterable>
。然后将该结果与第三个元素组合,然后将该结果与第四个元素组合,依此类推,直到列表耗尽。然后reduce()
返回最终结果。
Guido 说得对,他说的最直接的应用reduce()
是那些使用结合运算符 。让我们从加号运算符开始(+
):
>>> >>> def f ( x , y ):
... return x + y
...
>>> from functools import reduce
>>> reduce ( f , [ 1 , 2 , 3 , 4 , 5 ])
15
这个电话给reduce()
产生结果15
从列表中[1, 2, 3, 4, 5]
如下:
reduce(f, [1, 2, 3, 4, 5])
这是对列表中的数字进行求和的一种相当迂回的方式!虽然这很好用,但还有一种更直接的方法。 Python 的内置和() 返回可迭代对象中数值的总和:
>>> >>> sum ([ 1 , 2 , 3 , 4 , 5 ])
15
请记住,二元加运算符也连接字符串 。因此,这个相同的示例也将逐步连接列表中的字符串:
>>> >>> reduce ( f , [ "cat" , "dog" , "hedgehog" , "gecko" ])
'catdoghedgehoggecko'
同样,有一种方法可以实现这一点,大多数人会认为它更典型的 Pythonic。这正是str.join() 做:
>>> >>> "" . join ([ "cat" , "dog" , "hedgehog" , "gecko" ])
'catdoghedgehoggecko'
现在考虑一个使用二元乘法运算符的示例(*
)。这阶乘 正整数的n
定义如下:
您可以使用以下方法实现阶乘函数reduce()
和range()
如下所示:
>>> >>> def multiply ( x , y ):
... return x * y
...
>>> def factorial ( n ):
... from functools import reduce
... return reduce ( multiply , range ( 1 , n + 1 ))
...
>>> factorial ( 4 ) # 1 * 2 * 3 * 4
24
>>> factorial ( 6 ) # 1 * 2 * 3 * 4 * 5 * 6
720
再说一次,有一种更直接的方法可以做到这一点。您可以使用factorial()
标准提供的数学模块 :
>>> >>> from math import factorial
>>> factorial ( 4 )
24
>>> factorial ( 6 )
720
作为最后一个示例,假设您需要查找列表中的最大值。 Python提供了内置函数最大限度() 来做到这一点,但你可以使用reduce()
还有:
>>> >>> max ([ 23 , 49 , 6 , 32 ])
49
>>> def greater ( x , y ):
... return x if x > y else y
...
>>> from functools import reduce
>>> reduce ( greater , [ 23 , 49 , 6 , 32 ])
49
请注意,在上面的每个示例中,函数传递给reduce()
是一个单行函数。在每种情况下,您都可以使用lambda
函数代替:
>>> >>> reduce ( lambda x , y : x + y , [ 1 , 2 , 3 , 4 , 5 ])
15
>>> reduce ( lambda x , y : x + y , [ "foo" , "bar" , "baz" , "quz" ])
'foobarbazquz'
>>> def factorial ( n ):
... from functools import reduce
... return reduce ( lambda x , y : x * y , range ( 1 , n + 1 ))
...
>>> factorial ( 4 )
24
>>> factorial ( 6 )
720
>>> reduce (( lambda x , y : x if x > y else y ), [ 23 , 49 , 6 , 32 ])
49
这是避免将不需要的函数放入命名空间的便捷方法。另一方面,当您使用时,阅读代码的人可能会更难确定您的意图lambda
而不是定义一个单独的函数。通常情况下,这是可读性和便利性之间的平衡。
呼唤reduce()
有初始值
还有另一种调用方式reduce()
指定归约序列的初始值:
reduce ( < f > , < iterable > , < init > )
当存在时,<init>
指定组合的初始值。在第一次通话中<f>
,参数是<init>
和第一个元素<iterable>
。然后将该结果与第二个元素相结合<iterable>
, 等等:
>>> >>> def f ( x , y ):
... return x + y
...
>>> from functools import reduce
>>> reduce ( f , [ 1 , 2 , 3 , 4 , 5 ], 100 ) # (100 + 1 + 2 + 3 + 4 + 5)
115
>>> # Using lambda:
>>> reduce ( lambda x , y : x + y , [ 1 , 2 , 3 , 4 , 5 ], 100 )
115
现在函数调用的顺序如下所示:
reduce(f, [1, 2, 3, 4, 5], 100)
您可以轻松获得相同的结果,而无需reduce()
:
>>> >>> 100 + sum ([ 1 , 2 , 3 , 4 , 5 ])
115
正如您在上面的示例中所看到的,即使您可以使用以下方式完成任务reduce()
,通常可以找到更直接、更Pythonic 没有它来完成相同任务的方法。也许不难想象为什么reduce()
最终从核心语言中删除了。
也就是说,reduce()
这是一个了不起的功能。本节开头的描述指出reduce()
组合元素以产生单一结果 。但该结果可以是复合对象,例如列表或元组。是因为,reduce()
是一个非常笼统的高阶函数 从中可以实现许多其他功能。
例如,您可以实施map()
按照reduce()
:
>>> >>> numbers = [ 1 , 2 , 3 , 4 , 5 ]
>>> list ( map ( str , numbers ))
['1', '2', '3', '4', '5']
>>> def custom_map ( function , iterable ):
... from functools import reduce
...
... return reduce (
... lambda items , value : items + [ function ( value )],
... iterable ,
... [],
... )
...
>>> list ( custom_map ( str , numbers ))
['1', '2', '3', '4', '5']
你可以实施filter()
使用reduce()
还有:
>>> >>> numbers = list ( range ( 10 ))
>>> numbers
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> def is_even ( x ):
... return x % 2 == 0
...
>>> list ( filter ( is_even , numbers ))
[0, 2, 4, 6, 8]
>>> def custom_filter ( function , iterable ):
... from functools import reduce
...
... return reduce (
... lambda items , value : items + [ value ] if function ( value ) else items ,
... iterable ,
... []
... )
...
>>> list ( custom_filter ( is_even , numbers ))
[0, 2, 4, 6, 8]
事实上,对对象序列的任何操作都可以表示为归约。