刨根問底:對象也能夠當方法用?

一、問題

我剛開始接觸爬蟲的時候,只是看完了 python 的基礎,對 python 的語法尚未一個很深刻的瞭解,在使用 bs4 這個庫的時候,對其中某些語法感到很是的驚奇,不明白是怎麼實現的。python

bs4 的官方文檔中說到:find_all() 幾乎是 Beautiful Soup 中最經常使用的搜索方法,因此咱們定義了它的簡寫方法。BeautifulSoup 對象和 tag 對象能夠被看成一個方法來使用,這個方法的執行結果與調用這個對象的 find_all() 方法相同,下面兩行代碼是等價的:bash

soup.find_all("a")
soup("a")
複製代碼

這裏,soup 是一個 BeautifulSoup 對象,soup("a") 這很明顯是把對象當方法使用了,這是怎麼作到的呢?函數

二、實現

在 Python 中,除了用戶定義的函數,調用運算符(即 ())還能夠應用到其餘對象上。內置的 callable() 函數用來判斷一個對象可否調用。就是說,任何 Python 對象均可以表現得像函數同樣,爲此,只需實現實例方法 __call__ui

來看一個簡單的示例:spa

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))
複製代碼

輸出:code

3
3
True
複製代碼

這樣就明白了,bs4 中亦是如此,源碼以下:cdn

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 關鍵字的函數或方法。調用生成器函數返回的是生成器對象。

掃碼關注個人公衆號

大齡碼農的Python之路
相關文章
相關標籤/搜索