• 第三十四篇 Python面向对象之 反射(自省)


    什么是反射?

    反射的概念是由Smith在1982年提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成就。

    四个可以实现自省的函数,是Python的内置函数

      下列方法适用于类和对象
      • 先看这四个方法对实例(b1)的使用
    # 演示代码
    class BlackMedium:
        feature = 'Ugly'
        def __init__(self, name, address):
            self.name = name
            self.address = address
    
        def sell_house(self):
            print("[%s] 是卖房子的,sb才从它这买" %self.name)
    
        def rent_house(self):
            print("[%s] 是租房子的,sb才从它这租,它黑人" %self.name)
    
    # 实例化
    b1 = BlackMedium('某某置业', '回龙观')
    • hasattr(object, name):判断object里有没有一个name字符串('属性名')对应的方法或属性。

      object:表示对象; name:属性名,是字符串形式;

    # 检测数据属性
    print(hasattr(b1, 'name'))        # True     # b1.__dict__['name']
    # 检测函数属性
    print(hasattr(b1, 'sell_house'))  # True
    print(hasattr(b1, 'sell_housereqre'))  # False
    • getattr(object, name, default=None): 获取属性值

      object:表示对象; name:属性名,是字符串形式;value:属性对应的值

    # 获取属性的具体值
    print(getattr(b1, 'name'))   # 某某置业
    print(getattr(b1, 'rent_house'))  # <bound method BlackMedium.rent_house of <__main__.BlackMedium object at 0x00B52F50>>
    func = getattr(b1, 'rent_house')
    func()    # [某某置业] 是租房子的,sb才从它这租,它黑人
    print(getattr(b1, 'feature'))   # Ugly
    # default 参数
    print(getattr(b1, 'sell_house323', '没有这个属性')) # 没有这个属性
    
    getattr()  # 等价于 b1.sell_house
    • setattr(object, name, value): 修改或者新增属性及值

      object:表示对象; name:属性名,是字符串形式;value:属性对应的值

    # setattr设置数据属性
    setattr(b1, 'sb', True)
    setattr(b1, 'sb1', 1234)
    setattr(b1, 'name', "万神置业")
    setattr(b1, 'feature', '黑中介')
    print(b1.__dict__)
    #  {'name': '万神置业', 'address': '回龙观', 'sb': True, 'sb1': 1234, 'feature': '黑中介'}
    
    # setattr设置函数属性
    setattr(b1, 'funct', lambda x:x+1)
    setattr(b1, 'funct1', lambda self:self.name+"ss")
    print(b1.__dict__)
    # {'name': '万神置业', 'address': '回龙观', 'sb1': 1234, 'feature': '黑中介', 'funct': <function <lambda> at 0x02FEB618>}
    # 调用新增的函数属性
    print(b1.funct(10))     # 11
    print(b1.funct1(b1))    # 万神置业ss
    • delattr(object, name)  删除属性。

      object:表示对象; name:属性名,是字符串形式

    delattr(b1, 'sb')   # 等于 del b1.sb
    print(b1.__dict__)
    #  {'name': '万神置业', 'address': '回龙观', 'sb1': 1234, 'feature': '黑中介'}
      •   再看这四个方法对类(BlackMedium)的使用 
    # 定义类,但没有进行实例化
    class BlackMedium:
        feture='Ugly'
        def __init__(self,name,addr):
            self.name=name
            self.addr=addr
    
        def sell_hourse(self):
            print('【%s】 正在卖房子,傻逼才买呢' %self.name)
    
        def rent_hourse(self):
            print('【%s】 正在租房子,傻逼才租呢' % self.name)
    # hasattr()
    print(hasattr(BlackMedium,'feture'))  # True
    
    print(getattr(BlackMedium,'feture'))
    
    print(setattr(BlackMedium, 'feture', '黑中介'))
    print(getattr(BlackMedium, 'feture'))  # 黑中介
    
    delattr(BlackMedium, 'sell_hourse')
    print(BlackMedium.__dict__)
    # {'__module__': '__main__', 'feture': '黑中介', '__init__': <function BlackMedium.__init__ at 0x007D1B70>, 'rent_hourse': <function BlackMedium.rent_hourse at 0x007D1AE0>, '__dict__': <attribute '__dict__' of 'BlackMedium' objects>, '__weakref__': <attribute '__weakref__' of 'BlackMedium' objects>, '__doc__': None}
    #

    Python一切皆对象,文件也是对象,所以文件也可反射

    def say_hi():
        print('你好啊')
    test.py文件
    # fanshe.py 文件
    import
    test as obj # 导入test模块,重命名为obj print(obj) print(hasattr(obj,'say_hi')) # True print(hasattr(obj,'say_hisssssssssssssssssssssssssssssssssssssssssss')) # False if hasattr(obj,'say_hi'): func=getattr(obj,'say_hi') func() else: print('其他的逻辑') x=111 y=222 # 执行结果 # 你好阿
    # 需求:检测一下fanshe.py这个当前文件有没有某些属性
    
    import test as obj  # 导入test模块,重命名为obj
    
    print(obj)
    
    print(hasattr(obj,'say_hi'))  # True
    print(hasattr(obj,'say_hisssssssssssssssssssssssssssssssssssssssssss'))  # False
    
    if hasattr(obj,'say_hi'):
        func=getattr(obj,'say_hi')
        func()
    else:
        print('其他的逻辑')
    
    x=111
    y=222
    
    
    # 关键问题是,上面拿到别人的导入就行了,那怎么拿到自己的呢?
    
    # import fanshe as obj1   # 在当前文件里是不可以再导入自己的
    # 需要用sys模块来操作
    import sys
    obj1=sys.modules[__name__]
    
    print('===>',hasattr(obj1,'x'))

    二. 为什么用反射?

    好处一:实现可插拔机制

    是项目中,一个项目有多个程序员写,如果A写程序的时候要用到B所写的类,但是B休假了,还没有完成他写的类,A想到了反射,使用了反射机制A可以继续完成自己的代码,等B休假回来后再继续完成类的定义并且实现A想要的功能。

    总之,反射好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种“后期绑定”,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。

    演示:

    class FtpClient:
        'ftp客户端,还没具体实现功能'
        def __init__(self,addr):
            print("正在连接服务器[{}]".format(addr))
            self.addr = addr
    
    f1 = FtpClient('192.168.123.123')
    # f1.put()   # 因为B还没定义put()方法,所以会报错,不能这么干,所以需要用下面的判断做
    if hasattr(f1,'put'):   # 判断f1里是否有put 方法
        func_get =  getattr(f1, 'put')   # 如果有,就可以get到这个方法
        func_get()   # 然后运行这个方法
    else:
        print("执行其他逻辑")     # 如果没有put这个方法,就执行其他逻辑
    
    #  结果
    正在连接服务器[192.168.123.123]
    执行其他逻辑
    B只定义了接口,还没实现功能
    class FtpClient:
        'ftp客户端,还没具体实现功能'
        def __init__(self,addr):
            print("正在连接服务器[{}]".format(addr))
            self.addr = addr
        # B休假回来了,实现了put方法
        def put(self):
            print("文件开始上传了")
    
    # from try import FtpClient
    f1 = FtpClient('192.168.123.123')
    # f1.put()
    if hasattr(f1,'put'):   # 判断f1里是否有put 方法
        func_get =  getattr(f1, 'put')   # 如果有,就可以get到这个方法
        func_get()   # 然后运行这个方法
    else:
        print("执行其他逻辑")     # 如果没有put这个方法,就执行其他逻辑
    
    #结果
    正在连接服务器[192.168.123.123]
    文件开始上传了
    B休假回来,实现了A想要的方法

     好处二:动态导入模块(基于反射当前模块成员)

    模块的动态导入

    所谓动态导入模块,就是模块名是字符串形式的,根据字符串使用importLib模块导入。

    如果要导入的模块给你的是一个字符串,那怎么导入呢?目录结构如下:

    需求是:要将m1下的t.py导入到01.动态导入模块.py文件里

    # 1. 正常的导入及调用执行
    from m1 import t
    t.test1()
    # 2. 现在有个新问题,要求你不能按照上面的方式到导入,给的是个字符串  'm1.t'
    # 通过字符串方式导入模块
    module_t = __import__('m1.t')
    print(module_t)
    # <module 'm1' (namespace)>
    # 为什么导入的模块是m1,而不是t呢?而我们要导入的就是t模块啊?
    '''
    这种导入方式,不管你下面套多少层,返回的都是最顶层的模块
    '''
    # 调用:都需要通过最顶层的模块以.的方式,一层一层的往下找,直到调用你需要引用的模块
    module_t.t.test1()
    # 3. 利用importlib模块导入字符串形式的模块
    import importlib
    m = importlib.import_module("m1.t")
    print(m)
    # <module 'm1.t' from 'F:\workspace\Try2\m1\t.py'>
    # 以这种方式导入,直接就定位到你想要调用的t模块了
    
    # 调用
    m.test1()
  • 相关阅读:
    coder的脚印
    Mysql
    MSDos
    Windows Develop
    Eclipse 使用总结
    DBA常用SQL
    SSH总结
    Unity3D协程
    yield的作用
    UGUI优化
  • 原文地址:https://www.cnblogs.com/victorm/p/9341003.html
Copyright © 2020-2023  润新知