基本語言特性
函數是幾乎任何程序語言都支持的基本語言特性。然而,不同的語言對函數的看法卻是不同的,這不僅導致了函數在語言中的地位,也產生出了不同的編程風格。
在scheme語言中,函數與普通數據別無二致,所以自然而然的將函數作爲第一類對象,同時scheme更強函數式編程和函數遞歸,以追求計算的本質。
在java中,函數與普通數據是區分開的,即函數不能直接被傳遞,只能被包裹到類中再通過實例對象被傳遞,高度的強調面向對象編程。
在python中,函數也被當做第一類對象,支持少量的函數式編程風格,但依然以面向對象爲主。
關於第一類對象的思考
所謂第一類對象,就是指這樣的一種對象:
- 可以賦值給變量
- 可以傳參給函數
- 可以從函數中傳出
第一句話其實已經包含了後兩句,所以它們可以概括爲一句話:類似23,'hello',True,instance這種可以賦值給變量的對象。
首先,我覺得編程語言應該將函數對象作爲第一類對象,這應該成爲編程語言的標準特性,函數對象和數字23沒有什麼區別,它們本質上都是數據,所以函數對象可以和普通數據一樣被各種操作。但在java中,函數對象被看成與普通數據是不同的,這一點我覺得並不合理。如果在java中要傳遞一個函數對象,還必須首先將它包裹到一個類中,寄宿到一個實例對象中才能傳遞。
其次,將函數作爲第一類對象,本來就是應該具有的語言特性,並不能說某種語言(如python)支持此特性,就覺得另一種不支持此特性的語言(如java)更差勁。
此外,支持此特性,可以讓我們以函數式風格編寫代碼,在某些場景下,比面向對象風格的代碼更有表達力和更好的封裝性。
函數的定義
在python中使用如下語句定義一個函數對象:
def add(x, y):
print('this is function add')
result = x + y
return result
思考這個函數定義,它包含了幾方面的內容:
- 使用python的關鍵字def,目的是告訴python解釋器後續是一個函數定義,以便使用Function類實例化一個函數對象。
- 定義了add作爲函數對象的變量名,此時函數對象作爲第一類對象。
- 定義了參數列表(x, y)
- 定義了函數體
- 定義了返回值result
一個函數被定義完畢,將會存放在堆上以便被共享調用。
因爲add是此函數的引用,add存在於此函數定義處的上下文環境中。
函數的使用
result = add(2, 3)
print(result)
函數是描述符對象
因爲函數是一個對象,python中內置了一個dir函數可以查看對象的所有屬性信息。
如果仔細的查看可以看到,每一個函數對象的dir結果中,都有一個__get__函數。
這表示:
- python內置的Fucntion類中肯定定義了此
__get__
函數。 - 此
__get__
函數可以解釋實例方法的執行流程,即爲啥調用實例方法的時候會自動傳遞self。
函數是語句集合的抽象
因爲一個函數體中包含多條語句,這些語句有着高度統一的目標,都想爲統一的目標貢獻自己的力量。不同目標的語句應該放在不同的函數中。函數作爲多條語句的抽象,而類又作爲多個函數的抽象,然後模塊又作爲多個類的抽象,這是一個層次關係,目的都是爲了提供良好的封裝以降低程序的複雜度,提升模塊化。
函數是執行單元
嚴格來說,語句纔是最小的執行單元,但是我們一般站在函數的角度來表達一個程序(因爲站在語句的角度的話,代碼量太大了)。當函數成爲一個線程的執行單元時,我們對程序的編寫就可以轉換成:如何合理的設計函數。這種思路傾向於函數式編程。
把函數作爲執行單元,還有一個特點是,程序的執行是按照函數棧執行的,我們可以中斷和恢複函數以實現協程異步。