轉(zhuǎn):https://foofish.net/function-is-first-class-object.html
在計算機(jī)中闺骚,函數(shù)調(diào)用是通過棧(stack)這種數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,每當(dāng)進(jìn)入一個函數(shù)調(diào)用虫碉,棧就會加一層棧幀胸梆,每當(dāng)函數(shù)返回,棧就會減一層棧幀碰镜。由于棧的大小不是無限的,所以秽荤,遞歸調(diào)用的次數(shù)過多,會導(dǎo)致棧溢出王滤。
python 的函數(shù)是第一對象
正確理解 python 函數(shù),能夠幫我們更好地理解 裝飾器(@)第喳、匿名函數(shù)(lambda)與函數(shù)式編程等高階技術(shù)踱稍。
一、函數(shù)是對象
在 Python 中萬物皆為對象珠月,函數(shù)也不例外,函數(shù)作為對象可以賦值給一個變量驻谆、可以作為元素添加到集合對象中、可作為參數(shù)值傳遞給其它函數(shù)胜臊,還可以當(dāng)做函數(shù)的返回值伙判,這些特性就是第一類對象所特有的。
簡單的函數(shù):
>>> def foo(text):
... return len(text)
...
>>> foo('zen of python')
13
函數(shù) foo() 作為一個對象勒魔,擁有對象模型的三個通用屬性:id、類型冠绢、值:
>>> id(foo)
34515536
>>> type(foo)
<class 'function'>
>>> foo
<function foo at 0x020EAA50>
函數(shù)作為對象羊娃,可以賦值給另外一個變量:
>>> bar = foo
>>> bar
<function foo at 0x020EAA50>
>>> bar("zen of python")
13
>>> bar == foo
True
bar = foo
是因為他們指向一樣
賦值給另外一個變量時蕊玷,函數(shù)并不會被調(diào)用弥雹,僅僅是在函數(shù)對象上綁定一個新的名字而已(或新名字指向該函數(shù)),單純的起一個新名字不需要加括號和參數(shù)贸诚;
二、函數(shù)可以存儲在容器
容器對象(list酱固、dict、set等)中可以放任何對象龄减,包括證書、字符串希停。函數(shù)也可以放在容器對象中:
>>> 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 是我們自定義的函數(shù)署隘,str 和 len 是兩個內(nèi)置函數(shù)∥コ纾可以通過索引或列表遍歷來調(diào)用函數(shù):
[<function foo at 0x020EAA50>, <class 'str'>, <built-in function len>]
>>> funcs[0]('zen of python')
13
三、函數(shù)可以作為參數(shù)
函數(shù)還可以作為參數(shù)傳遞給另外一個函數(shù):
>>> def show(func):
... size = func("zen of python")
... print('length of string is : {}'.format(size))
...
>>> show(foo)
length of string is : 13
四羞延、函數(shù)還可以作為返回值:
>>> def nick():
... return foo
...
>>> nick
<function nick at 0x020EA078>
>>> a = nick
>>> a
<function nick at 0x020EA078>
>>> a("python")
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: nick() takes 0 positional arguments but 1 was given
>>> a = nick()
>>> a
<function foo at 0x020EAA50>
>>> a("python")
6
這里有個錯誤肴楷,見最后
還可以簡寫為:
>>> nick()("python")
6
函數(shù)接收一個或多個函數(shù)作為輸入或者函數(shù)輸出(返回)的值是函數(shù)時荠呐,我們稱這樣的函數(shù)為高階函數(shù),show 和 nick 都屬于高階函數(shù)泥张。
python 內(nèi)置函數(shù)中,典型的高階函數(shù)是 map渗钉,map 接收一個函數(shù)和一個迭代對象作為參數(shù),調(diào)用 map 時鳄橘,依次把迭代對象的元素作為參數(shù)調(diào)用該函數(shù):
>>> map(foo, ['the', 'zen', 'of', 'python'])
<map object at 0x020D0250>
>>> lens = map(foo, ['the', 'zen', 'of', 'python'])
>>> list(lens)
[3, 3, 2, 6]
map 函數(shù)的作用相當(dāng)于:
>>> [foo(i) for i in ['the', 'zen', 'of', 'python'] ]
[3, 3, 2, 6]
只不過 map 函數(shù)的效率更高一點
五、函數(shù)可以嵌套
python 還允許函數(shù)中定義函數(shù)瘫怜,這種函數(shù)叫做嵌套函數(shù):
>>> def get_length(text):
... def clean(t): # 2
... return t[1:]
... new_text = clean(text) # 1
... return len(new_text)
...
>>> get_length('python')
5
這個函數(shù)的目的是取出字符串的第一個字符后再計算它的長度本刽。get_length 調(diào)用時鲸湃,先執(zhí)行 1 處的代碼,發(fā)現(xiàn)有調(diào)用 clean 函數(shù)笋除,接著執(zhí)行 2 的代碼,把返回值賦值給 new_text垃它,再執(zhí)行后續(xù)代碼。
>>> clean("python")
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'clean' is not defined
函數(shù)中里面的嵌套函數(shù)不能在函數(shù)外面被調(diào)用嗤瞎,只有局部使用域。
六贝奇、實現(xiàn)了 call的類也可以作為函數(shù)
對于一個自定義的類靠胜,如果實現(xiàn)了 call 方法,那么該類的實例對象的行為就是一個函數(shù)浪漠,可以被調(diào)用:
>>> class ADD:
... def __init__(self, n):
... self.n = n
... def __call__(self, x):
... return self.n + x
...
>>> add = ADD(1)
>>> add(4)
5
>>> ADD(1)(4)
5
執(zhí)行 add(4) 相當(dāng)于調(diào)用 ADD.call(add, 4),self 就是實例對象 add该镣,self
.n 等于 1响谓,所以返回值是:1 + 4:
add(4)
||
Add(1)(4)
||
Add.__call__(add, 4)
確定對象是否可以調(diào)用,可以用內(nèi)置函數(shù) callable 來判斷娘纷。
>>> callable(foo)
True
>>> callable(1)
False
>>> callable(int)
True
總結(jié)
python 中包含函數(shù)在內(nèi)的一切皆為對象,函數(shù)作為第一類對象赖晶,支持賦值給變量,支持作為參數(shù)傳遞給其他函數(shù)捂贿,支持作為其他函數(shù)的返回值胳嘲,支持函數(shù)的嵌套,實現(xiàn)了 call 方法的類實例對象也可以當(dāng)做函數(shù)被調(diào)用胎围。
有點不明白,調(diào)用函數(shù)和類汽纤,什么時候需要寫 ()福荸,什么時候不需要寫:
函數(shù):
>>> def foo(text):
... return len(text)
...
>>> def nick():
... return foo
a = nick 調(diào)用的 是 nick 函數(shù)本身,有沒有參數(shù)都可以這樣寫背传,相當(dāng)于給函數(shù)起個別名台夺;
b = nick() 調(diào)用的是 nick 函數(shù)的返回值(foo),nick(var) 函數(shù)必須寫完整颤介,不然無法返回值;
nick()(var)
:nick()
是foo
函數(shù)冤灾,nick()(var)
是函數(shù)foo
的返回值,等同于b(var)
韵吨;
類:
>>> class test(object):
... y = "me"
... def __init__(self):
... self.y = 'you'
...
>>> a = test
>>> a
<class 'test'>
>>> print(a.y)
me
>>> a.y
'me'
>>> b = test()
>>> b
<test object at 0x020D0E50>
>>> b.y
'you'
>>> print(b.y)
you
不帶括號移宅,調(diào)用的是類本身,沒有執(zhí)行 __init__
函數(shù)盏浇;帶括號的實例化方法調(diào)用了 __init__()
函數(shù),此時必須傳入需要的參數(shù)绢掰;
函數(shù)帶不帶括號童擎,決定了是調(diào)用當(dāng)前函數(shù),還是調(diào)用返回值
類帶不帶括號班挖,決定了掉不掉用 __init__
方法