正確理解 Python函數,能夠幫助我們更好地理解 Python 裝飾器、匿名函數(lambda)、函數式編程等高階技術。
函數(Function)作為程序語言中不可或缺的一部分,太稀松平常了。但函數作為第一類對象(First-Class Object)卻是 Python 函數的一大特性。那到底什么是第一類對象呢?
函數是對象
在 Python 中萬物皆為對象,函數也不例外,函數作為對象可以賦值給一個變量、可以作為元素添加到集合對象中、可作為參數值傳遞給其它函數,還可以當做函數的返回值,這些特性就是第一類對象所特有的。
先來看一個簡單的例子
>>> def foo(text):... return len(text)...>>> foo("zen of python")13
這是一個再簡單不過的函數,用於計算參數 text 的長度,調用函數就是函數名后面跟一個括號,再附帶一個參數,返回值是一個整數。
函數身為一個對象,擁有對象模型的三個通用屬性:id、類型、和值。
>>> id(foo)4361313816>>> type(foo)<class ‘function‘>>>> foo<function foo at 0x103f45e18>
作為對象,函數可以賦值給一個變量
>>> bar = foo

賦值給另外一個變量時,函數並不會被調用,僅僅是在函數對象上綁定一個新的名字而已。
>>> bar("zen of python")13>>>
同理,你還可以把該函數賦值給更多的變量,唯一變化的是該函數對象的引用計數不斷地增加,本質上這些變量最終指向的都是同一個函數對象。
>>> a = foo >>> b = foo >>> c = bar >>> a is b is c True
函數可以存儲在容器
容器對象(list、dict、set等)中可以存放任何對象,包括整數、字符串,函數也可以作存放到容器對象中,例如
>>> funcs = [foo, str, len]
>>> funcs
[<function foo at 0x103f45e18>, <class ‘str‘>, <built-in function len>]
>>> for f in funcs:
... print(f("hello"))
...
5
hello
5
>>>
foo 是我們自定義的函數,str 和 len 是兩個內置函數。for 循環逐個地迭代出列表中的每個元素時,函數對象賦值給了 f 變量,調用 f(“hello”) 與 調用 foo(“hello”) 本質是一樣的效果,每次 f 都重新指向一個新的函數對象。當然,你也可以使用列表的索引定位到元素來調用函數。
>>> funcs[0]("Python之禪")# 等效於 foo("Python之禪")8
函數可以作為參數
函數還可以作為參數值傳遞給另外一個函數,例如:
>>> def show(func):... size = func("python 之禪") # 等效於 foo("Python之禪") ... print ("length of string is : %s" % size)...>>> show(foo)length of string is : 9
函數可以作為返回值
函數作為另外一個函數的返回值,例如:
>>> def nick():
... return foo
>>> nick
<function nick at 0x106b549d8>
>>> a = nick()
>>> a
<function foo at 0x10692ae18>
>>> a("python")
6
還可以簡寫為
>>> nick()("python")
6
函數接受一個或多個函數作為輸入或者函數輸出(返回)的值是函數時,我們稱這樣的函數為高階函數,比如上面的 show 和nick 都屬於高階函數。
Python內置函數中,典型的高階函數是 map 函數,map 接受一個函數和一個迭代對象作為參數,調用 map 時,依次迭代把迭代對象的元素作為參數調用該函數。
>>> map(foo, ["the","zen","of","python"])>>> lens = map(foo, ["the","zen","of","python"])>>> list(lens)[3, 3, 2, 6]
map 函數的作用相當於:
>>> [foo(i) for i in ["the","zen","of","python"]][3, 3, 2, 6]
只不過 map 的運行效率更快一點。
函數可以嵌套
Python還允許函數中定義函數,這種函數叫嵌套函數。
>>> def get_length(text):... def clean(t): # 2... return t[1:]... new_text = clean(text) # 1... return len(new_text)...>>> get_length("python")5>>>
這個函數的目的是去除字符串的第一個字符后再計算它的長度,盡管函數本身的意義不大,但能足夠說明嵌套函數。get_length 調用時,先執行1處代碼,發現有調用 clean 函數,於是接着執行2中的代碼,把返回值賦值給了 new_text ,再繼續執行后續代碼。
>>> clean("python")Traceback (most recent call last):
File "<stdin>", line 1, in <module>NameError: name ‘clean‘ is not defined
函數中里面嵌套的函數不能在函數外面訪問,只能是在函數內部使用,超出了外部函數的做用域就無效了。
實現了 __call__ 的類也可以作為函數
對於一個自定義的類,如果實現了 __call__ 方法,那么該類的實例對象的行為就是一個函數,是一個可以被調用(callable)的對象。例如:
class Add: def __init__(self, n): self.n = n def __call__(self, x): return self.n + x>>> add = Add(1)>>> add(4)>>> 5
執行 add(4) 相當於調用 Add._call__(add, 4),self 就是實例對象 add,self.n 等於 1,所以返回值為 1+4
add(4) || Add(1)(4) || Add.__call__(add, 4)
確定對象是否為可調用對象可以用內置函數callable來判斷。
>>> callable(foo)True>>> callable(1)False>>> callable(int)True
總結
Python中包含函數在內的一切皆為對象,函數作為第一類對象,支持賦值給變量,作為參數傳遞給其它函數,作為其它函數的返回值,支持函數的嵌套,實現了__call__方法的類實例對象也可以當做函數被調用。
