Python之反射淺談

日期:2020年3月19日
作者:Commas
註釋:學習就是爲了忘記
如果您想了解更多有關Python的知識,那麼請點《我的Python淺談系列目錄》



一、反射是什麼?

反射是一個非常重要的概念,它是一種字符串對象的映射關係,利用字符串去操作對象,可以對操作的對象進行成員的查找、獲取、添加以及刪除。通俗地說,反射是一種基於字符串的事件驅動,使用字符串就可以獲取對象中的屬性或方法(方法的內存地址,加西文圓括號()與對應傳參,即可執行),也可以添加或刪除對象中的屬性或方法,是不是很神奇呢?那我們接着往下看吧。

二、反射的四大工具

所謂工欲善其事,必先利其器。我們先來了解一下python反射的四大工具,如下:

分類 內置函數(工具) 說明
判斷 hasattr(obj, name) 判斷對象obj中是否有屬性name
獲取 getattr(obj, name[, default]) 獲取對象obj的屬性name,等價於obj.name。若default有值,則name不存在就調用obj.default,否則name不存在就拋出異常
新增 setattr(obj, name, value) 給對象obj設置一個屬性name,並且爲其賦值value,等價於obj.name = value
刪除 delattr(obj, name) 刪除對象obj的name屬性,等價於del obj.name

其中attr是attribute的縮寫,即屬性。

三、反射的對象有哪些?

可以使用反射的對象總共有4類,如下:

  • 實例化對象
  • 類(類也是一個對象,類對象)
  • 本模塊(.py結尾文件)
  • 其他模塊(.python結尾文件)

(1)實例化對象的反射實例

class Rectangle:
    """矩形類"""
    def __init__(self, b, h):
        self.b = b
        self.h = h

    def cal_area(self):
        return self.b*self.h

    def cal_perimeter(self):
        return (self.b+self.h)*2


def main():
    # 實例化對象
    rect = Rectangle(4, 5)
    # (1)直接調用對象方法
    print("(1)直接調用對象方法")
    print(rect.b)
    print(rect.h)
    print(rect.cal_area())
    print(rect.cal_perimeter())

    # (2)實例化對象的反射
    print("(2)實例化對象的反射")
    list_attr = ["b", "h", "cal_area", "cal_perimeter"]
    for str_attr in list_attr:
        if hasattr(rect, str_attr):
            obj_attr = getattr(rect, str_attr)
            if callable(obj_attr):  # if hasattr(obj_attr, '__call__'): #判斷是否可調用,即是否爲方法
                # 反射屬性(對象方法)的地址
                # print("{}的地址爲:{}".format(str_attr, obj_attr))
                # 執行方法
                print("{}(方法)的計算結果爲 {}".format(str_attr, obj_attr()))
            else:
                print("{}(屬性)的值爲{}".format(str_attr, obj_attr))


if __name__ == "__main__":
    main()

控制檯輸出結果:
(1)直接調用對象方法
4
5
20
18
(2)實例化對象的反射
b(屬性)的值爲4
h(屬性)的值爲5
cal_area(方法)的計算結果爲20
cal_perimeter(方法)的計算結果爲18

(2)類對象的反射實例

class Rectangle:
    """矩形類"""
    def __init__(self, b, h):
        self.b = b
        self.h = h

    @classmethod
    def show_me(cls):
        return "大家好,我是{}".format(cls.__doc__)

    @staticmethod
    def show_me2():
        return "我是一個Rectangle類中的靜態方法show_me2()"

    def cal_area(self):
        return self.b*self.h

    def cal_perimeter(self):
        return (self.b+self.h)*2


def main():
    list_attr = ["__doc__", "show_me", "show_me2"]
    for str_attr in list_attr:
        if hasattr(Rectangle, str_attr):
            obj_attr = getattr(Rectangle, str_attr)
            if callable(obj_attr):  # if hasattr(obj_attr, '__call__'): #判斷是否可調用,即是否爲方法
                # 反射屬性(對象方法)的地址
                # print("{}的地址爲:{}".format(str_attr, obj_attr))
                # 執行方法
                print("{}(方法)的計算結果爲 {}".format(str_attr, obj_attr()))
            else:
                print("{}(屬性)的值爲{}".format(str_attr, obj_attr))


if __name__ == "__main__":
    main()

控制檯輸出結果:
__doc__(屬性)的值爲矩形類
show_me(方法)的計算結果爲 大家好,我是矩形類
show_me2(方法)的計算結果爲 我是一個Rectangle類中的靜態方法show_me2()

(3)本模塊的反射實例

import sys

var1 = "我是來自selfpy.py中的變量var1"


def show_me():
    return "我是來自selfpy.py中的show_me()"


def main():
    # 獲取本模塊的對象
    print("__name__的值爲%s" % __name__)
    obj = sys.modules[__name__]

    # (1)獲取屬性var1
    str_attr = "var1"
    print("(1)獲取屬性%s" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
    else:
        print("未找到%s" % str_attr)
    # 調用函數show_me()

    # (2)獲取屬性var2
    str_attr = "var2"
    print("(2)獲取屬性%s" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
    else:
        print("未找到%s" % str_attr)

    # (3)調用函數show_me()
    str_attr = "show_me"
    print("(3)獲取屬性%s()" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
        print(getattr(obj, str_attr)())
    else:
        print("未找到%s" % str_attr)


if __name__ == '__main__':
    main()

控制檯輸出結果
__name__的值爲__main__
(1)獲取屬性var1
我是來自selfpy.py中的變量var1
(2)獲取屬性var2
未找到var2
(3)獲取屬性show_me()
<function show_me at 0x000002964D70C268>
我是來自selfpy.py中的show_me()

知識加油站:

模塊或工具 說明
sys sys模塊是與python解釋器交互的一個接口
sys.modules 一個全局字典,該字典是python啓動後就加載在內存中。每當導入新的模塊,sys.modules將自動記錄該模塊。當第二次再導入該模塊時,python會直接到字典中查找,從而加快了程序運行的速度。它擁有字典所擁有的一切方法。

(4)其他模塊的反射實例

現在我們準備兩個py文件:

  • 其他py文件:otherpy.py
  • 本py文件:selfpy.py

其他py文件otherpy.py的代碼如下:

var1 = "我是來自otherpy.py中的變量var1"

def show_me():
    return "我是來自otherpy.py中的show_me()"

本py文件selfpy.py的代碼如下:


def main():
    # 導入其它py文件(對象),NO1和NO2兩種方法選一種
    
    # NO1:import object直接導入
    # import otherpy as obj
    
    # NO2:__import__(modules),其中modules爲對象的字符串名稱
    # 實現了基於字符串的動態的模塊導入
    obj = __import__("otherpy")

    # (1)獲取屬性var1
    str_attr = "var1"
    print("(1)獲取屬性%s" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
    else:
        print("未找到%s" % str_attr)
    # 調用函數show_me()

    # (2)獲取屬性var2
    str_attr = "var2"
    print("(2)獲取屬性%s" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
    else:
        print("未找到%s" % str_attr)

    # (3)調用函數show_me()
    str_attr = "show_me"
    print("(3)獲取屬性%s()" % str_attr)
    if hasattr(obj, str_attr):
        print(getattr(obj, str_attr))
        print(getattr(obj, str_attr)())
    else:
        print("未找到%s" % str_attr)


if __name__ == '__main__':
    main()

控制檯輸出結果
(1)獲取屬性var1
我是來自otherpy.py中的變量var1
(2)獲取屬性var2
未找到var2
(3)獲取屬性show_me()
<function show_me at 0x0000013AFB2DA950>
我是來自otherpy.py中的show_me()


版權聲明:本文爲博主原創文章,如需轉載,請給出:
原文鏈接:https://blog.csdn.net/qq_35844043/article/details/104987746

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章