写 Python 代码不可不知的函数式编程技术

2023-06-05 0 770

Lizier Medium

译者:Raivat Shah

参予:恶魔、Jamin

写 Python 代码不可不知的函数式编程技术

责任编辑对 Python 中的表达式式Vaubecourt展开了单纯的进阶如是说。

近年,愈来愈王承恩采用表达式式程式设计(functional programming)。因而,许多现代的陈述句词汇(如 Java 和 Python)已经开始全力支持表达式式Vaubecourt。责任编辑对 Python 中的表达式式Vaubecourt展开了单纯的进阶如是说。

责任编辑适宜对表达式式程式设计有基本上介绍的听众。

写 Python 代码不可不知的函数式编程技术

责任编辑译者是香港科技大学计算机系统大学和「USP」学识渊博方案小学生 Raivat Shah,著眼于程式设计与统计数据科学研究。

丝尾表达式

在 Python 中,表达式是「丝尾国民」(first-class)。换句话说,表达式与其它正则表达式(如 int)处在公平话语权。

因而,我们可以将表达式赋值给变量,也可以将其作为参数传入其它表达式,将它们存储在其它统计数据结构(如 dicts)中,并将它们作为其它表达式的返回值。

把表达式作为对象

由于其它正则表达式(如 string、list 和 int)都是对象,那么表达式也是 Python 中的对象。我们来看示例表达式 foo,它将自己的名称打印出来:

deffoo():print(“foo”)

由于表达式是对象,因而我们可以将表达式 foo 赋值给任意变量,然后调用该变量。例如,我们可以将表达式赋值给变量 bar:

bar=foobar()#willprint”foo”totheconsole

语句 bar = foo 将表达式 foo 引用的对象赋值给变量 bar。

把对象作为表达式

当对象可调用时(callable),它们与表达式一样,如 object()。这是通过 __call__ 方法实现的。

示例如下:

classGreeter:def__init__(self,greeting):self.greeting=greetingdef__call__(self,name):returnself.greeting+””+name

每一次配置 Greeter 类的对象时,我们都会创建一个新的对象,即打招呼时可以喊的新名字。如下所示:

morning=Greeter(“goodmorning”)#createsthecallableobjectmorning(“john”)#callingtheobject#prints”goodmorningjohn”totheconsole

我们可以调用 morning 对象的原因在于,我们已经在类定义中采用了 __call__ 方法。为了检查对象是否可调用,我们采用内置表达式 callable:

callable(morning)#truecallable(145)#false.intisnotcallable.

统计数据结构内的表达式

表达式和其它对象一样,可以存储在统计数据结构内部。例如,我们可以创建 int to func 的字典。当 int 是待执行步骤的简写时,这就会派上用场。

#storeindictionarymapping={0:foo,1:bar}x=input()#getintegervaluefromusermapping[x]()#callthefuncreturnedbydictionaryaccess

类似地,表达式也可以存储在多种其它统计数据结构中。

把表达式作为参数和返回值

表达式还可以作为其它表达式的参数和返回值。接受表达式作为输入或返回表达式的表达式叫做高阶表达式,它是表达式式程式设计的重要组成部分。

高阶表达式具备强大的能力。就像《Eloquent JavaScript》中解释的那样:

「高阶表达式允许我们对动作执行抽象,而不只是抽象数值。」

我们来看一个例子。假设我们想对一个项目列表(list of items)执行迭代,并将其顺序打印出来。我们可以轻松构建一个 iterate 表达式:

defiterate(list_of_items):foriteminlist_of_items:print(item)

看起来很酷吧,但这只不过是一级抽象而已。如果我们想在对列表执行迭代时展开打印以外的其它操作要怎么做呢?

这就是高阶表达式存在的意义。我们可以创建表达式 iterate_custom,待执行迭代的列表和要对每个项应用的表达式都是 iterate_custom 表达式的输入:

defiterate_custom(list_of_items,custom_func):foriteminlist_of_items:custom_func(item)

这看起来微不足道,但其实非常强大。

我们已经把抽象的级别提高了一层,使标识符具备更强的可重用性。现在,我们不仅可以在打印列表时调用该表达式,还可以对涉及序列迭代的列表执行任意操作。

表达式还能被返回,从而使事情变得更加单纯。就像我们在 dict 中存储表达式一样,我们还可以将表达式作为控制语句,来决定适宜的表达式。例如:

defadd(x,y):returnx+ydefsub(x,y):returnx-ydefmult(x,y):returnx*ydefcalculator(opcode):ifopcode==1:returnaddelifopcode==2:returnsubelse:returnmultmy_calc=calculator(2)#mycalcisasubtractormy_calc(5,4)#returns5-4=1my_calc=calculator(9)#mycalcisnowamultipliermy_calc(5,4)#returns5x4=20.

嵌套表达式

表达式还可以在其它表达式内部,这就是「内部表达式」。内部表达式在创建辅助表达式时非常有用,辅助表达式即作为子模块来全力支持主表达式的小型可重用表达式。

在问题需要特定表达式定义(参数类型或顺序)时,我们可以采用辅助表达式。这种不遵循现代做法的操作使得解决问题变得更加单纯,示例参见:http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/lect4-2×3.pdf。

假设你想定义一个斐波那契表达式 fib(n),该表达式只有一个参数 n,我们必须返回第 n 个斐波那契数。

定义此类表达式的一种可行方式是:采用辅助表达式来追踪斐波那契数列的前两个项(因为斐波那契数是前两个数之和)。

deffib(n):deffib_helper(fk1,fk,k):ifn==k:returnfkelse:returnfib_helper(fk,fk1+fk,k+1)ifn<=1:returnnelse:returnfib_helper(0,1,1)

将该计算从表达式主体移到表达式参数,这具备非常强大的力量。因为它减少了递归方法中可能出现的冗余计算。

单表达式表达式(Lambda 表达式)

如果我们想在未给表达式命名之前写一个表达式要怎么做?如果我们想写一个简短的单行表达式(如上述示例中的表达式 foo 或 mult)要怎么做?

我们可以在 Python 中采用 lambda 关键字来定义此类表达式。示例如下:

mult=lambdax,y:x*ymult(1,2)#returns2

该 mult 表达式的行为与采用现代 def 关键字定义表达式的行为相同。

注意:lambda 表达式必须为单行,且不能包含程序员写的返回语句。

事实上,它们通常具备隐式的返回语句(在上面的示例中,表达式想表达 return x * y,不过我们省略了 lambda 表达式中的显式返回语句)。

lambda 表达式更加强大和精准,因为我们还可以构建匿名表达式(即没有名称的表达式):

(lambdax,y:x*y)(9,10)#returns90

当我们只需要一次性采用某表达式时,这种方法非常方便。例如,当我们想填充字典时:

importcollectionspre_fill=collections.defaultdict(lambda:(0,0))#alldictionarykeysandvaluesaresetto0

接下来我们来看 Map、Filter 和 Reduce,以更多地介绍 lambda。

Map、Filter 和 Reduce

Map

map 表达式基于指定过程(表达式)将输入集转换为另一个集合。这类似于上文提到的 iterate_custom 表达式。例如:

defmultiply_by_four(x):returnx*4scores=[3,6,8,3,5,7]modified_scores=list(map(multiply_by_four,scores))#modifiedscoresisnow[12,24,32,12,20,28]

在 Python 3 中,map 表达式返回的 map 对象可被类型转换为 list,以方便采用。现在,我们无需显式地定义 multiply_by_four 表达式,而是定义 lambda 表达式:

modified_scores=list(map(lambdax:4*x,scores))

当我们想对集合内的所有值执行某项操作时,map 表达式很有用。

Filter

就像名称所显示的那样,filter 表达式可以帮助筛除不想要的项。例如,我们想要去除 scores 中的奇数,那么我们可以采用 filter:

even_scores=list(filter(lambdax:Trueif(x%2==0)elseFalse,scores))#even_scores=[6,8]

由于提供给 filter 的表达式是逐个决定是否接受每一个项的,因而该表达式必须返回 bool 值,且该表达式必须是一元表达式(即只采用一个输入参数)。

Reduce

reduce 表达式用于「总结」或「概述」统计数据集。例如,如果我们想要计算所有分数的总和,就可以采用 reduce:

sum_scores=reduce((lambdax,y:x+y),scores)#sum_scores=32

这要比写循环语句单纯多了。注意:提供给 reduce 的表达式需要两个参数:一个表示正在接受检查的项,另一个表示所用运算的累积结果。

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务