什么是反射
反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)
Python 面向對象中的反射
- 通過字符串的形式操作對象的屬性
- Python 中一切皆為對象,所以只要是對象都可以使用反射
- 比如:實例對象、類對象、本模塊、其他模塊,因為他們都能通過 對象.屬性 的方式獲取、調用
反射中關鍵的四個函數
- hasattr
- getattr
- setattr
- delattr
hasattr
def hasattr(*args, **kwargs): """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass
- 返回對象是否具有具有給定名稱的屬性
- 這是通過調用 getattr(obj,name) 並捕獲AttributeError來完成的
getattr
def getattr(object, name, default=None): """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
- 獲取對象指定名稱的屬性
- getattr(x , y) 等價寫法 x.y
- 當屬性不存在,則返回 default 值,如果沒有指定 default 就會拋出異常
setattr
def setattr(x, y, v): """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
- 給指定對象的指定屬性設置為值
- setattr(x,y,v) 等價寫法 x.y = v
delattr
def delattr(x, y): """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
- 從指定對象中刪除指定屬性
- delattr(x,y) 等價寫法 del x.y
反射類的成員
class PoloBlog: sum = 0 def __init__(self, name): self.name = name def test(self): print("====姓名==== ", self.name)
hasattr
blog = PoloBlog("小菠蘿")
# hasattr print(hasattr(blog, "name")) # 實例對象-實例屬性 print(hasattr(blog, "sum")) # 實例對象-類屬性 print(hasattr(PoloBlog, "sum")) # 類對象-類屬性 print(hasattr(PoloBlog, "name")) # 類對象-實例屬性 # 輸出結果 True True True False
getattr
# getattr print(getattr(blog, "name")) # 實例對象-實例屬性 print(getattr(blog, "sum")) # 實例對象-類屬性 print(getattr(PoloBlog, "sum")) # 類對象-類屬性 print(getattr(PoloBlog, "name", "默認值")) # 類對象-實例屬性 # 輸出結果 小菠蘿 0 0 默認值
setattr
# 設置一個新的實例屬性 setattr(blog, "age", 24) # 設置一個新的實例方法 setattr(blog, "printNameAge", lambda self: f"姓名:{self.name} 年齡:{self.age}") print(blog.__dict__) print(blog.printNameAge(blog)) # 輸出結果 {'name': '小菠蘿', 'age': 24, 'printNameAge': <function <lambda> at 0x10391a1f0>} 姓名:小菠蘿 年齡:24
delattr
# delattr delattr(blog, "age") delattr(blog, "printNameAge") print(blog.__dict__) # 輸出結果 {'name': '小菠蘿'}
反射本模塊的成員
除了可以檢測類中有沒有某個屬性、方法,還可以用來檢測某個模塊下有沒有方法、類、變量
sums = 0 def test1(): print("test") class A(): pass this_module = sys.modules[__name__] print(__name__) print(this_module) print(hasattr(this_module, "sums")) # 變量 print(hasattr(this_module, "test1")) # 方法 print(hasattr(this_module, "A")) # 類 # 輸出結果 __main__ <module '__main__' from '/Users/polo/Documents/pylearn/第四章:面向對象/22_反射.py'> True True True
反射其他模塊的成員
輸出結果
True
反射22222
小菠蘿
fanshe 是另一個模塊
反射的應用一
需求
- 打開瀏覽器,訪問一個網站
- 單擊登錄就跳轉到登錄界面
- 單擊注冊就跳轉到注冊界面
- 單擊的其實是一個個的鏈接,每一個鏈接都會有一個函數或者方法來處理
未使用反射前
class Web: def login(self): print('歡迎來到登錄頁面') def register(self): print('歡迎來到注冊頁面') def save(self): print('歡迎來到存儲頁面') while True: obj = Web() choose = input(">>>").strip() if choose == 'login': obj.login() elif choose == 'register': obj.register() elif choose == 'save': obj.save()
使用反射后
class Web: def login(self): print('歡迎來到登錄頁面') def register(self): print('歡迎來到注冊頁面') def save(self): print('歡迎來到存儲頁面') while True: obj = Web() choose = input(">>>").strip() # 判斷對象是否有對應的方法 if hasattr(obj, choose): # 獲取對應的方法 f = getattr(obj, choose) # 執行方法 f()
反射的應用二
在做接口自動化測試的時候,我們一般都會封裝 BaseRequest 類來進行復用,類里面會封裝不同請求方法
未使用反射前
class BaseRequest: req = requests.Session() def get(self, url): resp = self.req.get(url) print("==get==") return resp def post(self, url): resp = self.req.post(url) print("==post==") return resp def put(self, url): resp = self.req.put(url) print("==put==") return resp # 不使用反射的方法 def main(self, method, url): if method == "get": self.get(url) elif method == "post": self.post(url) elif method == "put": self.put(url)
使用反射后
# 使用反射的方法 def main_attr(self, method, url): if hasattr(self, method): func = getattr(self, method) func(url)
執行代碼
request = BaseRequest() # 不使用反射 request.main("get", "http://www.baidu.com") request.main("post", "http://www.baidu.com") request.main("put", "http://www.baidu.com") # 使用反射 request.main_attr("get", "http://www.baidu.com") request.main_attr("post", "http://www.baidu.com") request.main_attr("put", "http://www.baidu.com") # 輸出結果 ==get== ==post== ==put== ==get== ==post== ==put==
總結
當封裝了多個方法,然后需要根據不同條件去調用不同方法的時候,就可以考慮使用反射了,代碼量少不是丁點半點