1. 程式人生 > >day7 反射

day7 反射

typeerror hasattr mon all png attr 動態 一切都 ges

反射是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 反射