python命令式编程的概念,【Python】十分钟学会函数式编程
點擊上方“小白學視覺”,選擇加"星標"或“置頂”重磅干貨,第一時間送達
本文轉自:深度學習這點小事
函數式編程到底是什么?本文將詳解其概念,同時分享怎樣在 Python 中使用函數式編程。主要內容包括列表解析式和其他形式的解析式。函數式模型
在命令式模型中,執行程序的方式是給計算機一系列指令讓它執行。執行過程中計算機會改變狀態。例如,比如 A 的初始值是 5,后來改變了 A 的值。那么 A 就是個變量,而變量的意思就是包含的值會改變。
而在函數式模式中,你不需要告訴計算機做什么,而是告訴計算機是什么。比如數字的最大公約數是什么,1 到 n 的乘積是什么等等。
因此,變量是不能被改變的。變量一旦被設置,就永遠保持同一個值(注意在純粹的函數式語言中,它們不叫變量)。因此,在函數式模型中,函數沒有副作用。副作用就是函數對函數外的世界做出的改變。來看看下面這段Python代碼的例子:a?=?3
def?some_func():
global?a
a?=?5
some_func()
print(a)
代碼的輸出是 5。在函數式模型中,改變變量的值是完全不允許的,讓函數影響函數外的世界也是不允許的。函數唯一能做的就是做一些計算然后返回一個值。
你可能會想:“沒有變量也沒有副作用?這有什么好的?”好問題。
如果函數使用同樣的參數調用兩次,那么我們可以保證它會返回同樣的結果。如果你學過數學函數,你肯定知道這樣做的好。這叫做引用透明性(referential transparency)。由于函數沒有副作用,那么我們可以加速計算某個東西的程序。比如,如果程序知道 func(2)返回 3,那么可以將這個值保存在表中,這樣就不需要重復運行我們早已知道結果的函數了。
通常,函數式編程不使用循環,而是使用遞歸。遞歸是個數學概念,通常的意思是“把結果作為自己的輸入”。使用遞歸函數,函數可以反復調用自己。下面就是個使用Python定義的遞歸函數的例子:def?factorial_recursive(n):
#?Base?case:?1!?=?1
if?n?==?1:
return?1
#?Recursive?case:?n!?=?n?*?(n-1)!
else:
return?n?*?factorial_recursive(n-1)
函數式編程語言也是懶惰的。懶惰的意思是,除非到最后一刻,否則它們不會執行計算或做任何操作。如果代碼要求計算2+2,那么函數式程序只有在真正用到計算結果的時候才會去計算。我們馬上就會介紹Python中的這種懶惰。映射
要理解映射(map),首先需要理解什么是可迭代對象。可迭代對象(iterable)指任何可以迭代的東西。通常是列表或數組,但 Python 還有許多其他可迭代對象。甚至可以自定義對象,通過實現特定的魔術方法使其變成可迭代對象。魔術方法就像 API 一樣,能讓對象更有 Python 風格。要讓對象變成可迭代對象,需要實現以下兩個魔術方法:class?Counter:
def?__init__(self,?low,?high):
#?set?class?attributes?inside?the?magic?method?__init__
#?for?"inistalise"
self.current?=?low
self.high?=?high
def?__iter__(self):
#?first?magic?method?to?make?this?object?iterable
return?self
def?__next__(self):
#?second?magic?method
if?self.current?>?self.high:
raise?StopIteration
else:
self.current?+=?1
return?self.current?-?1
第一個魔術方法“__iter__”(雙下劃線iter)返回迭代子,通常在循環開始時調用。__next__則返回迭代的下一個對象。
可以打開命令行試一下下面的代碼:for?c?in?Counter(3,?8):
print(c)
這段代碼將會輸出:3
4
5
6
7
8
在 Python 中,迭代器就是只實現了__iter__魔術方法的對象。也就是說,你可以訪問對象中都包含的位置,但無法遍歷整個對象。一些對象實現了__next__魔術方法,但沒有實現__iter__魔術方法,比如集合(本文稍后會討論)。在本文中,我們假設涉及到的一切對象都是可迭代的對象。
現在我們知道了什么是可迭代的對象,回過頭來討論下映射函數。映射可以對可迭代對象中的每個元素執行指定的函數。通常,我們對列表中的每個元素執行函數,但要知道映射其實可以針對絕大多數可迭代對象使用。map(function,?iterable)
假設有一個列表由以下數字組成:[1,?2,?3,?4,?5]
我們希望得到每個數字的平方,那么代碼可以寫成這樣:x?=?[1,?2,?3,?4,?5]
def?square(num):
return?num*num
print(list(map(square,?x)))
Python中的函數式函數是懶惰的。如果我們不加“list()”,那么函數只會將可迭代對象保存下來,而不會保存結果的列表。我們需要明確地告訴Python“把它轉換成列表”才能得到結果。
在Python中一下子從不懶惰的函數求值轉換到懶惰的函數似乎有點不適應。但如果你能用函數式的思維而不是過程式的思維,那么最終會適應的。
這個“square(num)”的確不錯,但總覺得有點不對勁。難道為了僅使用一次的map就得定義整個函數嗎?其實我們可以使用lambda函數(匿名函數)。Lambda 表達式
Lambda表達式就是只有一行的函數。比如下面這個lambda表達式可以求出給定數字的平方:square?=?lambda?x:?x?*?x
運行下面的代碼:>>>?square(3)
9
你肯定在問:“參數去哪兒了?這究竟是啥意思?看起來根本不像函數啊?”
嗯,的確是不太容易懂……但還是應該能夠理解的。我們上面的代碼把什么東西賦給了變量“square”。就是這個東西:lambda?x:
它告訴Python這是個lambda函數,輸入的名字為x。冒號后面的一切都是對輸入的操作,然后它會自動返回操作的結果。
這樣我們的求平方的代碼可以簡化成一行:x?=?[1,?2,?3,?4,?5]
print(list(map(lambda?num:?num?*?num,?x)))
有了lambda表達式,所有參數都放在左邊,操作都放在右邊。雖然看上去有點亂,但不能否認它的作用。實際上能寫出只有懂得函數式編程的人才能看懂的代碼還是有點小興奮的。而且把函數變成一行也非常酷。歸納
歸納(reduce)是個函數,它把一個可迭代對象變成一個東西。通常,我們在列表上進行計算,將列表歸納成一個數字。歸納的代碼看起來長這樣:reduce(function,?list)
上面的函數可以使用lambda表達式。
列表的乘積就是把所有數字乘到一起。可以這樣寫代碼:product?=?1
x?=?[1,?2,?3,?4]
for?num?in?x:
product?=?product?*?num
但使用歸納,可以寫成這樣:from?functools?import?reduce
product?=?reduce((lambda?x,?y:?x?*?y),[1,?2,?3,?4])
這樣能得到同樣的結果。這段代碼更短,而且借助函數式編程,這段代碼更簡潔。過濾
過濾(filter)函數接收一個可迭代對象,然后過濾掉對象中一切不需要的東西。
通常過濾接收一個函數和一個列表。它會針對列表中的每個元素執行函數,如果函數返回True,則什么都不做。如果函數返回False,則從列表中去掉那個元素。
語法如下:filter(function,?list)
我們來看一個簡單的例子。沒有過濾,代碼要寫成這樣:x?=?range(-5,?5)
new_list?=?[]
for?num?in?x:
總結
以上是生活随笔為你收集整理的python命令式编程的概念,【Python】十分钟学会函数式编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php随机数字不重复,php生成N个不重
- 下一篇: oracle 分布键,DWS使用技巧:根