函數式編程既美觀又純粹。功能代碼可以很干凈,但是也可能很凌亂。一些頑固的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的信息,請繼續關注中培偉業。