在这篇文章里,你将学会什么是函数范式以及如何应用 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,则从列表中删除该项。
语法如下:
让咱们看一个小例子,没有 filter 咱们会写:
应用 filter,能够这样写:
Python 作为一门一直倒退与遍及的语言,还在不断更新中。在学习时,倡议找一些学习搭档一起来学习和探讨,成果更佳。如果想学习 Python,欢送退出 Python 学习交换群(627012464),一起督促,一起学习。外面有开发工具,很多干货和技术材料分享!
高阶函数
高阶函数能够将函数作为参数并返回函数。一个非常简单的例子如下:
第二个返回函数的例子:
结尾我说过纯函数式编程语言没有变量。更高阶的函数使这变得更容易。
Python 中的所有函数都是一等公民。一等公民被定义为具备以下一个或多个特色:
在运行时创立
在数据结构中调配变量或元素
作为函数的参数传递
作为函数的后果返回
Python 中的所有函数都能够用作高阶函数。
Partial application
Partial application(也称为闭包)有点奇怪,但十分酷。您能够在不提供所需的所有参数的状况下调用函数。让咱们在一个例子中看到这一点。咱们想要创立一个函数,它承受 2 个参数,一个基数和一个指数,并返回指数幂的基数,如下所示:
当初咱们想要一个专用的平方函数,应用幂函数计算出数字的平方:
这无效,但如果咱们想要一个立方体性能呢?或者求四次方的性能呢?咱们能够持续写下它们吗?好吧,你能够。但程序员很懒的。如果你一遍又一遍地反复同样的事件,这表明有一种更快的办法来加快速度,这将使你不再反复。咱们能够在这里应用闭包。让咱们看一个应用闭包的 square 函数的示例:
是不是很酷!咱们能够只应用 1 个参数来调用须要 2 个参数的函数。
咱们还能够应用一个循环来生成一个幂函数,该函数实现从立方体始终到 1000 的幂。
函数式编程不是 pythonic
您可能曾经留神到了,咱们想要在函数式编程中做的很多事件都围绕着列表。除了 reduce 函数和闭包之外,您看到的所有函数都会生成列表。Guido(Python 之父)不喜爱 Python 中的函数式,因为 Python 曾经有了本人生成列表的办法。
如果你在 Python 的交互环境下写入”import this“,你将会失去:
这是 Python 之禅。这是一首对于 Pythonic 意味着什么的诗。咱们想要波及的局部是:
There should be one — and preferably only one — obvious way to do it.(应该尽量找到一种,最好是惟一一种显著的解决方案)
在 Python 中,map 和 filter 能够执行与列表推导(上面探讨)雷同的操作。这突破了 Python 之禅的一个规定,因而函数式编程的这些局部不被视为“pythonic”。
另一个话题是 Lambda。在 Python 中,lambda 函数是一个一般函数。Lambda 是语法糖。这两种说法是等价的。
一般函数能够执行 lambda 函数能够执行的所有操作,但它不能以相同的形式工作。lambda 函数不能实现一般函数能够执行的所有操作。
这是一个简短的论证,为什么函数式编程不能很好地适应整个 Python 生态系统。你可能曾经留神到我之前提到了列表推导,咱们当初将探讨它们。
列表推导
后面,我提到过你能够用 map 或 filter 做的任何事件,你能够用列表推导。列表推导是一种在 Python 中生成列表的办法。语法是:
让咱们对列表中的每个数字进行平方,例如:
咱们能够看到如何将函数利用于列表中的每一项。咱们如何利用 filter 呢?看看后面的代码:
咱们能够将其转换成一个列表推导,像这样:
列表反对 if 这样的语句。您不再须要将一百万个函数利用于某些货色以取得您想要的货色。事实上,如果你想尝试生成某种列表,那么应用列表推导看起来会更清晰,更容易。如果咱们想要将列表中每个 0 以下的数字平方怎么办?有了 lambda,map 和 filter 你会写:
这仿佛很长很简单。通过列表推导,它只是:
列表推导仅实用于列表。map,filter 适宜任何可迭代的对象,那么这有什么用呢?你能够对你遇到的任何可迭代对象应用任何推导。
其余推导
你能够为任何可迭代对象创立一个推导。
能够应用推导生成任何可迭代的对象。从 Python 2.7 开始,您甚至能够生成字典(hashmap)。
如果它是可迭代的,则能够生成它。让咱们看一下最初一组的例子。
set 是一个元素列表,在该列表中没有元素反复两次。
set 中的元素没有程序。
您可能会留神到 set(汇合)与 dict(字典)具备雷同的花括号。Python 十分聪慧。依据你是否为 dict 提供值,它会晓得你是在写 dict 推导还是 set 推导。
总结
函数式编程好看而纯正。函数式代码能够很洁净,但也可能很乱。一些 Python 程序员不喜爱 Python 中的函数式编程。但我认为,你应该在解决问题时,应用最佳工具。