day7 反射
反射是python開發中常用的功能,伴隨開發的整個過程,因此要熟練掌握反射的用法。
反射常用的函數有四個:hasattr()、getattr()、setattr()和delattr()四個反射的函數。
本文主要介紹python中的反射,以及該機制的簡單應用,熟悉JAVA的程序員,一定經常和Class.forName打交道。在很多框架中(Spring,eclipse plugin機制)都依賴於JAVA的反射能力,而在python中,也同樣有著強大的反射能力,本文將做簡單的介紹。
一、前言
>>> def f1():
... print("f1是這個函數的名字!")
>>> s1 = "f1"
>>> print("%s是個字符串" %s1)
f1是個字符串
>>> f1
<function f1 at 0x7fe9b995df28>
>>> f1()
f1是這個函數的名字!
>>> s1()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ‘str‘ object is not callable
在上面的代碼中,我們必須區分兩個概念,f1和"f1"。前者是函數f1的函數名,後者只是一個叫"f1"的字符串,兩者是不同的事物。我們可以用f1()的方式調用函數f1,但我們不能用"f1"()的方式調用函數。說白了就是,不能通過字符串來調用名字看起來相同的函數!
二、web實例
考慮有這麽一個場景,根據用戶輸入的URL的不同,調用不同的函數,實現不同的操作,也就是一個URL路由器的功能,這在Web框架裏是核心部件之一。下面有一個精簡版的示例:
首先,有一個commons模塊,它裏面有幾個函數,分別用於展示不同的頁面,代碼如下:
1 def login():2 print("這是一個登錄頁面!") 3 4 def logout(): 5 print("這是一個退出頁面!") 6 7 def home(): 8 print("這是網站主頁面!")
其次,有一個visit模塊,作為程序入口,接受用戶輸入,展示相應的頁面,代碼如下:(這段代碼是比較初級的寫法)
1 import commons 2 #反射,用判斷實現的反射,字符串調用模塊的形式 3 def run(): 4 ing = input("請輸入你想訪問頁面的URL:").strip() 5 if ing == "login": #判斷用戶是否要調用login()方法,如果是調用模塊commons中的login()方法 6 commons.login() 7 8 elif ing == "logout": #判斷用戶是否要調用logout()方法,如果是調用commons中的logout()方法 9 commons.logout() 10 11 elif ing == "home": #判斷用戶是否要調用home()方法,如果是,調用commons中的home()方法 12 commons.home() 13 14 if __name__ == "__main__": #以後使用程序都要寫上__name__ == "__main__"作為程序的主入口 15 run()
我們運行visit.py,輸入:home,頁面結果如下:
請輸入你想訪問頁面的URL:home
這是網站主頁面!
這就實現了一個簡單的WEB路由功能,根據不同的url,執行不同的函數,獲得不同的頁面。
然而,讓我們考慮一個問題,如果commons模塊裏有成百上千個函數呢(這非常正常)?。難道你在visit模塊裏寫上成百上千個elif?顯然這是不可能的!那麽怎麽破?
三、反射機制
仔細觀察visit中的代碼,我們會發現用戶輸入的url字符串和相應調用的函數名好像!如果能用這個字符串直接調用函數就好了!但是,前面我們已經說了字符串是不能用來調用函數的。為了解決這個問題,python為我們提供一個強大的內置函數:getattr!我們將前面的visit修改一下,代碼如下:
1 import commons 2 #反射,用判斷實現的反射,字符串調用模塊的形式 3 def run(): 4 # ing = input("請輸入你想訪問頁面的URL:").strip() 5 # if ing == "login": 6 # commons.login() 7 # 8 # elif ing == "logout": 9 # commons.logout() 10 # 11 # elif ing == "home": 12 # commons.home() 13 14 ing = input("請輸入你想訪問頁面的URL:").strip() 15 func = getattr(commons,ing) #從模塊commons中獲取ing等同字符串的函數內存地址 16 func() #執行從commons模塊中獲取的函數 17 18 if __name__ == "__main__": 19 run()
首先說明一下getattr函數的使用方法:它接收2個參數,前面的是一個對象或者模塊,後面的是一個字符串,註意了!是個字符串!
例子中,用戶輸入儲存在inp中,這個inp就是個字符串,getattr函數讓程序去commons這個模塊裏,尋找一個叫inp的成員(是叫,不是等於),這個過程就相當於我們把一個字符串變成一個函數名的過程。然後,把獲得的結果賦值給func這個變量,實際上func就指向了commons裏的某個函數。最後通過調用func函數,實現對commons裏函數的調用。這完全就是一個動態訪問的過程,一切都不寫死,全部根據用戶輸入來變化。
http://www.jb51.net/article/87479.htm
day7 反射