Python 中的函数式编程:何时以及如何使用它

2023-12-03

函数式编程是一种编程范式,其中主要计算方法是函数求值。在本教程中,您将探索 Python 中的函数式编程。

函数式编程通常在 Python 代码中扮演相当小的角色。但熟悉它是件好事。至少,在阅读其他人编写的代码时,您可能会时不时地遇到它。您甚至可能会发现在您自己的代码中使用 Python 的函数式编程功能是有利的。

在本教程中,您将学习:

  • 什么是函数式编程范式意味着
  • 这句话是什么意思功能一等公民在Python中
  • 如何定义匿名函数lambda关键词
  • 如何使用实现功能代码map(), filter(), 和reduce()

免费奖金: 关于掌握 Python 的 5 个想法,为 Python 开发人员提供的免费课程,向您展示将 Python 技能提升到新水平所需的路线图和思维方式。

什么是函数式编程?

A 纯函数是一个函数,其输出值仅取决于其输入值,没有任何可观察到的副作用。在函数式编程,一个程序完全由纯函数的求值组成。计算通过嵌套或进行组合函数调用,无需更改状态或可变数据。

函数式范例很受欢迎,因为它与其他编程范例相比具有多种优势。功能代码为:

  • 高水平:您描述的是您想要的结果,而不是明确指定实现该结果所需的步骤。单一陈述往往简洁但内容丰富。
  • 透明的:纯函数的行为仅取决于其输入和输出,没有中间值。这消除了副作用的可能性,从而有利于调试.
  • 可并行化:不会引起副作用的例程可以更容易地并行运行与彼此。

许多编程语言都支持某种程度的函数式编程。在某些语言中,几乎所有代码都遵循函数范式。哈斯克尔就是这样的一个例子。相比之下,Python 确实支持函数式编程,但也包含其他编程模型的功能。

虽然确实有深入的函数式编程的描述有点复杂,这里的目标不是提供严格的定义,而是向您展示通过 Python 中的函数式编程可以做什么。

Python 对函数式编程的支持程度如何?

为了支持函数式编程,如果功能在给定的编程语言中具有两种能力:

  1. 将另一个函数作为参数
  2. 将另一个函数返回给其调用者

Python 在这两方面都表现得很好。正如您在本系列之前所了解到的,Python 程序中的一切都是对象。 Python 中的所有对象都具有或多或少相同的地位,函数也不例外。

在Python中,函数是一等公民。这意味着函数与值具有相同的特征,例如字符串数字。您希望能够使用字符串或数字执行的任何操作也可以使用函数执行。

例如,您可以将函数分配给变量。然后,您可以像使用函数本身一样使用该变量:

>>>
 1>>> def func():
 2...     print("I am function func()!")
 3...
 4
 5>>> func()
 6I am function func()!
 7
 8>>> another_name = func
 9>>> another_name()
10I 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)
10I 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()
13I am function inner()!
14
15>>> outer()()
16I 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])
 5True

第 1 行的语句只是lambda本身的表达。在第 2 行,Python 显示了表达式的值,您可以看到它是一个函数。

内置Python函数可调用()回报真的如果传递给它的参数看起来是可调用的并且错误的否则。第 4 行和第 5 行显示返回的值lambda表达式实际上是可调用的,就像函数应该的那样。

在这种情况下,参数列表由单个参数组成s。后续的表达式s[::-1]切片语法是以相反顺序返回 s 中的字符。所以这lambdaexpression 定义一个临时的、无名的函数,它接受一个字符串参数并返回字符反转的参数字符串。

由 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>)

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>, <iterable1>, <iterable2>, ..., <iterablen>) applies <f> to the elements in each <iterablei> in parallel and returns an iterator that yields the results.

The number of <iterablei> arguments specified to map() must match the number of arguments that <f> expects. <f> acts on the first item of each <iterablei>, and that result becomes the first item that the return iterator yields. Then <f> acts on the second item in each <iterablei>, 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),如下图所示:

Diagram of map() call with multiple iterables

返回值来自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>)

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)产生一个迭代器,它产生整数0n - 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>)

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 function illustration
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定义如下:

Definition of factorial

您可以使用以下方法实现阶乘函数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 function with <init> argument
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]

事实上,对对象序列的任何操作都可以表示为归约。

结论

函数式编程是一种编程范例,其中主要计算方法是纯函数的求值。虽然 Python 主要不是一种函数式语言,但熟悉一下还是有好处的lambda, map(), filter(), 和reduce()因为它们可以帮助您编写简洁、高级、可并行的代码。您还会在其他人编写的代码中看到它们。

在本教程中,您学习了:

  • 什么函数式编程 is
  • 如何功能在Python中是一等公民,以及这如何使它们适合函数式编程
  • 如何定义一个简单的匿名函数lambda
  • 如何实现功能代码map(), filter(), 和reduce()

至此,您已经完成了有关使用 Python 的基础知识的介绍性系列的结尾。恭喜!现在,您已经为以高效的 Python 风格编写有用的程序奠定了坚实的基础。

如果您有兴趣将 Python 技能提升到一个新的水平,那么您可以查看更多内容中间的先进的教程。您还可以查看一些Python项目想法开始展示你的 Python 超能力。快乐编码!

« Python 中的命名空间和作用域
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 中的函数式编程:何时以及如何使用它 的相关文章

  • 用于查找列表/集合中唯一元素的代码

    根据上面阴影部分的面积应该代表 A XOR B XOR C XOR A AND B AND C 如何将其翻译成Python代码 代码必须与上述表达式中提供的集合操作密切相关 至少这是首选 该代码必须足够通用 能够处理 3 个以上的列表 UP
  • Cython 函数中的字符串

    我想这样做将字符串传递给 Cython 代码 test py s Bonjour myfunc s test pyx def myfunc char mystr cdef int i for i in range len mystr err
  • 管理 Tweepy API 搜索

    如果这是对之前在其他地方回答过的问题的粗略重复 请原谅我 但我不知道如何使用 tweepy API 搜索功能 是否有任何有关如何使用搜索推文的文档api search 功能 有什么方法可以控制返回的推文数量 结果类型等功能 由于某种原因 结
  • 使用 Pillow 和 Numpy 进行图像推导

    I have two images and 我想导出一个只有红色 Hello 的图像 例如 所以我正在运行一个简单的推导python脚本 from PIL import Image import numpy as np root root
  • 如何在 kubernetes 上使多个 pod 相互通信

    我是 Kubernetes 新手 我正在尝试通过 microk8s 将应用程序部署到 Kubernetes 该应用程序包含Python Flask后端 Angular前端 Redis和MySQL数据库 我将映像部署在多个 Pod 中 状态显
  • SQLAlchemy:检查给定值是否在列表中

    问题 在 PostgreSQL 中 检查某个字段是否在给定列表中是使用IN操作员 SELECT FROM stars WHERE star type IN Nova Planet SQLAlchemy 的等价物是什么INSQL查询 我尝试过
  • __getitem__、__setitem__ 如何处理切片?

    我正在运行 Python 2 7 10 我需要拦截列表中的更改 我所说的 更改 是指在浅层意义上修改列表的任何内容 如果列表由相同顺序的相同对象组成 则列表不会更改 无论这些对象的状态如何 否则 它会更改 我不需要找出来how列表已经改变
  • 为什么我不能“string”.print()?

    我的理解print 在 Python 和 Ruby 以及其他语言 中 它是字符串 或其他类型 上的方法 因为它的语法非常常用 打印 嗨 works 那么为什么不呢 hi print 在 Python 中或 hi print在红宝石工作 当你
  • 如何将一串Python代码编译成一个可以调用函数的模块?

    在 Python 中 我有一串 Python 源代码 其中包含以下函数 mySrc def foo print foo def bar print bar 我想将这个字符串编译成某种形式类似模块的对象这样我就可以调用代码中包含的函数 这是我
  • 获取 HTML 代码的结构

    我正在使用 BeautifulSoup4 我很好奇是否有一个函数可以返回 HTML 代码的结构 有序标签 这是一个例子 h1 Simple example h1 p This is a simple example of html page
  • 将查询参数添加到 URL

    我正在尝试自动从网站下载数据 我需要将动态参数传递到每天更改的站点 html 的结构是表格而不是表单 如何传递参数并从 url 获取结果 这是我尝试过的 它需要在 python 2 7 中 import urllib url https d
  • 如何使用 Selenium Webdriver (Python) 在上下文菜单中选择“将图像另存为...”来保存图像

    我正在尝试使用 selenium webdriver 将特定图像保存到目录中 我希望通过模拟右键单击 img 元素并选择 将图像另存为 来实现此目的 使用以下代码我可以打开上下文菜单 但无法选择正确的选项 browser WebDriver
  • 让 TensorFlow 在 ARM Mac 上使用 GPU

    我已经安装了TensorFlow在 M1 上 ARM Mac 根据这些说明 https github com apple tensorflow macos issues 153 一切正常 然而 模型训练正在进行CPU 如何将培训切换到GPU
  • 如何让 Python 找到 ffprobe?

    I have ffmpeg and ffprobe安装在我的 mac macOS Sierra 上 并且我已将它们的路径添加到 PATH 中 我可以从终端运行它们 我正在尝试使用ffprobe使用以下代码获取视频文件的宽度和高度 impor
  • 单击 selenium 中的链接时循环遍历表格的行(python)

    示例页面源代码如下所示 div class div1 table class foot market tbody td class today name td tbody tbody td class today name td tbody
  • Python中的MariaDB连接器无法连接到远程服务器

    我使用与远程 Mariadb 服务器的连接已有几个月了 今天 无法再通过 macOS 上的 python mariadb 模块和 mariadb 连接器建立连接 基本安装如下 brew install mariadb connector c
  • 在Python中将罗马数字转换为整数

    根据 user2486 所说 这是我当前的代码 def romanMap map M 1000 CM 900 D 500 CD 400 C 100 XC 90 L 50 XL 40 X 10 IX 9 V 5 V 4 I 1 return
  • 在Python中打开网站框架或图像

    所以我对 python 相当熟练 并且经常使用 urllib2 和 Cookies 来实现网站自动化 我刚刚偶然发现了 webbrowser 模块 它可以在默认浏览器中打开一个网址 我想知道是否可以从该 url 中仅选择一个对象并打开它 具
  • Python 可以替代 Java 小程序吗?

    除了制作用于物理模拟 如抛射运动 重力等 的教育性 Java 小程序之外 还有其他选择吗 如果你想让它在浏览器中运行 你可以使用PyJamas http pyjs org 这是一个 Python 到 Javascript 的编译器和工具集
  • 在哪里可以找到Python内置序列类型的时间和空间复杂度

    我一直无法找到此信息的来源 无法亲自查看 Python 源代码来确定这些对象是如何工作的 有谁知道我可以在网上找到这个吗 结帐时间复杂度 http wiki python org moin TimeComplexitypy dot org

随机推荐

  • 有效使用 NumPy 的 np.arange()

    数值模拟是数值计算的基本 Python 库 它最重要的类型是数组类型被称为ndarray NumPy 提供了很多数组创建例程对于不同的情况 arange 就是这样一个基于的函数数值范围 它通常被称为np arange 因为np是 NumPy
  • Python 中的 Minimax:学习如何输掉 Nim 游戏

    目录 玩一个简化的 Nim 游戏 Get to Know the Minimax Algorithm 探索游戏树 找到最佳的下一步行动 Lose the Game of Nim Against a Python Minimax Player
  • Python pandas:你可能不知道的技巧和功能

    目录 1 在解释器启动时配置选项和设置 2 用pandas的测试模块制作玩具数据结构 3 利用访问器方法 4 从组件列创建 DatetimeIndex 5 使用分类数据节省时间和空间 6 通过迭代内省 Groupby 对象 7 使用此映射技
  • 版本控制

    你应该使用版本控制随着时间的推移 保留您的对象的完整记录 它还充当防止意外删除对象的保护机制 当您请求版本化对象 Boto3 将检索最新版本 当您添加对象的新版本时 该对象总共占用的存储空间是其版本大小的总和 因此 如果您存储一个 1 GB
  • 在 Python 中使用 JSON 数据

    JSON 是一种轻量级数据交换格式 它允许我们将 Python 程序中的对象表示为可以通过互联网发送的人类可读文本 许多 API 和数据库使用 JSON 进行通信 您将学习如何使用 Python 的内置函数json模块将程序中的数据序列化为
  • Python 社区采访 Eric Wastl

    本周 我们加入了埃里克 瓦斯特 高级架构师TCGP播放器和创始人代码的出现 AoC 请加入我们 讨论 AoC 的起源 在解决 AoC 中的代码挑战时如何避免常见陷阱 以及 Eric 在每年的 3 8 秒 空闲时间中所做的事情 如果您在阅读本
  • 视频字幕和脚本现在可在 Real Python 上使用

    嘿 今天我有一个重大更新要分享 真正的 Python 视频课程现在有完整的字幕和成绩单 我认为这对于可访问性有很大帮助 并使您最喜欢的 Python 学习资源更易于查看和搜索 让我们做一个快速演示 现在附带视频课程完整字幕您可以在方便时打开
  • 识别无效的 Python 语法

    Python 以其简单的语法而闻名 然而 当你第一次学习Python 或者当你在另一种编程语言上有扎实的背景后才开始学习Python 你可能会遇到一些Python不允许的事情 如果您曾经收到过SyntaxError当尝试运行 Python
  • 使用 Python 发送电子邮件

    目录 Getting Started 选项 1 设置 Gmail 帐户进行开发 选项 2 设置本地 SMTP 服务器 Sending a Plain Text Email 启动安全 SMTP 连接 发送您的纯文本电子邮件 Sending F
  • 真正的 Python 安全性和报告

    真正的 Python 安全性和报告 如果您发现安全漏洞 请通过以下方式告知我们信息 security realpython com 我们尝试尽快回复 修复 并非常感谢您的帮助 感谢以下人员发现并负责任地披露了 Real Python 中的安
  • 把它写出来

    当您作为一名新程序员在您的旅程中取得进展时 您可能想知道是否应该做笔记 是的你应该 事实上 研究表明 手写笔记最有利于长期记忆 这对于那些致力于成为全职开发人员的人来说尤其有益 因为许多面试将涉及在白板上编写代码 一旦您开始处理小型项目和程
  • 2020 年 11 月 4 日

    主持人 David Amos 回答了 Real Python 成员的问题 在这次会议上 我们讨论了 Python 新闻和更新 代码如何在导入时运行 这可能是一个问题吗 有关静态网站生成器的更多信息 Python 在硬件项目上的应用 过渡到数
  • 最好的 Python 书籍

    目录 Best Books for Learning Python Python 速成课程 Head First Python 第二版 用 Python 发明你自己的电脑游戏 第四版 思考 Python 如何像计算机科学家一样思考 第二版
  • 选择优先级队列

    优先级队列是队列的特殊实例 其中存储顺序基于内部项目的优先级 这通常用于调度算法 使调度中的某些项目比其他项目更重要 在 Python 中获取优先级队列有多种方法 您可以使用内置的list类型结合sort 函数 根据优先级排序 或者 而不是
  • 使用 PyQt 处理 SQL 数据库:基础知识

    目录 Connecting PyQt to an SQL Database 创建数据库连接 处理多个连接 使用不同的 SQL Divers 打开数据库连接 Running SQL Queries With PyQt 执行静态 SQL 查询
  • map() 函数:概述

    在本节中 您将了解更多有关函数式编程的基础知识 即如何使用map 函数进行变换数据结构 您将采用本课程之前视频中使用不可变数据结构表示的示例数据集 然后创建一个转变的使用Python内置的相同数据的版本map 功能 map 是 Python
  • 在 Nitrous.IO 上使用 Django 和 Flask

    目录 入门 使用 Virtualenv 设置 Python 2 7 安装Django 安装烧瓶 这是我们的朋友 Greg McKeever 的客座帖子亚硝酸盐 Nitrous IO 是一个允许您在云中快速启动自己的开发环境的平台 以下是在上
  • Python 基础知识:字符串和字符串方法(概述)

    许多程序员 无论其专业如何 都会在计算机上处 理文本 每日基础 例如 网络开发人员使用来自以下位置的文本输入 网络表格 数据科学家处理文本以提取数据并执行 诸如情感分析之类的任务 可以帮助识别和分类 文本正文中的意见 Python 中的文本
  • Python 中的函数式编程:何时以及如何使用它

    目录 什么是函数式编程 Python 对函数式编程的支持程度如何 使用 lambda 定义匿名函数 Applying a Function to an Iterable With map 使用单个可迭代对象调用 map 使用多个可迭代对象调
  • 教材:书面教程

    在本视频中 您将了解如何访问 Real Python 上的另一种类型的学习资源 基于文本的教程 您将了解在哪里可以找到我们编写的 Python 教程 如何访问额外资源 例如代码示例和可下载指南 等等 重要链接 教程概述页面 教程主题页面