共计 4190 个字符,预计需要花费 11 分钟才能阅读完成。
在这篇文章里,你将学会什么是函数范式以及如何应用 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 中的函数式编程。但我认为,你应该在解决问题时,应用最佳工具。
本文转自 https://juejin.cn/post/7043656047721971720,如有侵权,请分割删除。