函數(shù)式編程既美觀又純粹。功能代碼可以很干凈,但是也可能很凌亂。一些頑固的Python程序員不喜歡Python的功能范式。您應(yīng)該使用想要使用的工具,并使用最好的工具完成工作。在本文中,您將學(xué)習(xí)什么是函數(shù)范例以及如何在Python中使用函數(shù)編程。您還將了解列表理解和其他形式的理解。下面讓我們從功能范式先開(kāi)始吧。
功能范式
在命令式范例中,您可以通過(guò)向計(jì)算機(jī)分配一系列任務(wù)來(lái)執(zhí)行操作,然后由計(jì)算機(jī)執(zhí)行這些任務(wù)。在執(zhí)行它們時(shí),它可以更改狀態(tài)。
例如,假設(shè)您設(shè)置了A=5,然后再更改A。從某種意義上說(shuō),變量是變量?jī)?nèi)部的值變化的。
在功能范式中,您不告訴計(jì)算機(jī)該做什么,而是告訴它什么東西。數(shù)的最大公約數(shù)是多少,乘積是什么1ton是,依此類推。
變量不能改變。設(shè)置變量后,它會(huì)一直保持這種狀態(tài)(請(qǐng)注意,在純函數(shù)式語(yǔ)言中,它們不稱為變量)。因此,功能在功能范式中沒(méi)有副作用。副作用是函數(shù)在函數(shù)外部進(jìn)行了更改。
讓我們看一些典型的Python代碼示例:
a=3defsome_func():
globala
a=5
some_func()
print(a)
該代碼的輸出為5。在函數(shù)范式中,更改變量是一個(gè)很大的禁忌,而讓函數(shù)影響超出其范圍的事物也是一個(gè)很大的禁忌。函數(shù)唯一可以做的就是計(jì)算并返回。
現(xiàn)在您可能會(huì)想:“沒(méi)有變量,沒(méi)有副作用嗎?為什么這么好?”。好問(wèn)題,討厭的陌生人讀這篇。
如果使用相同的參數(shù)兩次調(diào)用一個(gè)函數(shù),則可以保證返回相同的結(jié)果。如果您已經(jīng)了解了數(shù)學(xué)函數(shù),那么您將感激不盡。
我們稱之為參照透明性。由于函數(shù)沒(méi)有副作用,因此,如果我們要構(gòu)建一個(gè)用于計(jì)算事物的程序,則可以加快程序速度。如果程序知道func(2)等于3,我們可以將其存儲(chǔ)在表格中。當(dāng)我們已經(jīng)知道答案時(shí),這可以防止程序運(yùn)行相同的功能。
通常,在函數(shù)式編程中,我們不使用循環(huán)。我們使用遞歸。遞歸是一個(gè)數(shù)學(xué)概念,它意味著“饋入自身”。使用遞歸函數(shù)時(shí),該函數(shù)將自身稱為子函數(shù)。
這是Python中遞歸函數(shù)的一個(gè)很好的示例:
deffactorial_recursive(n):
#Basecase:1!=1
ifn==1:
return1
#Recursivecase:n!=n*(n-1)!
else:
returnn*factorial_recursive(n-1)
一些編程語(yǔ)言也很懶。這意味著他們直到最后一秒鐘才進(jìn)行計(jì)算或執(zhí)行任何操作。如果我們寫一些代碼來(lái)執(zhí)行2+2,功能程序僅在需要使用結(jié)果時(shí)才進(jìn)行計(jì)算。我們將很快探索Python中的惰性。
地圖
為了了解地圖,讓我們首先看看什么是可迭代的。可迭代是您可以迭代的任何事物。這些是列表或數(shù)組,但是Python具有許多不同的可迭代項(xiàng)。您甚至可以創(chuàng)建自己的對(duì)象,這些對(duì)象可以通過(guò)實(shí)現(xiàn)魔術(shù)方法進(jìn)行迭代。魔術(shù)方法就像一個(gè)API,可以幫助您的對(duì)象變得更加Pythonic。
您需要實(shí)現(xiàn)2種魔術(shù)方法以使對(duì)象可迭代:
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
第一種魔術(shù)方法__iter__或dunderiter(雙下劃線iter)返回迭代對(duì)象,我們經(jīng)常在循環(huán)開(kāi)始時(shí)使用它。接下來(lái)是__next__,返回下一個(gè)對(duì)象。
讓我們進(jìn)入快速終端會(huì)話并檢查一下:
forcinCounter(3,8):
print(c)
這將打印:
3、4、5、6、7、8
在Python中,迭代器是僅包含一個(gè)__iter__魔術(shù)方法。這意味著我們可以訪問(wèn)對(duì)象中的位置,但是不能遍歷對(duì)象。一些對(duì)象將具有魔術(shù)方法__next__而不是__iter__魔術(shù)方法。
現(xiàn)在我們知道了一個(gè)可迭代的對(duì)象,讓我們回到map函數(shù)。map函數(shù)使我們可以將函數(shù)應(yīng)用于可迭代的每個(gè)項(xiàng)目。我們希望對(duì)列表中的每個(gè)項(xiàng)目都應(yīng)用一個(gè)函數(shù),但要知道大多數(shù)迭代器都有可能使用該函數(shù)。Map需要2個(gè)輸入,即要應(yīng)用的功能和可迭代的對(duì)象。
map(function,iterable)
假設(shè)我們有一個(gè)數(shù)字列表,如下所示:[1,2,3,4,5]
我們想要對(duì)每個(gè)數(shù)字取平方,可以編寫如下代碼:
x=[1,2,3,4,5]
defsquare(num):
returnnum*num
print(list(map(square,x)))
功能性Python很懶。如果我們不包括list()該函數(shù)將存儲(chǔ)可迭代對(duì)象的定義,而不是列表本身。我們需要告訴Python“將其轉(zhuǎn)換為列表”以供我們使用。
在Python中突然從非延遲評(píng)估轉(zhuǎn)到延遲評(píng)估很奇怪。如果您在功能性思維方式上的思考多于命令式思維,則您會(huì)習(xí)慣于它。
現(xiàn)在寫一個(gè)普通的函數(shù)就像square(num)但是看起來(lái)不對(duì)。我們只需要在地圖中使用一次就定義一個(gè)整體功能?好了,我們可以使用lambda(匿名)函數(shù)在map中定義一個(gè)函數(shù)。
Lambda表達(dá)式
Lambda表達(dá)式是單行函數(shù)。以這個(gè)lambda表達(dá)式為例,該表達(dá)式平方一個(gè)給定的數(shù)字:
square=lambdax:x*x
現(xiàn)在運(yùn)行此命令:
>>>square(3)
9
我聽(tīng)到你了“布蘭登,爭(zhēng)論在哪里?這到底是什么?看起來(lái)不像功能嗎?”
好吧,這很混亂,但是可以解釋。我們正在給變量賦值square。
這部分:
lambdax:
告訴Python這是一個(gè)lambda函數(shù),輸入名為x。冒號(hào)之后的所有內(nèi)容都是我們對(duì)輸入所做的事情,并且返回的結(jié)果是什么。
要將平方程序簡(jiǎn)化為一行,我們可以執(zhí)行以下操作:
x=[1,2,3,4,5]
print(list(map(lambdanum:num*num,x)))
在lambda表達(dá)式中,所有參數(shù)都在左側(cè),而您要使用它們的內(nèi)容在右側(cè)。它有點(diǎn)混亂,沒(méi)有人可以否認(rèn)。編寫只有其他函數(shù)式程序員才能閱讀的代碼,這是有一定樂(lè)趣的。另外,將一項(xiàng)功能轉(zhuǎn)換為單行代碼也很酷。
減少
Reduce是一種將可迭代變成一件事的功能。通常,我們將對(duì)列表執(zhí)行計(jì)算以將其減少到一個(gè)數(shù)字。
減少看起來(lái)像這樣:
reduce(function,list)
我們可以(并且經(jīng)常會(huì))使用lambda表達(dá)式作為函數(shù)。
列表的乘積是將每個(gè)數(shù)字相乘。
要對(duì)此編程:
product=1
x=[1,2,3,4]
fornuminx:
product=product*num
但是使用reduce可以編寫:
fromfunctoolsimportreduce
product=reduce((lambdax,y:x*y),[1,2,3,4])
要獲得相同的產(chǎn)品。代碼更短,并且具有函數(shù)式編程知識(shí),因此更加整潔。
過(guò)濾
filter函數(shù)接受一個(gè)可迭代的對(duì)象,并過(guò)濾掉該可迭代對(duì)象中所有不需要的東西。
過(guò)濾器具有一個(gè)功能和一個(gè)列表。它將函數(shù)應(yīng)用于列表中的每個(gè)項(xiàng)目,如果該函數(shù)返回True,則不執(zhí)行任何操作。
如果返回False,則將其從列表中刪除。
語(yǔ)法如下:
filter(function,list)
讓我們看一個(gè)小例子,沒(méi)有過(guò)濾器,我們將編寫:
x=range(-5,5)
new_list=[]
fornuminx:
ifnum<0:
new_list.append(num)
使用filter,它將變?yōu)椋?/p>
x=range(-5,5)
all_less_than_zero=list(filter(lambdanum:num<0,x))
高階函數(shù)
高階函數(shù)可以將函數(shù)用作參數(shù)并返回函數(shù)。
一個(gè)非常簡(jiǎn)單的示例如下所示:
defsummation(nums):
returnsum(nums)
defaction(func,numbers):
returnfunc(numbers)
print(action(summation,[1,2,3]))
#Outputis6
或第二個(gè)定義的簡(jiǎn)單示例,returnfunctions,是:
defrtnBrandon():
return“brandon”
defrtnJohn():
return“john”
defrtnPerson():
age=int(input(“What’syourage?”))
ifage==21:
returnrtnBrandon()
else:
returnrtnJohn()
您之前知道我怎么說(shuō)純函數(shù)式編程語(yǔ)言沒(méi)有變量嗎?
好吧,高階函數(shù)使此操作更容易。
如果我們要做的只是通過(guò)長(zhǎng)函數(shù)通道傳遞數(shù)據(jù),則無(wú)需在任何地方存儲(chǔ)變量。
Python中的所有函數(shù)都是一流的對(duì)象。
我們將一流對(duì)象定義為具有以下一個(gè)或多個(gè)功能:
·在運(yùn)行時(shí)創(chuàng)建
·分配給數(shù)據(jù)結(jié)構(gòu)中的變量或元素
·作為參數(shù)傳遞給函數(shù)
·作為函數(shù)的結(jié)果返回
因此,Python中的所有函數(shù)都是一流的,可以用作高階函數(shù)。
部分申請(qǐng)
部分應(yīng)用程序(也稱為閉包)很奇怪,但是很酷。我們可以在不提供所需所有參數(shù)的情況下調(diào)用函數(shù)。我們來(lái)看一個(gè)例子。
我們想要?jiǎng)?chuàng)建一個(gè)函數(shù),該函數(shù)接受2個(gè)參數(shù)(一個(gè)底數(shù)和一個(gè)指數(shù)),然后將base返回給指數(shù)的冪,如下所示:
defpower(base,exponent):
returnbase**exponent
現(xiàn)在我們想要一個(gè)專用的平方函數(shù),使用冪函數(shù)計(jì)算一個(gè)數(shù)字的平方:
defsquare(base):
returnpower(base,2)
這行得通,但是如果我們想要多維數(shù)據(jù)集函數(shù)怎么辦?還是4的冪的函數(shù)?我們可以永遠(yuǎn)繼續(xù)寫它們嗎?好吧,我們可以。
但是程序員很懶。如果我們重復(fù)做同樣的事情,則表明有一種更快的方法可以加快處理速度,這將使我們不再重復(fù)處理。我們可以在這里使用部分應(yīng)用程序。
讓我們看一下使用部分應(yīng)用程序的平方函數(shù)的示例:
fromfunctoolsimportpartial
square=partial(power,exponent=2)
print(square(2))
#outputis4
那不是很酷!通過(guò)告訴Python第二個(gè)參數(shù)是什么,我們可以僅使用1個(gè)參數(shù)來(lái)調(diào)用需要2個(gè)參數(shù)的函數(shù)。
我們還可以使用循環(huán)來(lái)生成冪函數(shù),該冪函數(shù)從立方到1000的冪一直有效。
fromfunctoolsimportpartial
powers=[]
forxinrange(2,1001):
powers.append(partial(power,exponent=x))
print(powers[0](3))
#outputis9
函數(shù)式編程不是Pythonic
您可能已經(jīng)注意到,但是我們?cè)诤瘮?shù)式編程中要做的許多事情都圍繞列表進(jìn)行。除了reduce函數(shù)和部分應(yīng)用程序之外,您看到的所有函數(shù)都會(huì)生成列表。
如果將“importthis”寫入“PythonIDLE”會(huì)話,則會(huì)得到:
>>>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的禪宗。這是一首關(guān)于Pythonic意味著什么的詩(shī)。
我們要在這里涉及的部分是:
Thereshouldbeone?—?andpreferablyonlyone?—?obviouswaytodoit.
在Python中,地圖和過(guò)濾器可以做與列表理解相同的事情。這打破了PythonZen的一條規(guī)則,因此函數(shù)編程的這些部分是“pythonic”。
另一個(gè)話題是Lambda。在Python中,lambda函數(shù)是常規(guī)函數(shù)。Lambda是語(yǔ)法糖。
兩者是等效的:
foo=lambdaa:2
deffoo(a):
return2
常規(guī)函數(shù)可以執(zhí)行l(wèi)ambda函數(shù)可以做的所有事情,但是反之則不行。Lambda函數(shù)無(wú)法執(zhí)行常規(guī)函數(shù)可以執(zhí)行的所有操作。
這是關(guān)于為什么函數(shù)式編程不能很好地適合整個(gè)Python生態(tài)系統(tǒng)的簡(jiǎn)短爭(zhēng)論。
您可能已經(jīng)注意到我之前提到過(guò)列表理解,現(xiàn)在我們將討論它們。
清單理解
之前,我提到過(guò)您可以使用map或filter進(jìn)行的任何操作,也可以使用列表理解的方法。這是我們將了解它們的部分。列表理解是一種在Python中生成列表的方法。
語(yǔ)法為:
[functionforiteminiterable]
因此,讓我們對(duì)列表中的每個(gè)數(shù)字取平方,例如:
print([x*xforxin[1,2,3,4]])
好的,所以我們可以看到如何將函數(shù)應(yīng)用于列表中的每個(gè)項(xiàng)目。我們?nèi)绾螒?yīng)用過(guò)濾器?
好吧,請(qǐng)看一下前面的代碼:
x=range(-5,5)
all_less_than_zero=list(filter(lambdanum:num<0,x))
print(all_less_than_zero)
我們可以這樣將其轉(zhuǎn)換為列表理解:
x=range(-5,5)
all_less_than_zero=[numfornuminxifnum<0]
列表推導(dǎo)支持if這樣的語(yǔ)句。您不再需要將一百萬(wàn)個(gè)函數(shù)應(yīng)用于某些對(duì)象即可獲得所需的東西。實(shí)際上,如果我們要嘗試某種列表機(jī)會(huì),那么使用列表理解將使它看起來(lái)更干凈,更容易。
如果我們想對(duì)列表中0以下的每個(gè)數(shù)字求平方怎么辦?
好吧,使用lambda,map和filter,我們將編寫:
x=range(-5,5)
all_less_than_zero=list(map(lambdanum:num*num,list(filter(lambdanum:num<0,x))))
這是漫長(zhǎng)而復(fù)雜的。通過(guò)列表理解,它是:
x=range(-5,5)
all_less_than_zero=[num*numfornuminxifnum<0]
列表理解僅對(duì)列表有益。映射和過(guò)濾器在任何可迭代的地方都可以工作,那又是怎么回事?我們可以對(duì)遇到的任何可迭代對(duì)象使用任何理解。
其他理解
我們可以使用理解來(lái)生成任何可迭代的。從Python2.7開(kāi)始,我們甚至可以生成一個(gè)字典(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’}
如果是可迭代的,我們可以生成它。讓我們看一下集合的最后一個(gè)例子。
TL;DR為:
·集是元素列表,該列表中沒(méi)有元素重復(fù)兩次
·套裝順序無(wú)所謂
#takenfrompage87,chapter3ofFluentPythonbyLucianoRamalho
>>>fromunicodedataimportname
>>>{chr(i)foriinrange(32,256)if‘SIGN’inname(chr(i),‘’)}
{’×’,‘¥’,‘°’,‘£’,‘?’,‘?’,‘%’,‘μ’,‘>‘,‘¤’,‘±’,‘?’,‘§’,‘<’,‘=’,‘?’,‘$’,‘÷’,‘¢’,‘+’}
您可能會(huì)注意到,集合具有與字典相同的花括號(hào)。Python很聰明。它會(huì)根據(jù)您是否為字典提供額外的價(jià)值而知道您是在編寫字典理解還是集合理解。
上述就是關(guān)于Python語(yǔ)法功能有哪些的全部?jī)?nèi)容,想了解更多關(guān)于Python的信息,請(qǐng)繼續(xù)關(guān)注中培偉業(yè)。