天天看點

通過 getattr 擷取對象引用

你已經知道 Python 函數是對象。 你不知道的是,使用 getattr 函數,可以得到一個直到運作時才知道名稱的函數的引用。

例 4.10. getattr 介紹

>>> li = ["Larry", "Curly"] >>> li.pop   1 <built-in method pop of list object at 010DF884> 
>>> getattr(li, "pop")           2 <built-in method pop of list object at 010DF884> 
>>> getattr(li, "append")("Moe") 3 
>>> li ["Larry", "Curly", "Moe"] 
>>> getattr({}, "clear")         4 <built-in method clear of dictionary object at 00F113D4> 
>>> getattr((), "pop")           5 Traceback (innermost last):   File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'pop'
           
通過 getattr 擷取對象引用
該語句擷取清單的 pop 方法的引用。注意該語句并不是調用 pop 方法;調用 pop 方法的應該是 li.pop()。這裡指的是方法對象本身。
通過 getattr 擷取對象引用
該語句也是傳回 pop 方法的引用,但是此時,方法名稱是作為一個字元串參數傳遞給 getattr 函數的。getattr 是一個有用到令人無法緻信的内置函數,可以傳回任何對象的任何屬性。在這個例子中,對象是一個 list,屬性是 pop 方法。
通過 getattr 擷取對象引用
如果不深信它是多麼的有用,試試這個:getattr 的傳回值 是 方法,然後你就可以調用它就像直接使用 li.append("Moe") 一樣。但是實際上你沒有直接調用函數;隻是以字元串形式指定了函數名稱。
通過 getattr 擷取對象引用
getattr 也可以作用于字典。
通過 getattr 擷取對象引用
理論上,getattr 可以作用于元組,但是由于 元組沒有方法,是以不管你指定什麼屬性名稱 getattr 都會引發一個異常。

4.4.1. 用于子產品的 getattr

getattr 不僅僅适用于内置資料類型,也可作用于子產品。

例 4.11. apihelper.py 中的 getattr 函數

>>> import odbchelper 
>>> odbchelper.buildConnectionString             1 <function buildConnectionString at 00D18DD4> 
>>> getattr(odbchelper, "buildConnectionString") 2 <function buildConnectionString at 00D18DD4> 
>>> object = odbchelper 
>>> method = "buildConnectionString" 
>>> getattr(object, method)                      3 <function buildConnectionString at 00D18DD4> 
>>> type(getattr(object, method))                4 <type 'function'> 
>>> import types 
>>> type(getattr(object, method)) == types.FunctionType True 
>>> callable(getattr(object, method))            5 True
           
通過 getattr 擷取對象引用
該語句傳回 odbchelper 子產品中 buildConnectionString 函數的引用,第 2 章 第一個 Python 程式 你已經研習過這個方法了。(你看到的這個十六進制位址是我機器上的;你的輸出結果會有所不同。)
通過 getattr 擷取對象引用
使用 getattr,你能夠獲得同一函數的同一引用。通常,getattr(object, "attribute") 等價于 object.attribute。如果 object 是一個子產品的話,那麼 attribute 可能是定義在子產品中的任何東西:函數、類或者全局變量。
通過 getattr 擷取對象引用
接下來的是你真正用在 info 函數中的東西。object 作為一個參數傳遞給函數; method 是方法或者函數的名稱字元串。
通過 getattr 擷取對象引用
在這個例子中,method 是函數的名稱,通過擷取 type 可以進行驗證。
通過 getattr 擷取對象引用
由于 method 是一個函數,是以它是可調用的。

4.4.2. getattr 作為一個分發者

getattr 常見的使用模式是作為一個分發者。舉個例子,如果你有一個程式可以以不同的格式輸出資料,你可以為每種輸出格式定義各自的格式輸出函數,然後使用唯一的分發函數調用所需的格式輸出函數。

例如,讓我們假設有一個以 HTML、XML 和普通文本格式列印站點統計的程式。輸出格式在指令行中指定,或者儲存在配置檔案中。statsout 子產品定義了三個函數:output_html、output_xml 和 output_text。然後主程式定義了唯一的輸出函數,如下:

例 4.12. 使用getattr 建立分發者

import statsout  
def output(data, format="text"):                              1     
output_function = getattr(statsout, "output_%s" % format) 2     return 
output_function(data)   3 
           
通過 getattr 擷取對象引用
output 函數接收一個必備參數 data,和一個可選參數 format。如果沒有指定 format 參數,其預設值是 text 并完成普通文本輸出函數的調用。
通過 getattr 擷取對象引用
你可以連接配接 format 參數值和 "output_" 來建立一個函數名稱作為參數值,然後從 statsout 子產品中取得該函數。這種方式允許今後很容易的擴充程式以支援其它的輸出格式,而且無需修改分發函數。所要做的僅僅是向 statsout 中添加一個函數,比如 output_pdf,之後隻要将 “pdf” 作為 format 的參數值傳遞給 output 函數即可。
通過 getattr 擷取對象引用
現在你可以簡單的調用輸出函數就像調用其它函數一樣了。output_function 變量是指向 statsout 子產品中相應函數的引用。

你是否發現前面示例的一個 Bug?即字元串和函數之間的松耦合,而且沒有錯誤檢查。如果使用者傳入一個格式參數,但是在 statsout 中沒有定義相應的格式輸出函數,會發生什麼呢?還好,getattr 會傳回 None,它會取代一個有效函數并被指派給 output_function,然後下一行調用函數的語句将會失敗并抛出一個異常。這種方式不好。

值得慶幸的是,getattr 能夠使用可選的第三個參數,一個預設傳回值。

例 4.13. getattr 預設值

import statsout  
def output(data, format="text"):
     output_function = getattr(statsout, "output_%s" % format, statsout.output_text)     
     return output_function(data) 1 
           
通過 getattr 擷取對象引用
這個函數調用一定可以工作,因為你在調用 getattr 時添加了第三個參數。第三個參數是一個預設傳回值,如果第二個參數指定的屬性或者方法沒能找到,則将傳回這個預設傳回值。

正如你所看到,getattr 是相當強大的。它是自省的核心,在後面的章節中你将看到它更強大的示例

轉自:http://359611946.blog.163.com/blog/static/11619002011019102430143/