刨根問底:物件也可以當方法用?
1、問題
我剛開始接觸爬蟲的時候,只是看完了 python 的基礎,對 python 的語法還沒有一個很深入的瞭解,在使用 bs4 這個庫的時候,對其中某些語法感到非常的驚奇,不明白是怎麼實現的。
bs4 的官方文件中說到:find_all()
幾乎是 Beautiful Soup 中最常用的搜尋方法,所以我們定義了它的簡寫方法。BeautifulSoup
物件和tag
物件可以被當作一個方法來使用,這個方法的執行結果與呼叫這個物件的find_all()
方法相同,下面兩行程式碼是等價的:
soup.find_all("a") soup("a")
這裡,soup
是一個BeautifulSoup
物件,soup("a")
這很明顯是把物件當方法使用了,這是怎麼做到的呢?
2、實現
在 Python 中,除了使用者定義的函式,呼叫運算子(即 ())還可以應用到其他物件上。內建的callable()
函式用來判斷一個物件能否呼叫。就是說,任何 Python 物件都可以表現得像函式一樣,為此,只需實現例項方法__call__
。
來看一個簡單的示例:
class Sum: def __init__(self, x, y): self._x = x self._y = y def add(self): return self._x + self._y def __call__(self): return self.add() sum = Sum(1, 2) print(sum.add()) print(sum()) print(callable(sum))
輸出:
3 3 True
這樣就明白了,bs4 中亦是如此,原始碼如下:
class Tag(PageElement): def __call__(self, *args, **kwargs): return self.find_all(*args, **kwargs)
這背後涉及到的概念叫做可呼叫物件 ,Python 資料模型文件列出了 7 種可呼叫物件。
- 使用者定義的函式 :使用 def 語句或 lambda 表示式建立。
- 內建函式 :使用 C 語言(CPython)實現的函式,如 len 或 time.strftime。
- 內建方法 :使用 C 語言實現的方法,如 dict.get。
- 方法 :在類的定義體中定義的函式。
-
類
:呼叫類時會執行類的
__new__
方法建立一個例項,然後執行__init__
方法,初始化例項,最後把例項返回給呼叫方。因為 Python 沒有new
運算子,所以呼叫類相當於呼叫函式。(通常,呼叫類會建立那個類的例項,不過覆蓋__new__
方法的話,也可能出現其他行為。) -
類的例項
:如果類定義了
__call__
方法,那麼它的例項可以作為函式呼叫。 - 生成器函式 :使用 yield 關鍵字的函式或方法。呼叫生成器函式返回的是生成器物件。