函数式编程既美观又纯粹。功能代码可以很干净,但是也可能很凌乱。一些顽固的Python程序员不喜欢Python的功能范式。您应该使用想要使用的工具,并使用最好的工具完成工作。在本文中,您将学习什么是函数范例以及如何在Python中使用函数编程。您还将了解列表理解和其他形式的理解。下面让我们从功能范式先开始吧。
功能范式
在命令式范例中,您可以通过向计算机分配一系列任务来执行操作,然后由计算机执行这些任务。在执行它们时,它可以更改状态。
例如,假设您设置了A=5,然后再更改A。从某种意义上说,变量是变量内部的值变化的。
在功能范式中,您不告诉计算机该做什么,而是告诉它什么东西。数的最大公约数是多少,乘积是什么1ton是,依此类推。
变量不能改变。设置变量后,它会一直保持这种状态(请注意,在纯函数式语言中,它们不称为变量)。因此,功能在功能范式中没有副作用。副作用是函数在函数外部进行了更改。
让我们看一些典型的Python代码示例:
a=3defsome_func():
globala
a=5
some_func()
print(a)
该代码的输出为5。在函数范式中,更改变量是一个很大的禁忌,而让函数影响超出其范围的事物也是一个很大的禁忌。函数唯一可以做的就是计算并返回。
现在您可能会想:“没有变量,没有副作用吗?为什么这么好?”。好问题,讨厌的陌生人读这篇。
如果使用相同的参数两次调用一个函数,则可以保证返回相同的结果。如果您已经了解了数学函数,那么您将感激不尽。
我们称之为参照透明性。由于函数没有副作用,因此,如果我们要构建一个用于计算事物的程序,则可以加快程序速度。如果程序知道func(2)等于3,我们可以将其存储在表格中。当我们已经知道答案时,这可以防止程序运行相同的功能。
通常,在函数式编程中,我们不使用循环。我们使用递归。递归是一个数学概念,它意味着“馈入自身”。使用递归函数时,该函数将自身称为子函数。
这是Python中递归函数的一个很好的示例:
deffactorial_recursive(n):
#Basecase:1!=1
ifn==1:
return1
#Recursivecase:n!=n*(n-1)!
else:
returnn*factorial_recursive(n-1)
一些编程语言也很懒。这意味着他们直到最后一秒钟才进行计算或执行任何操作。如果我们写一些代码来执行2+2,功能程序仅在需要使用结果时才进行计算。我们将很快探索Python中的惰性。
地图
为了了解地图,让我们首先看看什么是可迭代的。可迭代是您可以迭代的任何事物。这些是列表或数组,但是Python具有许多不同的可迭代项。您甚至可以创建自己的对象,这些对象可以通过实现魔术方法进行迭代。魔术方法就像一个API,可以帮助您的对象变得更加Pythonic。
您需要实现2种魔术方法以使对象可迭代:
classCounter:
def__init__(self,low,high):
#setclassattributesinsidethemagicmethod__init__
#for“inistalise”
self.current=low
self.high=high
def__iter__(self):
#firstmagicmethodtomakethisobjectiterable
returnself
def__next__(self):
#secondmagicmethod
ifself.current>self.high:
raiseStopIteration
else:
self.current+=1
returnself.current-1
第一种魔术方法__iter__或dunderiter(双下划线iter)返回迭代对象,我们经常在循环开始时使用它。接下来是__next__,返回下一个对象。
让我们进入快速终端会话并检查一下:
forcinCounter(3,8):
print(c)
这将打印:
3、4、5、6、7、8
在Python中,迭代器是仅包含一个__iter__魔术方法。这意味着我们可以访问对象中的位置,但是不能遍历对象。一些对象将具有魔术方法__next__而不是__iter__魔术方法。
现在我们知道了一个可迭代的对象,让我们回到map函数。map函数使我们可以将函数应用于可迭代的每个项目。我们希望对列表中的每个项目都应用一个函数,但要知道大多数迭代器都有可能使用该函数。Map需要2个输入,即要应用的功能和可迭代的对象。
map(function,iterable)
假设我们有一个数字列表,如下所示:[1,2,3,4,5]
我们想要对每个数字取平方,可以编写如下代码:
x=[1,2,3,4,5]
defsquare(num):
returnnum*num
print(list(map(square,x)))
功能性Python很懒。如果我们不包括list()该函数将存储可迭代对象的定义,而不是列表本身。我们需要告诉Python“将其转换为列表”以供我们使用。
在Python中突然从非延迟评估转到延迟评估很奇怪。如果您在功能性思维方式上的思考多于命令式思维,则您会习惯于它。
现在写一个普通的函数就像square(num)但是看起来不对。我们只需要在地图中使用一次就定义一个整体功能?好了,我们可以使用lambda(匿名)函数在map中定义一个函数。
Lambda表达式
Lambda表达式是单行函数。以这个lambda表达式为例,该表达式平方一个给定的数字:
square=lambdax:x*x
现在运行此命令:
>>>square(3)
9
我听到你了“布兰登,争论在哪里?这到底是什么?看起来不像功能吗?”
好吧,这很混乱,但是可以解释。我们正在给变量赋值square。
这部分:
lambdax:
告诉Python这是一个lambda函数,输入名为x。冒号之后的所有内容都是我们对输入所做的事情,并且返回的结果是什么。
要将平方程序简化为一行,我们可以执行以下操作:
x=[1,2,3,4,5]
print(list(map(lambdanum:num*num,x)))
在lambda表达式中,所有参数都在左侧,而您要使用它们的内容在右侧。它有点混乱,没有人可以否认。编写只有其他函数式程序员才能阅读的代码,这是有一定乐趣的。另外,将一项功能转换为单行代码也很酷。
减少
Reduce是一种将可迭代变成一件事的功能。通常,我们将对列表执行计算以将其减少到一个数字。
减少看起来像这样:
reduce(function,list)
我们可以(并且经常会)使用lambda表达式作为函数。
列表的乘积是将每个数字相乘。
要对此编程:
product=1
x=[1,2,3,4]
fornuminx:
product=product*num
但是使用reduce可以编写:
fromfunctoolsimportreduce
product=reduce((lambdax,y:x*y),[1,2,3,4])
要获得相同的产品。代码更短,并且具有函数式编程知识,因此更加整洁。
过滤
filter函数接受一个可迭代的对象,并过滤掉该可迭代对象中所有不需要的东西。
过滤器具有一个功能和一个列表。它将函数应用于列表中的每个项目,如果该函数返回True,则不执行任何操作。
如果返回False,则将其从列表中删除。
语法如下:
filter(function,list)
让我们看一个小例子,没有过滤器,我们将编写:
x=range(-5,5)
new_list=[]
fornuminx:
ifnum<0:
new_list.append(num)
使用filter,它将变为:
x=range(-5,5)
all_less_than_zero=list(filter(lambdanum:num<0,x))
高阶函数
高阶函数可以将函数用作参数并返回函数。
一个非常简单的示例如下所示:
defsummation(nums):
returnsum(nums)
defaction(func,numbers):
returnfunc(numbers)
print(action(summation,[1,2,3]))
#Outputis6
或第二个定义的简单示例,returnfunctions,是:
defrtnBrandon():
return“brandon”
defrtnJohn():
return“john”
defrtnPerson():
age=int(input(“What’syourage?”))
ifage==21:
returnrtnBrandon()
else:
returnrtnJohn()
您之前知道我怎么说纯函数式编程语言没有变量吗?
好吧,高阶函数使此操作更容易。
如果我们要做的只是通过长函数通道传递数据,则无需在任何地方存储变量。
Python中的所有函数都是一流的对象。
我们将一流对象定义为具有以下一个或多个功能:
·在运行时创建
·分配给数据结构中的变量或元素
·作为参数传递给函数
·作为函数的结果返回
因此,Python中的所有函数都是一流的,可以用作高阶函数。
部分申请
部分应用程序(也称为闭包)很奇怪,但是很酷。我们可以在不提供所需所有参数的情况下调用函数。我们来看一个例子。
我们想要创建一个函数,该函数接受2个参数(一个底数和一个指数),然后将base返回给指数的幂,如下所示:
defpower(base,exponent):
returnbase**exponent
现在我们想要一个专用的平方函数,使用幂函数计算一个数字的平方:
defsquare(base):
returnpower(base,2)
这行得通,但是如果我们想要多维数据集函数怎么办?还是4的幂的函数?我们可以永远继续写它们吗?好吧,我们可以。
但是程序员很懒。如果我们重复做同样的事情,则表明有一种更快的方法可以加快处理速度,这将使我们不再重复处理。我们可以在这里使用部分应用程序。
让我们看一下使用部分应用程序的平方函数的示例:
fromfunctoolsimportpartial
square=partial(power,exponent=2)
print(square(2))
#outputis4
那不是很酷!通过告诉Python第二个参数是什么,我们可以仅使用1个参数来调用需要2个参数的函数。
我们还可以使用循环来生成幂函数,该幂函数从立方到1000的幂一直有效。
fromfunctoolsimportpartial
powers=[]
forxinrange(2,1001):
powers.append(partial(power,exponent=x))
print(powers[0](3))
#outputis9
函数式编程不是Pythonic
您可能已经注意到,但是我们在函数式编程中要做的许多事情都围绕列表进行。除了reduce函数和部分应用程序之外,您看到的所有函数都会生成列表。
如果将“importthis”写入“PythonIDLE”会话,则会得到:
>>>importthis
TheZenofPython,byTimPeters
Beautifulisbetterthanugly.
Explicitisbetterthanimplicit.
Simpleisbetterthancomplex.
Complexisbetterthancomplicated.
Flatisbetterthannested.
Sparseisbetterthandense.
Readabilitycounts.
Specialcasesaren’tspecialenoughtobreaktherules.
Althoughpracticalitybeatspurity.
Errorsshouldneverpasssilently.
Unlessexplicitlysilenced.
Inthefaceofambiguity,refusethetemptationtoguess.
Thereshouldbeone--andpreferablyonlyone--obviouswaytodoit.
Althoughthatwaymaynotbeobviousatfirstunlessyou’reDutch.
Nowisbetterthannever.
Althoughneverisoftenbetterthan*right*now.
Iftheimplementationishardtoexplain,it’sabadidea.
Iftheimplementationiseasytoexplain,itmaybeagoodidea.
Namespacesareonehonkinggreatidea--let’sdomoreofthose!
这就是Python的禅宗。这是一首关于Pythonic意味着什么的诗。
我们要在这里涉及的部分是:
Thereshouldbeone — andpreferablyonlyone — obviouswaytodoit.
在Python中,地图和过滤器可以做与列表理解相同的事情。这打破了PythonZen的一条规则,因此函数编程的这些部分是“pythonic”。
另一个话题是Lambda。在Python中,lambda函数是常规函数。Lambda是语法糖。
两者是等效的:
foo=lambdaa:2
deffoo(a):
return2
常规函数可以执行lambda函数可以做的所有事情,但是反之则不行。Lambda函数无法执行常规函数可以执行的所有操作。
这是关于为什么函数式编程不能很好地适合整个Python生态系统的简短争论。
您可能已经注意到我之前提到过列表理解,现在我们将讨论它们。
清单理解
之前,我提到过您可以使用map或filter进行的任何操作,也可以使用列表理解的方法。这是我们将了解它们的部分。列表理解是一种在Python中生成列表的方法。
语法为:
[functionforiteminiterable]
因此,让我们对列表中的每个数字取平方,例如:
print([x*xforxin[1,2,3,4]])
好的,所以我们可以看到如何将函数应用于列表中的每个项目。我们如何应用过滤器?
好吧,请看一下前面的代码:
x=range(-5,5)
all_less_than_zero=list(filter(lambdanum:num<0,x))
print(all_less_than_zero)
我们可以这样将其转换为列表理解:
x=range(-5,5)
all_less_than_zero=[numfornuminxifnum<0]
列表推导支持if这样的语句。您不再需要将一百万个函数应用于某些对象即可获得所需的东西。实际上,如果我们要尝试某种列表机会,那么使用列表理解将使它看起来更干净,更容易。
如果我们想对列表中0以下的每个数字求平方怎么办?
好吧,使用lambda,map和filter,我们将编写:
x=range(-5,5)
all_less_than_zero=list(map(lambdanum:num*num,list(filter(lambdanum:num<0,x))))
这是漫长而复杂的。通过列表理解,它是:
x=range(-5,5)
all_less_than_zero=[num*numfornuminxifnum<0]
列表理解仅对列表有益。映射和过滤器在任何可迭代的地方都可以工作,那又是怎么回事?我们可以对遇到的任何可迭代对象使用任何理解。
其他理解
我们可以使用理解来生成任何可迭代的。从Python2.7开始,我们甚至可以生成一个字典(hashmap)。
#Takenfrompage70chapter3ofFluentPythonbyLucianoRamalho
DIAL_CODES=[(86,‘China’),
(91,‘India’),
(1,‘UnitedStates’),
(62,‘Indonesia’),
(55,‘Brazil’),
(92,‘Pakistan’),
(880,‘Bangladesh’),
(234,‘Nigeria’),
(7,‘Russia’),
(81,‘Japan’),
]
>>>country_code={country:codeforcode,countryinDIAL_CODES}
>>>country_code
{’Brazil’:55,‘Indonesia’:62,‘Pakistan’:92,‘Russia’:7,‘China’:86,‘UnitedStates’:1,‘Japan’:81,‘India’:91,‘Nigeria’:234,‘Bangladesh’:880}
>>>{code:country.upper()forcountry,codeincountry_code.items()ifcode<66}
{1:‘UNITEDSTATES’,7:‘RUSSIA’,62:‘INDONESIA’,55:‘BRAZIL’}
如果是可迭代的,我们可以生成它。让我们看一下集合的最后一个例子。
TL;DR为:
·集是元素列表,该列表中没有元素重复两次
·套装顺序无所谓
#takenfrompage87,chapter3ofFluentPythonbyLucianoRamalho
>>>fromunicodedataimportname
>>>{chr(i)foriinrange(32,256)if‘SIGN’inname(chr(i),‘’)}
{’×’,‘¥’,‘°’,‘£’,‘©’,‘¬’,‘%’,‘µ’,‘>‘,‘¤’,‘±’,‘¶’,‘§’,‘<’,‘=’,‘®’,‘$’,‘÷’,‘¢’,‘+’}
您可能会注意到,集合具有与字典相同的花括号。Python很聪明。它会根据您是否为字典提供额外的价值而知道您是在编写字典理解还是集合理解。
上述就是关于Python语法功能有哪些的全部内容,想了解更多关于Python的信息,请继续关注。