06.13 Python 的函數是第一類 First-Class 對象

Python 的函數是第一類 First-Class 對象

Python的函數是第一類對象(first-class object)。你可以把一個函數複製給變量,或者把函數存儲在一個結構中。可以像參數一樣把函數傳遞進另一個函數,還可以從一個函數中返回另一個函數。

知道了這些,有助於理解 lambda 和裝飾器。這也是 面向函數編程 的一部分。

通過這篇文章,我用最簡單的例子,為大家做個說明。

首先定義一個函數,然後調用它:

Python 的函數是第一類 First-Class 對象

輸出:

hello alex!

函數是對象


和列表,字符串一樣,函數是對象。我們可以把函數賦值給一個變量。

Python 的函數是第一類 First-Class 對象

輸出:

hello john!

hi = hello 這一行並沒有調用函數 hello 。實際上是 hi 引用了函數 hello。現在 hi 是可調用的,最後執行 hi('john'),調用函數代碼。打印了 hello john!。

函數對象和函數的名稱是兩個概念,在上面的例子,hi 和 hello 都指向同一個函數對象。如果刪除 hello 並不影響 hi,它繼續指向原來的函數對象。

Python 的函數是第一類 First-Class 對象

輸出:

hello everyone! 

另外,函數在創建階段就賦予了名稱。上面的函數對象創建的時候,指定了名稱 hello ,我們把 hi 指向了函數對象,並不影響它之前的名稱。

print(hi.__name__) # 輸出 hello

可見,輸出值還是 hello。

函數可以存儲在數據結構中


因為函數是第一類對象,你可以把它存入數據結構中,比如列表:

Python 的函數是第一類 First-Class 對象

輸出:

Python 的函數是第一類 First-Class 對象

可以不用賦值給變量,直接通過列表索引調用函數對象:

print(funcs[0]('maggie')) # 輸出 hello maggie!

函數可以傳遞給其他的函數


函數可以通過參數傳遞進其他函數。

Python 的函數是第一類 First-Class 對象

輸出:

hello alex!

這裡,把 hello 函數傳遞進了 hello_alex。在 hello_alex 調用了函數 hello,並打印。

另一個例子:

Python 的函數是第一類 First-Class 對象

輸出:

HELLO ALEX!

我們寫了新的函數 hello_alex_upper,這個函數將默認的輸出大寫。

函數作為參數傳遞進另一個函數,這個特性是非常強大的。你可以不修改之前的代碼,直接為你的應用增加其他的行為。

函數能夠接收另一個函數作為參數,這種函數被稱為 高階函數(high order function)。屬於面向函數編程的範疇。

在 Python 中使用高階函數的一個經典的例子是 map 函數。它接收一個函數和一個序列。序列的每一個元素都經過函數的處理,然後返回修改過的結果序列。

>>> list(map(str.upper, ['hello', 'world']))
['HELLO', 'WORLD']

函數可以嵌套


Python 允許一個函數定義在另一個函數里,這通常被稱為 嵌套函數 (nested function) 或者

內部函數(inner function)

Python 的函數是第一類 First-Class 對象

輸出:

GREET ALEX

注意,內部函數 upper 在外部是訪問不了的,如果你試圖訪問,會報 AttributeError 錯誤。

print(greet.upper)

報錯:

Traceback (most recent call last): 

File "first-class-object.py", line 38, in <module>
print(greet.upper)
AttributeError: 'function' object has no attribute 'upper'
/<module>

如果你想訪問內部函數,需要返回該內部函數。

對象也可以像函數那樣調用


我們從類實例化,得到一個對象,那麼對象就可以調用類定義的相關方法。其實對象也可以像函數那樣調用,只需要在類上定義一個函數 __call__。

Python 的函數是第一類 First-Class 對象

輸出:

alexalex

在背後,能夠像方法一樣調用對象,實際上是對象調用它的 __call__ 方法。


分享到:


相關文章: