在这篇文章里,你将学会什么是函数范式以及如何使用Python进行函数式编程。你也将了解列表推导和其它形式的推导。
函数范式
在命令式范式中,通过为计算机提供一系列指令然后执行它们来完成任务。在执行这些指令时,可以改变某些状态。例如,假设你最初将A设置为5,然后更改A的值。这时在变量内部值的意义上,你改变了A的状态。
在函数式范式中,你不用告诉计算机做什么而是告诉他这个东西是什么。比如数字的最大公约数是什么,从1到n的乘积是什么等等。
因此,变量不能变化。一旦你设置了一个变量,它就永远保持这种状态(注意,在纯函数式语言中,它们不是变量)。因此,函数式编程没有副作用。副作用指的是函数改变它自己以外的东西。让我们看一些典型Python代码的示例:
这段代码的输出是5。在函数式范式中,改变变量是一个很大的禁忌,并且具有影响其范围之外事物的功能也是一个很大的禁忌。函数唯一能做的就是计算一些东西并将其作为结果返回。
现在你可能会想:“没有变量,没有副作用?为什么这样好?“这个问题问得好,我相信大多数人对此感到疑惑。
如果使用相同的参数调用函数两次,则保证返回相同的结果。如果你已经学习了数学函数,你就会知道这个好处。这称为参照透明度。由于函数没有副作用,如果你正在构建一个计算某些事情的程序,你可以加速程序。如果每次调用func(2)都返回3,我们可以将它存储在表中,这可以防止程序重复运行相同的功能。
通常,在函数式编程中,我们不使用循环。我们使用递归。递归是一个数学概念,通常意味着“自我调用”。使用递归函数,该函数将其自身作为子函数重复调用。这是Python中递归函数的一个很好的例子:
有些编程语言也具有惰性。这意味着他们直到最后一秒才计算或做任何事情。如果你编写一些代码来执行2 + 2,函数程序只会在你真正需要使用结果时计算出来。我们很快就会在Python中探索惰性。
Map
为了理解,我们先来看看迭代是什么。通常可以迭代的对象是列表或数组,但Python有许多不同的类型可以迭代。你甚至可以创建自己的对象,这些对象可以通过实现魔术方法进行迭代。魔术方法就像是一个API,可以帮助你的对象变得更加Pythonic。您需要实现2个魔术方法才能使对象成为可迭代的:
第一个魔术方法“__iter__”(注:这里是双下划线)返回迭代对象,这通常在循环开始时使用。”__next__“返回下一个对象。
让我们快速进入一个终端调用上面的代码:
运行将会打印出
在Python中,迭代器是一个只有__iter__魔术方法的对象。这意味着您可以访问对象中的位置,但不能遍历该对象。一些对象将具有魔术方法__next__而不是__iter__魔术方法,例如集合(在本文后面讨论)。对于本文,我们假设我们接触的所有内容都是可迭代的对象。
现在我们知道什么是可迭代对象了,让我们回到map函数。 map函数允许我们将函数应用于iterable中的每一项。 Map需要2个输入,它们分别是要应用的函数和可迭代对象。
假设我们有一个数字列表,如下所示:
我们想要对每个数字进行平方,我们可以编写如下代码:
Python中函数式的函数是具有惰性的。如果我们不使用“list”,该函数将存储iterable的定义,而不是列表本身。我们需要明确告诉Python“把它变成一个列表”供我们使用。
在Python中突然从非惰性求值转向惰性求值有点奇怪。如果你在函数式思维方式中考虑得更多,而不是命令式思维方式,那么你最终会习惯它。
现在写一个像“square(num)”这样的普通函数虽然很好,但却是不对的。我们必须定义一个完整的函数才能在map中使用它?好吧,我们可以使用lambda(匿名)函数在map中定义一个函数。
Lambda表达式
lambda表达式是一个只有一行的函数。举个例子,这个lambda表达式对给定的数字进行平方:
让我们运行它:
这看起来不像一个函数吗?
嗯,这有点令人困惑,但可以解释。我们将一些东西分配给变量“square”。那这个呢:
告诉Python这是一个lambda函数,输入叫做x。冒号之后的任何内容都是您对输入所做的操作,它会自动返回结果。
简化我们的square程序到只有一行代码,我们可以这样做:
所以在lambda表达式中,所有参数都在左边,你要用它们做的东西在右边。它有点乱。但事实是,编写只有其他函数式程序员才能阅读的代码会有一定的乐趣。此外,使用一个函数并将其转换为一行代码是非常酷的。
Reduce
Reduce是一个将迭代变成一个东西的函数。通常,你可以在列表上使用reduce函数执行计算以将其减少到一个数字。 Reduce看起来像这样:
我们经常会使用lambda表达式作为函数。
列表的乘积是每个单独的数字相乘。要做到这一点你将编写如下代码:
但是使用reduce你可以这样写:
获得相同的功能,代码更短,并且在使用函数式编程的情况下更整洁。(注:reduce函数在Python3中已不是内置函数,需要从functools模块中导入)
Filter
filter函数采用可迭代的方式,并过滤掉你在该可迭代中不需要的所有内容。
通常,filter需要一个函数和一个列表。它将函数应用于列表中的每一项,如果该函数返回True,则不执行任何操作。如果返回False,则从列表中删除该项。
语法如下:
总结
函数式编程美观而纯粹。函数式代码可以很干净,但也可能很乱。一些Python程序员不喜欢Python中的函数式编程。但我认为,你应该在解决问题时,使用最佳工具。