你還分不清什么是方法(method),什么是函數(shù)(function)?
點(diǎn)擊上方Python知識(shí)圈,設(shè)為星標(biāo)
回復(fù)1024獲取Python資料
閱讀文本大概需要 5 分鐘
在編程語(yǔ)言中有兩個(gè)很基礎(chǔ)的概念,即方法(method)和函數(shù)(function)。如果達(dá)到了編程初級(jí)/入門級(jí)水平,那么你肯定在心中已有了初步的答案。
也許在你心中已有答案了
除去入?yún)ⅰ⒎祷刂怠⒛涿瘮?shù)之類的正確的形式內(nèi)容之外,你也許會(huì)說(shuō)“函數(shù)就是定義在類外面的,而方法就是定義在類里面的,跟類綁定的”。
這種說(shuō)法有沒(méi)有問(wèn)題呢?當(dāng)然有!不然我就不會(huì)專門寫(xiě)這篇文章了,本文主要會(huì)來(lái)厘清這個(gè)問(wèn)題。
在標(biāo)準(zhǔn)庫(kù)inspect 中,它提供了兩個(gè)自省的函數(shù),即 ismethod() 和 isfunction(),可以用來(lái)判斷什么是方法,什么是函數(shù)。
因此,本文想要先來(lái)研究一下這兩個(gè)函數(shù),看看 Python 在處理方法/函數(shù)的概念時(shí),是怎么做的?
關(guān)于它們的用法,先看一個(gè)最簡(jiǎn)單的例子:
運(yùn)行的結(jié)果分別是“True”和“False”,表明我們所定義的 test() 是一個(gè)函數(shù),而不是一個(gè)方法。
這兩個(gè)函數(shù)也可以用來(lái)檢測(cè)自身,不難驗(yàn)證出它們都是一種函數(shù):
那么,接下來(lái)的問(wèn)題是:inspect 庫(kù)的兩個(gè)函數(shù)是什么工作原理呢?
先來(lái)看看 inspect 中的實(shí)現(xiàn)代碼:
在源碼中,我們看到了 isinstance() 函數(shù),它主要用于判斷一個(gè)對(duì)象(object)是否是某個(gè)類(class)的實(shí)例(instance)。
我們還看到了 types.FunctionType 及types.MethodType ,它們指的就是目標(biāo)類。繼續(xù)點(diǎn)進(jìn)去看源碼:
# 摘自 types.py
def _f(): pass
FunctionType = type(_f)
class _C:
def _m(self): pass
MethodType = type(_C()._m)
這里只是定義了兩個(gè)空的 _f() 和 _m(),然后就使用了內(nèi)置的 type() 函數(shù)。所以,我們完全可以把它們摘出來(lái),看看廬山真面目:
梳理它們的關(guān)系,可以得到:
經(jīng)過(guò)簡(jiǎn)化處理后,我們發(fā)現(xiàn)最關(guān)鍵的是兩個(gè)問(wèn)題:type() 函數(shù)如何判斷出一個(gè)對(duì)象是 function 或 method 類?instance() 函數(shù)如何判斷出一個(gè)對(duì)象是某個(gè)類的實(shí)例?
這兩個(gè)內(nèi)置函數(shù)都是用 C 語(yǔ)言實(shí)現(xiàn)的,這里我就不打算繼續(xù)深究了……
但是,讓我們?cè)倩仡^看看 inspect 中的注釋,就會(huì)注意到一些端倪:
-
isfunction() 判斷出的是用戶定義的函數(shù)(user-defined function), 它擁有__doc__、__name__ 等等屬性 -
ismethod() 判斷出的是實(shí)例方法(instance method), 它擁有函數(shù)的一些屬性,最特別的是還有一個(gè) __self__ 屬性
還是注釋更管用啊,由此我們能得到如下的推論:
1、非用戶定義的函數(shù),即內(nèi)置函數(shù),在 isfunction() 眼里并不是“函數(shù)”(FunctionType)!
下面驗(yàn)證一下 len()、dir() 和 range():
事實(shí)上,它們有專屬的類別(BuiltinFunctionType、BuiltinMethodType):
特別需要注意的是,內(nèi)置函數(shù)都是builtin_function_or_method 類型,但是 range()、type()、list() 等看起來(lái)像是函數(shù)的,其實(shí)不然:
(PS:關(guān)于這點(diǎn),我這篇文章 曾提到過(guò),就不再展開(kāi)了。)
2、一個(gè)類的靜態(tài)方法,在 ismethod() 眼里并不是方法(MethodType)!
創(chuàng)建了類的實(shí)例后,再看看:
可以看出,除了 classmethod 之外,只有類實(shí)例的實(shí)例方法,才會(huì)被 ismethod() 判定為真!而靜態(tài)方法,不管綁定在類還是實(shí)例上,都不算是“方法”!
有沒(méi)有覺(jué)得很不可思議(或者有點(diǎn)理不清了)?
好了,回到本文開(kāi)頭的問(wèn)題,我們最后來(lái)小結(jié)一下吧。
若以 inspect 庫(kù)的兩個(gè)函數(shù)為判斷依據(jù),則 Python 中的“方法與函數(shù)”具有一定的狹義性。在判斷什么是函數(shù)時(shí),它們并不把內(nèi)置函數(shù)計(jì)算在內(nèi)。同時(shí),在判斷什么是方法時(shí),并非定義在類內(nèi)部的都算,而是只有類方法及綁定了實(shí)例的實(shí)例方法才算是“方法”。
也許你會(huì)說(shuō),inspect 的兩個(gè)判斷函數(shù)并不足信,內(nèi)置函數(shù)也應(yīng)該算是“函數(shù)”,類里面的所有方法都應(yīng)該算是“方法”。
我承認(rèn)這種說(shuō)法在廣義上是可接受的,畢竟我們一直叫的就是“XX函數(shù)”、“XX方法”嘛。
但是,理論和廣義概念只是方便人們的溝通理解,而代碼實(shí)現(xiàn)才是本質(zhì)的區(qū)別。也就是說(shuō),Python 在實(shí)際區(qū)別“方法與函數(shù)”時(shí),并不是文中開(kāi)頭的簡(jiǎn)單說(shuō)法,還有更多的細(xì)節(jié)值得關(guān)注。
看完本文,你有什么想法呢?歡迎一起交流。
----------------------- 公眾號(hào):Python知識(shí)圈 博客:www.pyzhishiquan.com 知乎:Python知識(shí)圈 微信視頻號(hào):菜鳥(niǎo)程序員 (分享有趣的編程技巧、Python技巧) bilibili:菜鳥(niǎo)程序員的日常(目前原創(chuàng)視頻:22,累計(jì)播放量:75萬(wàn))
我的微信視頻號(hào)定時(shí)更新中,近期真人出鏡分析講解 Python 經(jīng)典習(xí)題,后續(xù)會(huì)分享更多的干貨,歡迎關(guān)注我的微信視頻號(hào)。
Python知識(shí)圈公眾號(hào)的交流群已經(jīng)建立,群里可以領(lǐng)取 Python 和人工智能學(xué)習(xí)資料,大家可以一起學(xué)習(xí)交流,效率更高,如果是想發(fā)推文、廣告、砍價(jià)小程序的敬請(qǐng)繞道!一定記得備注「交流學(xué)習(xí)」,我會(huì)盡快通過(guò)好友申請(qǐng)哦!通過(guò)好友后私聊我「學(xué)習(xí)資料」或者「進(jìn)群」都可以。
掃碼添加,備注:交流學(xué)習(xí)
往期推薦 01 02 03
我就知道你“在看” ![]()
