十分钟轻松学会python-10分钟学习函数式Python

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

be992369562c484f99426561f837c06c.jpg

在这篇10分钟的文章中,您将学习Python中的函数式范型。您还将学习列表推导式。

目录函数式范式

Python的map函数是如何运行的

Python中的lambda表达式

Python中的reduce函数

filter函数

Python中的高阶函数

带有函数的部分应用

函数编程不是Python化

列表推导式

任何可迭代对象的推导式

结论

函数式范式

在命令式编程范式中,我们通过给计算机一个任务序列来执行任务,然后计算机会执行这些任务。在执行它们时,计算机可以改变状态。例如,我们设A = 5,然后改变A的值。因为我们的A是变量,所以它内部的值是变化的。

在函数式编程范式中,我们不告诉计算机去做什么,而是告诉它是什么东西。一个数的最大公约数是什么,等等。

变量不会变化。一旦我们设置了一个变量,它就会永远保持这种状态。因此,函数在函数式范型中没有副作用。副作用就是函数改变了它外部的东西。让我们来看一个例子:

9a707dcf606b4dfa917a30a81ea1d931.jpg

输出是5。在函数式范型中,改变变量是一个很大的禁忌,而让函数影响它们范围之外的东西也是一个大大的禁忌。函数唯一能做的就是计算某些东西并返回它。

现在您可能会想"没有变量,就没有副作用?为什么这很好?”问得好,读这篇文章的古怪陌生人。

如果一个函数使用相同的参数被调用两次,那么它肯定会返回相同的结果。如果您学过数学函数,您就会喜欢这一点。我们称之为函数的引用透明性。由于函数没有副作用,如果我们构建一个计算程序,我们就可以加快该程序的速度。如果程序知道func(2)等于3,我们可以将其存储在一个表中。这将防止程序在我们已经知道答案的情况下去运行相同的函数。

通常,在函数式编程中,我们不使用循环,我们使用递归。递归是一个数学概念,它意味着"自食其力”。对于递归函数,该函数将自己作为一个子函数进行调用。下面是Python中递归函数的一个很好的例子:

4f3abe3707704881abefbed6edb56d51.jpg

一些编程语言也很懒。这意味着他们直到最后一秒才开始计算或做任何事情。如果我们编写一些代码来执行2 + 2,一个函数式程序只会在我们需要使用结果时才会计算这个结果。我们很快就会探讨Python中的惰性。

Python的map函数是如何运行的

为了理解映射,让我们首先看看什么是可迭代对象。一个可迭代对象是我们可以迭代的任何东西。这些是列表或数组,但是Python有许多不同的可迭代对象。我们甚至可以通过实现魔术方法来创建我们自己的可迭代对象。一个魔术方法就像一个API,它可以帮助我们的对象变得更Python化。要使一个对象成为一个可迭代对象,我们需要实现2个魔术方法:

76e1e83ca576473da895399af6c9d8de.jpg

第一个魔术方法是__iter__,或者叫特殊iter(双下划线)方法,它会返回迭代对象,我们通常在循环开始时使用它。特殊next方法,__next__,会返回下一个对象是什么。

让我们来看看这个:

695de93683b24c6fad12e2e04d6e5dae.jpg

这将输出:

c7e8fbd3638d40bc92b4eb3c782bcca6.jpg

在Python中,迭代器是一个对象,它只有一个简单的__iter__魔术方法。这意味着我们可以访问该对象中的位置,但不能遍历该对象。有些对象有魔术方法__next__,但没有__iter__魔术方法,如sets(将在本文后面讨论)。对于本文,我们将假设我们接触的所有东西都是一个可迭代的对象。

现在我们知道了什么是可迭代对象,让我们回到map函数。

map函数允许我们将一个函数应用到一个可迭代对象中的每个项。我们希望将一个函数应用到一个列表中的每个项,但是要知道这对大多数可迭代对象来说都是可行的。map的语法接受两个输入,即要应用的函数和可迭代的对象。

16af3ef0e1494ddf8357937d6461db36.jpg

假设我们有一个像这样的数字列表:

4df3677b303040258bf9eab2c5c9f9fe.jpg

我们相对每个数字进行平方,我们可以像这样写代码:

727dc94600654053abc68e04e0f05d08.jpg

函数式Python是惰性的。如果我们不包含list(),函数将存储该可迭代对象的定义,而不是列表本身。我们需要告诉Python"把这个转换成一个列表”,以便我们使用它。

在Python中突然从非惰性求值变成惰性求值是很奇怪的。如果您更多地以函数式思维而不是命令式思维进行思考,您就会习惯它。

现在写一个像square(num)这样的普通函数就很好了,但是它看起来不够好。我们必须定义一个完整的函数才可以在一个映射中使用它吗?好吧,我们可以使用lambda(匿名)函数在map中定义一个函数。

Python中的lambda表达式

Lambda函数是一个只有一行代码的函数,适用于短期内使用。我们经常将它们随同高阶函数,如filter、map和reduce函数,一起使用。这个lambda表达式会对传给它的数字进行平方:

965d189472c94ba8995db763b70f1411.jpg

现在我们来运行这个函数:

0c827b00638843efab1a5a1329b99c1a.jpg

我听到您在说:"Brandon,参数在哪里?这是什么鬼东西?它看起来一点也不像一个函数?”

嗯,这确实很令人困惑,但我可以解释它。我们将某个东西赋值给变量square。这部分:

e110c2c9d89d48be8bb8a158859dcb17.jpg

告诉Python这是一个lambda函数,输入被称为x。冒号之后的任何东西都是我们对输入所执行的操作,它返回的就是这些操作的结果。

为了将我们的平方程序简化成一行,我们可以这样做:

2e8e2c6a85f545e19f92924bb54b8119.jpg

在一个lambda表达式中,所有的参数都在左边,而我们要用它们做的事情都在右边。没人能否认,这有点乱。编写只有其他函数式程序员才能阅读的代码是一种乐趣。另外,将一个函数转换成一行程序是非常酷的事情。

Python中的reduce函数

reduce是一个函数,它将给定的函数应用于一个可迭代对象并返回一个东西。通常我们会在一个列表上进行计算,将其缩减至一个数字。Reduce看起来是这样的:

40b1fa00dba643cc97d1e2dff222a54e.jpg

我们可以(通常也会)使用lambda表达式作为函数。

列表的乘积是每一个数字相乘。编写的程序是这样:

4c3f633b12f3457e82be6f7800b2bf43.jpg

但是使用reduce我们可以这样写:

c8cbf57101ca404bb851b7e6565b38a3.jpg

我们得到了相同的乘积。代码更短,并且具有函数式编程的知识,因此更简洁。

fileter函数

filter函数接受一个iterable并过滤掉我们不希望存在于该iterable中的所有东西。

filter接受一个函数和一个列表。它将该函数应用于列表中的每一项,如果该函数返回True,则不执行任何操作。如果该函数返回False,它会从该列表中删除该项。

语法如下:

99f9670b4126477abe3289fc00977281.jpg

让我们看一个小例子,没有filter,我们会这样写:

952a4ba1598a424c95cbada690ba864f.jpg

使用filter, 这就变成了:

1eedb172ebdf49ab865bf52af11633ae.jpg

Python中的高阶函数

高阶函数可以将函数作为参数并返回函数。一个例子是:

f503062dca2c44899ddfdb3fbc038b2c.jpg

或者第二个定义,return functions,的一个简单例子是:

49c519e916f74ce18e1bda859e44cd91.jpg

高阶函数使非变化变量更容易处理。如果我们所做的只是在一系列函数中传递数据,那么我们就不需要在任何地方存储变量。

Python中的所有函数都是一级对象。当一个对象具有以下特性中的一个或多个时,我们将其定义为一级对象:

在运行时被创建

可以被赋值给一个变量或一个数据结构中的元素

作为参数被传递给函数

作为函数的结果被返回

因此Python中的所有函数都是一级函数,可以作为高阶函数使用。

带函数的部分应用

部分应用(也称为闭包)很奇怪,但是也很酷。我们可以调用一个函数而不提供它需要的所有参数。我们来在一个例子中看一下这一点。我们想要创建一个函数,它接受两个参数,一个基数和一个指数,然后返回基数的指数次方,就像这样:

c00e5da7821f40de818d3db7a1f49c2f.jpg

现在我们想要一个专用的平方函数,来使用power数求出一个数的平方:

b4ad11b2574a4f0c8f42aef5bf9c67d4.jpg

这是可行的,但如果我们想要一个立方函数呢?或者一个4次方函数呢?我们能一直写下去吗?嗯,我们可以。但是程序员是很懒的。如果我们重复地做同一件事时,这是一个信号,表明有一种更快的方法来加快做这些事情的速度,那将允许我们不再重复地做这些事情。我们可以在这里使用部分应用。让我们看一个使用了一个部分应用的平方函数的例子:

9fc678cf70a24dc39f6c937fe5585993.jpg

这难道不酷吗?我们可以通过告诉Python第二个参数是什么来只使用一个参数调用需要两个参数的函数。

我们还可以使用一个循环,来生成一个幂函数,其运行范围可以从立方到1000次幂。

4238081a69ee4941aa8fba15ca3c94f0.jpg

函数式编程不是Python化

您可能已经注意到了,我们在函数式编程中想要做的很多事情都是围绕列表进行的。除了reduce函数和部分应用外,我们所看到的所有函数都会生成列表。Guido (Python的发明者)不喜欢Python中的函数式的东西,因为Python已经有了自己的生成列表的方法。

如果我们在一个Python IDLE会话中输入"import this”,我们会得到:

a688e542f59544e18988105dbaf4e9e8.jpg

这就是Python之禅。这是一首关于某些东西Python化意味着什么的诗。这里我们要涉及的部分是:

应该有一种——最好是只有一种——显而易见的方法来做到它。

在Python中,map 与 filter可以做与列表表达式(接下来讨论)相同的事情。这打破了Python之禅中的一条规则,因此函数式编程的这些部分不是"Python式的”。

另一个话题是Lambda。在Python中,lambda函数是一个普通函数。Lambda是语法糖。这两个是等价的:

e3ad28c429d949a7b436cb8e72aaed19.jpg

一个普通函数可以做lambda函数所能做的所有事情,但反过来却不行。一个lambda函数不能完成一个普通函数所能完成的所有工作。

这是一个关于函数式编程为什么不能很好地适应整个Python生态系统的简短讨论。您可能已经注意到我之前提到过列表推导式,我们现在将讨论它们。

列表推导式

之前我提到过,我们可以用列表推导式完成我们可以用map或filter所做的任何事情。这是我们要学习的关于它们的部分。

列表推导式是在Python中生成列表的一种方式。其语法是:

8240608030564b749335b3c3662bc405.jpg

让我们对一个列表中的每个数字进行平方,并以此作为一个例子:

3e8753cca848403784aec1ff9c603a0a.jpg

好吧,这样我们就可以看到我们如何将一个函数应用到列表中的每一项。我们如何来应用一个filter函数呢?好吧,看看这段之前的代码:

205703826e9b4befb0fb2e04eec44901.jpg

我们可以像这样来把它转换成一个列表推导式:

970de34c64714f3e8e27b00328e5ad74.jpg

列表推导式支持像这样的if语句。我们不需要再应用很多个函数来得到我们想要的东西了。如果我们试图创造使用列表的机会,那么使用列表推导式可能会更清晰、更容易一些。

如果我们想要对列表中所有小于0的数进行平方呢?那么,使用lambda、map和filter,我们会这样写:

230839636ef74c4a94c4c0303cce1fcc.jpg

这有点冗长而复杂。使用一个列表推导式,它就会变成这样:

81a78c8e95de4f7bb00eb9ea58f26e8d.jpg

列表推导式只对列表有好处。map和filter作用于任何可迭代对象之上,那是怎么回事呢?我们可以对遇到的任何可迭代对象使用任何推导式。

任何可迭代对象的推导式

我们可以使用一个推导式来生成任何可迭代对象。因为我们使用的是Python 2.7,所以,我们甚至可以生成一个字典(hashmap)。

24be213930dc495c8f0ae4065323c0ca.jpg

如果它是一个可迭代对象,我们可以生成它。我们来看集合的最后一个例子。如果您不知道集合是什么,请查看我写的另一篇文章(https://skerritt.blog/a-primer-on-set-theory/ )。其中的TL;DR(集合定义)是:

集合是元素的列表,该列表中没有重复的元素

集合的顺序无关紧要。

0610458df24c4f1fb84a60edac206a5d.jpg

您可能会注意到,集合具有与字典相同的花括号。Python是很聪明的。它会根据我们是否为字典提供额外的值来判断我们写的是一个字典推导式还是一个集合推导式。如果您想了解更多关于推导式的内容,请查看这个可视化指南。

结论

函数式编程是漂亮而纯粹的。函数式代码可以是简洁的,但也可能是混乱的。您应该根据需要去使用它。英文原文:https://skerritt.blog/learn-functional-python-in-10-minutes/

译者:测试

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 7-2 jmu-python-分段函数是指在Python编程语言中,实现分段函数的计算和绘制。分段函数是指在不同的区间内,函数的表达不同。在Python中,可以使用if语句或者numpy库中的where函数来实现分段函数的计算。同时,也可以使用matplotlib库来绘制分段函数的图像。 ### 回答2: 7-2 jmu-python-分段函数是一道Python编程题目,要求我们编写一个程序,计算分段函数的值。分段函数是一种常用的数学函数,其定义域被划分为若干个子域,在每个子域里函数的表达是不同的。 对于这个问题,我们可以采用Python语言编写一个函数来求解。具体来说,我们可以先将不同的子域分别处理成单独的函数,在不同的子域里分别调用不同的函数,最终得到分段函数的值。 首先,我们需要确定定义域并将其划分为不同的子域。假设我们需要求解的分段函数为f(x) = x^2-2x+1(x<1); f(x) = x+2 (1<=x<10); f(x) = x^3-20 (x>=10)。可以看出,定义域被分为了三个子域:x<1、1<=x<10、x>=10。 接下来,我们可以分别编写三个子函数,分别用来计算在不同子域内的函数值。对于每个子函数,我们需要传入一个参数x(x为定义域内的一个值),并返回对应函数在该点的函数值。 def func1(x): return x**2 - 2*x + 1 def func2(x): return x + 2 def func3(x): return x**3 - 20 最后,我们可以编写一个分段函数的总函数,根据输入的x的不同值,分别调用不同的函数进行计算。 def piecewise_function(x): if x < 1: return func1(x) elif 1 <= x <10: return func2(x) else: return func3(x) 这样,我们就可以通过调用piecewise_function来计算任意一个x在分段函数中的函数值。 ### 回答3: 7-2 jmu-python-分段函数 分段函数是指函数的定义域被分成多个区间,在每个区间内采用不同的函数表达来表示函数的值。在图像上,分段函数表现出多段直线或曲线组成的形态。分段函数是高中数学中的一个重要概念,可以解决很多实际问题,如利润函数、收益函数等。 Python中可以通过if语句来实现分段函数的计算。首先要确定函数定义域的区间,然后在每个区间内使用不同的函数表达计算函数值。具体实现过程如下: 1.确定函数的定义域区间,例如定义域为(-∞,2),[2,5),[5,+∞)。 2.定义一个函数名,例如f(x)。 3.使用if语句,在每个区间内使用不同的函数表达计算函数值。 def f(x): if x<2: return x**2+3 elif x>=2 and x<5: return 2*x+1 else: return 4*x-3 4.调用函数,输入自变量x的值,求得函数值。 print(f(1)) # 输出4 print(f(3)) # 输出7 print(f(6)) # 输出21 上述代码中,函数f(x)的定义域被分成了三个区间:(-∞,2),[2,5),[5,+∞),并在每个区间内使用不同的函数表达计算函数值。通过if语句的条件判断,可以实现对不同区间的函数表达的选择。调用函数时,输入自变量x的值,就可以得到对应的函数值。 总之,Python中可以通过if语句实现分段函数的计算,这对于解决实际问题有很大的帮助。在编写代码时,需要注意定义域的分段和函数表达的选择,以便正确计算函数值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值