zoukankan      html  css  js  c++  java
  • 第三十四篇 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()
  • 相关阅读:
    3.Appium运行时出现:Original error: Android devices must be of API level 17 or higher. Please change your device to Selendroid or upgrade Android on your device
    3.Python连接数据库PyMySQL
    2.Python输入pip命令出现Unknown or unsupported command 'install'问题解决
    2.Linux下安装Jenkins
    5.JMeter测试mysql数据库
    Android 4学习(7):用户界面
    Android 4学习(6):概述
    Android 4学习(5):概述
    Android 4学习(4):概述
    Android 4学习(3):概述
  • 原文地址:https://www.cnblogs.com/victorm/p/9341003.html
Copyright © 2011-2022 走看看