學(xué)編程這么久,還傻傻分不清什么是方法(method),什么是函數(shù)(function)?

orange-tabby-cat-1769642.jpg

在編程語言中有兩個(gè)很基礎(chǔ)的概念,即方法(method)和函數(shù)(function)。如果達(dá)到了編程初級(jí)/入門級(jí)水平,那么你肯定在心中已有了初步的答案。

也許在你心中已有答案了

除去入?yún)?、返回值、匿名函?shù)之類的正確的形式內(nèi)容之外,你也許會(huì)說“函數(shù)就是定義在類外面的,而方法就是定義在類里面的,跟類綁定的”。

這種說法有沒有問題呢?當(dāng)然有!不然我就不會(huì)專門寫這篇文章了,本文主要會(huì)來厘清這個(gè)問題。

在標(biāo)準(zhǔn)庫inspect 中,它提供了兩個(gè)自省的函數(shù),即 ismethod() 和 isfunction(),可以用來判斷什么是方法,什么是函數(shù)。

因此,本文想要先來研究一下這兩個(gè)函數(shù),看看 Python 在處理方法/函數(shù)的概念時(shí),是怎么做的?

關(guān)于它們的用法,先看一個(gè)最簡(jiǎn)單的例子:

image

運(yùn)行的結(jié)果分別是“True”和“False”,表明我們所定義的 test() 是一個(gè)函數(shù),而不是一個(gè)方法。

這兩個(gè)函數(shù)也可以用來檢測(cè)自身,不難驗(yàn)證出它們都是一種函數(shù):

image

那么,接下來的問題是:inspect 庫的兩個(gè)函數(shù)是什么工作原理呢?

先來看看 inspect 中的實(shí)現(xiàn)代碼:

image
image

在源碼中,我們看到了 isinstance() 函數(shù),它主要用于判斷一個(gè)對(duì)象(object)是否是某個(gè)類(class)的實(shí)例(instance)。

我們還看到了 types.FunctionTypetypes.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ù)。所以,我們完全可以把它們摘出來,看看廬山真面目:

image

梳理它們的關(guān)系,可以得到:

image

經(jīng)過簡(jiǎn)化處理后,我們發(fā)現(xiàn)最關(guān)鍵的是兩個(gè)問題:type() 函數(shù)如何判斷出一個(gè)對(duì)象是 function 或 method 類?instance() 函數(shù)如何判斷出一個(gè)對(duì)象是某個(gè)類的實(shí)例?

這兩個(gè)內(nèi)置函數(shù)都是用 C 語言實(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():

image

事實(shí)上,它們有專屬的類別(BuiltinFunctionType、BuiltinMethodType):

image
image

特別需要注意的是,內(nèi)置函數(shù)都是builtin_function_or_method 類型,但是 range()、type()、list() 等看起來像是函數(shù)的,其實(shí)不然:

image

(PS:關(guān)于這點(diǎn),我這篇文章 曾提到過,就不再展開了。)

2、一個(gè)類的靜態(tài)方法,在 ismethod() 眼里并不是方法(MethodType)!

image

創(chuàng)建了類的實(shí)例后,再看看:

image

可以看出,除了 classmethod 之外,只有類實(shí)例的實(shí)例方法,才會(huì)被 ismethod() 判定為真!而靜態(tài)方法,不管綁定在類還是實(shí)例上,都不算是“方法”!

有沒有覺得很不可思議(或者有點(diǎn)理不清了)?

好了,回到本文開頭的問題,我們最后來小結(jié)一下吧。

若以 inspect 庫的兩個(gè)函數(shù)為判斷依據(jù),則 Python 中的“方法與函數(shù)”具有一定的狹義性。在判斷什么是函數(shù)時(shí),它們并不把內(nèi)置函數(shù)計(jì)算在內(nèi)。同時(shí),在判斷什么是方法時(shí),并非定義在類內(nèi)部的都算,而是只有類方法及綁定了實(shí)例的實(shí)例方法才算是“方法”。

也許你會(huì)說,inspect 的兩個(gè)判斷函數(shù)并不足信,內(nèi)置函數(shù)也應(yīng)該算是“函數(shù)”,類里面的所有方法都應(yīng)該算是“方法”。

我承認(rèn)這種說法在廣義上是可接受的,畢竟我們一直叫的就是“XX函數(shù)”、“XX方法”嘛。

但是,理論和廣義概念只是方便人們的溝通理解,而代碼實(shí)現(xiàn)才是本質(zhì)的區(qū)別。也就是說,Python 在實(shí)際區(qū)別“方法與函數(shù)”時(shí),并不是文中開頭的簡(jiǎn)單說法,還有更多的細(xì)節(jié)值得關(guān)注。

看完本文,你有什么想法呢?歡迎一起交流。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容