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

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