Python - 面向對象編程 - 反射 hasattr、getattr、setattr、delattr


什么是反射

反射的概念是由 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==

 

總結

當封裝了多個方法,然后需要根據不同條件去調用不同方法的時候,就可以考慮使用反射了,代碼量少不是丁點半點

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM