標籤:簡單的 show 經典的 eth 對象 直接 ESS pytho common
Python反射機制
我記得以前學習Java的時候,就接觸到了反射的概念,後來隨著工作,經常聽到反射的概念,今天決定好好總結一下。
下面3篇部落格我感覺寫的很不錯,大家可以進行參考。
71406953
http://www.mamicode.com/info-detail-1401422.html
https://www.cnblogs.com/huxi/archive/2011/01/02/1924317.html
首先大家需要知道反射的概念,比較經典的解釋分為以下幾種:
- 通過字串的形式來尋找或操作對象內部的屬性叫做反射
- 通過字串映射或者修改程式運行時的狀態、屬性或者方法叫做反射
- 反射是通過字串的形式操作對象相關的成員,反射的的本質其實就是利用字串的形式去對象(模組)中操作(尋找/擷取/刪除/添加)成員,一種基於字串的事件驅動!
在python當中一切事物皆對象,所以都可以使用反射。
python中的反射功能涉及到四個內建函數:hasattr、getattr、setattr、delattr,這四個函數分別用於對對象內部執行:檢查是否含
有某個成員、擷取成員、設定成員、刪除成員。
樣本程式1:訪問成員的兩種方式:obj.與obj.__dict__[‘成員‘]
#!/usr/bin/python# -*- coding:utf-8 -*-class Student(object): def __init__(self,name,age): self.name = name self.age = age def study(self,name): print(‘%s is studing‘%self.name)student = Student(‘angela‘,25)print(student.__dict__) #查看student的成員屬性(名稱空間對應的成員)print(vars(student)) #查看student的成員屬性(名稱空間對應的成員)#通過字串直接存取對象所對應的成員.print(student.__dict__[‘name‘],student.__dict__[‘age‘])print(student.__dict__.get(‘name‘))print(student.__dict__.get(‘age‘))#當然,我們一般都是這樣子的,但是上面的那種方式是根本print(student.name,student.age)
運行結果:
{‘name‘: ‘angela‘, ‘age‘: 25}{‘name‘: ‘angela‘, ‘age‘: 25}angela 25angela25angela 25Process finished with exit code 0
Python當中的反射涉及到了四個主要方法:
hasattr(obj,name_str):判斷對象obj是否含有名為name_str的方法或者靜態屬性,傳回值為布爾值,即通過字串的形式來判斷對
象內部是否含有某個屬性。
getattr(obj,name_str):根據字串name_str去擷取對象obj當中對應函數的記憶體位址或者靜態屬性對應的數值,如果傳回值為函數
的記憶體位址,需要加上括弧才能夠調用。
setattr(obj,name_str,value): 添加或者修改obj對象當中名為name_str的屬性或者方法的數值。
delattr(obj,name_str): 刪除obj對象當中名為name_str的屬性。
樣本程式:Python當中的反射對應的四個方法
#!/usr/bin/python# -*- coding:utf-8 -*-class Student(object): def __init__(self,name,age): self.name = name self.age = age def study(self,name): print(‘%s is studing‘%self.name)student = Student(‘angela‘,25)print(student.__dict__) #查看student的成員屬性(名稱空間對應的成員)#檢查對象是否含有某個成員.print(hasattr(student,‘name‘))print(hasattr(student,‘study‘))#通過字串擷取對象當中某個成員對應的數值.func = getattr(student,‘study‘)print(func)func(student)#通過字串設定或增加對象當中某個成員的數值setattr(student,‘name‘,‘Jack‘)setattr(student,‘salary‘,2000)print(student.__dict__)#通過字串刪除對象當中的某個成員.delattr(student,‘name‘)delattr(student,‘salary‘)print(student.__dict__)#設定成員的時候還可以增加方法成員.setattr(student,‘show‘,lambda num:num+1)print(vars(student))
運行結果:
{‘name‘: ‘angela‘, ‘age‘: 25}TrueTrue<bound method Student.study of <__main__.Student object at 0x000000000219D400>>angela is studing{‘salary‘: 2000, ‘name‘: ‘Jack‘, ‘age‘: 25}{‘age‘: 25}{‘show‘: <function <lambda> at 0x0000000001E5CBF8>, ‘age‘: 25}Process finished with exit code 0
樣本程式:在Python當中,萬物皆對象,所以只要是對象,就可以使用反射。
accout.py包含的內容:
#!/usr/bin/python# -*- coding:utf-8 -*-import sys#在Python當中,一切皆對象:只要是對象,就可以使用反射機制.class Person(object): def __init__(self,name,age): self.name = name self.age = age def eat(self,name): print(‘%s is eating‘%self.name)if __name__ == ‘__main__‘: #擷取當前的模組對象,並擷取該模組對象對應的數值. module_name = sys.modules[__name__] print(module_name.__dict__)
visit.py執行的內容:
#!/usr/bin/python# -*- coding:utf-8 -*-import accountif hasattr(account,‘Person‘): student = getattr(account,‘Person‘)(‘angela‘,25) print(student.__dict__) if hasattr(student,‘eat‘): func = getattr(student,‘eat‘) func(student)
運行結果:
{‘age‘: 25, ‘name‘: ‘angela‘}angela is eatingProcess finished with exit code 0
反射的應用情境:
在程式當中,如果我們想通過一個字串變數var來匯入一個模組或者一個模組下的某個方法,這個時候直接執行import var是會報錯
的,因為var在程式當中是一個變數,通過字串變數來直接調用名字看起來相同的函數是不可行的,這個時候就需要使用到反射。
根據使用者輸入的url的不同,調用不同的函數,實現不同的操作,也就是一個web url路由器的功能,這在web架構裡是核心組件之一。
樣本程式1:首先,有一個commons模組,它裡面有幾個函數,分別用於展示不同的頁面,代碼如下:
#!/usr/bin/python# -*- coding:utf-8 -*-def login(): print(‘這是一個登陸頁面!‘)def logout(): print(‘這是一個退出頁面!‘)def home(): print(‘這是網站的首頁面.‘)
隨後,有一個visit模組,通過這個模組可以登入到不同的頁面(簡易版程式),如果沒有使用到反射,大部分人可能會這樣寫:
#!/usr/bin/python# -*- coding:utf-8 -*-import commonsdef run(): inp = input(‘請輸入你想訪問的頁面的url:‘).strip() if inp == ‘login‘: commons.login() elif inp == ‘logout‘: commons.logout() elif inp == ‘home‘: commons.home() else: print(‘404‘)if __name__ == ‘__main__‘: run()
運行結果樣本:
請輸入你想訪問的頁面的url:login這是一個登陸頁面!Process finished with exit code 0
如果你會使用反射的話,我們就可以這樣寫(萬物皆對象)
import commonsdef run(): inp = input(‘請輸入你想訪問的頁面的url:‘).strip() if hasattr(commons,inp): func = getattr(commons,inp) func() else: print(‘404‘)if __name__ == ‘__main__‘: run()
樣本程式2:我們在類比一個Ftp的例子,其實道理都是一樣的,在Python當中,萬物皆對象,只要是對象,就可以使用反射。
#!/usr/bin/python# -*- coding:utf-8 -*-class Ftp_Client(object): def __init__(self,host): self.host = host print(‘正在串連機器:‘,host,‘....‘) def run(self): while True: line = input(‘請輸入你需要操作的命令:‘).strip() cmd = line.split()[0] file_name = line.split()[1] if hasattr(self,cmd): func = getattr(self,cmd) print(func) func(file_name) else: print(‘您輸入的指令不存在.‘) def get(self,filename): print(‘正在下載檔案%s,稍等...‘%filename)ftp_client = Ftp_Client(‘127.0.0.1‘)ftp_client.run()
運行結果:
正在串連機器: 127.0.0.1 ....請輸入你需要操作的命令:get word.txt<bound method Ftp_Client.get of <__main__.Ftp_Client object at 0x00000000024F9A20>>正在下載檔案word.txt,稍等...請輸入你需要操作的命令:put word.txt您輸入的指令不存在.請輸入你需要操作的命令:
樣本程式3:反射機制還經常用到協同開發過程當中。
假設現在有一個人A:正在開發一個介面,授權介面:grant。
#!/usr/bin/python# -*- coding:utf-8 -*-class Ugdap(object): def __init__(self,db_name,table_name): self.db_name = db_name self.table_name = table_name # def grant(self): # """ # :return: 該介面正在開發中... # """ # print(‘進行中授權.‘)
作為調用介面的我,不確認介面是否已經開發完畢,於是我可以這麼寫:
#!/usr/bin/python# -*- coding:utf-8 -*-from commons import UgdapUgdap_obj = Ugdap(‘fdm‘,‘exe_cool_data_operate‘)if hasattr(Ugdap_obj,‘grant‘): func = getattr(Ugdap_obj,‘grant‘) print(func) func()else: print(‘無法擷取到介面資訊,跳過該步驟.‘)
反射的意義:
可能有人會問python不是有兩個內建函數exec和eval嗎?他們同樣能夠執行字串。比如:
| 12345 |
exec("print(‘haha‘)") 結果: haha |
那麼直接使用它們不行嗎?非要那麼費勁地使用getattr,__import__幹嘛?
其實,在上面的例子中,圍繞的核心主題是如何利用字串驅動不同的事件,比如匯入模組、調用函數等等,這些都是python的反射機
制,是一種編程方法、設計模式的體現,凝聚了高內聚、松耦合的編程思想,不能簡單的用執行字串來代替。當然,exec和eval也有它
的舞台,在web架構裡也經常被使用。
PYTHON開發必備技能(5)