• 〖Python〗-- 反射、内置attr、包装


    【反射、内置attr、包装】

    一、isinstance issubclass

    1isinstance(obj,cls)检查是否obj是否是类 cls 的对象。


    2issubclass(sub, super)检查sub类是否是 super 类的派生类
      判断结果为布尔值:是返回True,不是返回False

    class Bar:  #定义父类
        pass
    class Foo(Bar):  #定义子类 继承 Bar
        pass
    
    class A:  #定义类 A
        pass
    
    obj=Foo() #实例化
    a = A()   #实例化
    #isinstance
    print(isinstance(obj,Foo))  #查看obj是否是类Foo的对象
    print(isinstance(obj,Bar))  #查看obj是否是类Bar的对象
    print(isinstance(a,A))      #查看a是否是类A的对象
    print(isinstance(a,Foo))    #查看a是否是类Foo的对象
    #issubclass
    print(Foo.__bases__)     #之前查看继承的方式
    print(issubclass(Foo,Bar))  #查看类Foo 是否是类Bar的子类
    print(issubclass(A,Bar))    #查看类A 是否是类Bar的子类
    
    #执行结果:
    True
    True
    True
    False
    (<class '__main__.Bar'>,)
    True
    False

    二、反射:getattr,setattr,delattr,hasattr
      1、定义:

      反射:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

        python面向对象中的反射:通过字符串的形式,操作对象的相关的属性。python中的一切事物都是对象(都可以使用反射)

    2、应用:

      1) hasattr   查找
      hasattr(object,'name') 应用于类或对象,查看有没有所对应的方法。实质还是从类或对象的名称空间去查找。判断结果返回布尔值,有为True,没有为False.

      2)getattr   获取  

      getattr(object,name,‘返回值’) 通过字符串获取 查看有没有这个属性,获取绑定方法。

      实质还是从类或对象的名称空间去查找,有的话返回为函数内存地址,加()就能运行。

      3)setattr 设置
      setattr(x,y,v)   x=类或对象, y='字符串类型的属性名',v=value 值  实质是给类或是对象添加数据属性。

      4)delattr 删除
      delattr(x,y)   x = 类或对象,y = '字符串类型的属性名'   删除类或是对象内的某个属性!

    #coding = utf-8
    #通过字符串的形式,为类或是对象添加属性
    class People:  #定义一个类
        country = 'China'
        def __init__(self,name):
            self.name = name
    
        def test(self):
            print('test')
    
    p = People('zh')  #实例化
    
    #hasattr
    h = hasattr(p,'name')  #查看对象p有没有name属性
    print(h)  #打印布尔值
    
    #执行结果:
    True
    
    #getattr(object,name,default=None(or'自定义的值'))
    print(p.__dict__)       #查看对象的名称空间
    print(People.__dict__)  #查看类的名称空间
    g = getattr(p,'name')   #获取对象name方法
    g1 = getattr(p,'test')  #获取对象test的绑定方法
    g2 = getattr(People,'test')  #获取类test方法
    g3 = getattr(People,'work',"没有此方法!")  #获取类work方法,没有打印value值
    print(g,g1,g2,g3)  #打印
    g1()  #执行对象对应的方法,不需要传值
    g2(p)  #执行类对应的方法,需要传值
    
    #执行结果:
    {'name': 'zh'}
    {'__module__': '__main__', 'country': 'China', '__init__': <function People.__init__ at 0x00000000028EC9D8>, 'test': <function People.test at 0x00000000028ECA60>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
    zh <bound method People.test of <__main__.People object at 0x00000000028FF208>> <function People.test at 0x00000000028ECA60> 没有此方法!
    test
    test
    
    #setattr()只能更改类或是对象内的 数据属性,函数属性没法变更。
    print(p.__dict__)   #设置前查看对象的名称空间
    setattr(p,'sex','male') #设置sex属性
    setattr(p,'age',18)     #设置age属性
    print(p.__dict__)      #设置后查看对象的名称空间
    print(p.sex,p.age)   #打印
    
    #执行结果:
    {'name': 'zh'}
    {'name': 'zh', 'sex': 'male', 'age': 18}
    male 18
    
    #delattr
    print(p.__dict__) #删除前查看对象的名称空间
    delattr(p,'age')  #删除 对象 的数据属性
    print(p.__dict__)  #删除操作完成,在对象的名称空间查看
    
    #执行结果:
    {'name': 'zh', 'sex': 'male', 'age': 18}
    {'name': 'zh', 'sex': 'male'}

     5)反射当前位置的模块成员。

      此处先明确两个概念:

        脚本文件:将程序写到一个文件中,以***.py的方式保存,使用的时候 利用python ***.py进行执行,该文件就称为脚本文件。

        模块:import 模块名 导入的文件就是模块,文件名就是模块名。

        在当前位置获取当前文件中定义的函数,如果直接将本文件以模块的形式导入文件中,程序执行直接触发递归,无法实现功能,此时在自己位置就不能导入自己的模块。此时就需要把本文件转成一个脚本模块,既可以导入到别的模块或文件中用,另外该模块自己也可执行。所以就需要使用下面的方法:
    方法:import sys  #导入sys模块

    this_modules = sys.modules[__name__](有返回值)  获取一个模块,将当前位置的文件转成一个脚本模块(有具体的文件地址)

    print(__name__)

    注意:__name__的用法:  如果我们是直接在本文件执行,那该文件中'__name__' == '__main__',但是如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__。(有一种加上保护锁的感觉)

    import sys  #导入sys模块
    def add():
        print('add')
    
    def change():
        print('change')
    
    def search():
        print('search')
    
    def delete():
        print('delete')
    
    this_module = sys.modules[__name__]   #利用sys模块中的modules方法,将本文件转成脚本模块
    print(this_module)  #查看
    print(__name__)  #查看__name__
    while True:
        m = input('input something:').strip()
        if not m :continue
        if hasattr(this_module,m):  #判断输入的内容在不在模块 类中
            func = getattr(this_module,m)  #获取这个方法,拿到返回值
            func()  #执行函数
        else:
            print('没有此方法!')
    
    #执行结果:
    <module '__main__' from 'F:/py_fullstack_s4/day31/__name__及反射的用途.py'>
    __main__
    input something:add
    add
    input something:work
    没有此方法!

    3、反射的好处:

      好处一:可以利用反射实现可插拔机制。

      可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,即事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。不影响其他人员对程序的调用和开发。

    class FtpClient:
        'ftp客户端,但是还没有实现具体的功能'
        def __init__(self,addr):
            print('正在连接服务器[%s]' %addr)
            self.addr=addr
        def get(self):
            print('get------->')
    ftpclient.py
    import ftpclient   #将文件以模块形式导入
    '''ftp服务端,需要用到客户端的功能'''
    f1=ftpclient.FtpClient('192.168.1.1')  #调用文件中的类
    if hasattr(f1,'get'):   #判断是否有这功能
        func_get=getattr(f1,'get')  #获取功能,得到返回值(对应函数的内存地址)
        func_get() #执行函数
    else:
        print('---->不存在此方法')
        print('处理其他的逻辑')
    ftpsever.py
    正在连接服务器[192.168.1.1]
    get------->
    执行ftpsever.py结果

     好处二:通过字符串动态导入模块

    #不推荐利用__import__(字符串) 的方法,官方推荐importlib.import_module(字符串)的方式
    import importlib
    # m = input('please input something:')
    t = importlib.import_module('time')
    print(t.time())
    
    #执行结果:
    1493024228.788776

    三、内置attr:__getattr__,__setattr__,__delattr__
    大前提:类内部设置
      1、__setattr__

      以函数方式,设置在类内部,实例化传值自动触发,直接获取为对象设置的属性,导致对象的名称空间中无法找到方法。

     
    class Foo:
        def __init__(self,name): #初始化属性
            self.name = name
        def __setattr__(self, key, value): # 拿到实例化的值
            print('---setattr---key:%s,value:%s'%(key,value))
         #self.__dict__[key] = value  #在对象的名称空间中添加方法
    
    #调用__setattr__的方法
    f = Foo('zh')  #类的实例化
    f.age = 18  #给类定义一个数据属性
    print(f.__dict__)  #打印当前字典
    
    #执行结果:
    ---setattr---key:name,value:zh
    ---setattr---key:age,value:18
    {}
     

      利用此种方法可以加上自己的限制,然后再通过 self.__dict[key]=value 的方法 添加到对象的名称空间中。

     2、__delattr__   

       删除某个属性,但不会真正删除。若删除的话必须通过 self.__dict__.pop(item)  在方法字典中 删除 

     3、__getattr__  (特别注意该内置函数的方法!!!)

      类内该属性不存在的情况下,才会触发执行,返回None;只要有该方法,就不执行。

     

     
    class Foo:
        def __init__(self,x): #初始化属性
            self.name = x
         # self.name = x ------------>  self = self;key = name; value = x
        def __setattr__(self, key, value): # 自动触发,获取self.属性名=值  self = self;key = 属性名;value = 值
            print('---setattr---key:%s,value:%s'%(key,value))
            self.__dict__[key] = value  #在对象的名称空间中添加方法
    
        def __delattr__(self, item):# 删除某项属性
            print('delattr:%s'%item)
            print(f.__dict__)  #打印当前对象的名称空间
            self.__dict__.pop(item)  #删除对象内的方法
    
        def __getattr__(self, item):  # 没有方法触发
            print('getattr---> %s %s'%(item,type(item)))
    
    #调用__setattr__的方法
    f = Foo('zh')  #类的实例化
    f.age = 18  #给类定义一个数据属性
    print(f.__dict__)  #打印当前字典
    
    #执行结果:
    ---setattr---key:name,value:zh
    ---setattr---key:age,value:18
    {'name': 'zh', 'age': 18}
    
    #调用__delattr__的方法
    del f.age
    print(f.__dict__)
    
    #执行结果:
    delattr:age
    {'name': 'zh', 'age': 18}
    {'name': 'zh'}
    
    #调用__getattr__的方法
    print(f.name)
    print(f.azcw)
    
    #执行结果:
    zh
    getattr---> azcw <class 'str'>
    None
     

     

    四、二次加工标准类型
    1、包装:基于继承实现对标准数据类型的包装对已有的数据类型进行包装,新增/改写方些方法,用以定制我们自己的数据类型。

      实质:创建一个新的类,给数据类型格外添加新的功能或是判断的功能。

     
    class List(list):  #以列表为例
        def append(self, p_object): #重新定义append方法,只允许添加数据类型
            if not isinstance(p_object,int):  #判断 添加的数据是不是数据类型
                raise TypeError('must be int')
            super().append(p_object)  #原定义的其他功能默认不动
    
    l = List([1,2,3,4])
    print(l)
    l.append(5)
    print(l)
    l.append('a')
    print(l)
    
    #执行结果:
    [1, 2, 3, 4]
    [1, 2, 3, 4, 5]
      File "F:/py_fullstack_s4/day31/定义自己的数据类型.py", line 12, in <module>
        l.append('a')
      File "F:/py_fullstack_s4/day31/定义自己的数据类型.py", line 5, in append
        raise TypeError('must be int')
    TypeError: must be int
     

     

     
    #基于继承的原理,定义自己的数据类型。
    #虽然更改了列表的append方法,但是对列表的其他功能没有改变
    class List(list):  #以列表为例
        def append(self, p_object): #重新定义append方法
            if not isinstance(p_object,int):  #判断 添加的数据是不是数据类型
                raise TypeError('must be int')
            super().append(p_object)  #原定义的其他功能默认不动
    
    l = List([1,2,3,4])
    print(l)
    l.append(5)
    print(l)
    # l.append('a')
    # print(l)
    l.insert(0,-1)  #列表的插入方法不改变
    l.insert(1,'abc')
    print(l)
    
    #执行结果:
    [1, 2, 3, 4]
    [1, 2, 3, 4, 5]
    [-1, 'abc', 1, 2, 3, 4, 5]
     

     

    2、授权:实现授权的关键点,就是覆盖__getattr__的方法

    对系统已经定义的函数进行包装的过程叫做授权,函数不再是类,有继承的方式,而是所有需要添加的功能由类中再定义,判断结束再写入该函数。已存在的功能默认执行。

      实质:创建一个新的类,给函数格外添加新的功能或是判断的功能。

     

     
    #不能用继承,来实现open函数的功能
    # f=open('a.txt','w')
    # print(f)
    # f.write('1111111')
    
    #授权的方式实现定制自己的数据类型
    import time
    class Open:
        def __init__(self,filepath,m='r',encode='utf-8'):  #初始化定义open函数的默认属性
            self.filepath=filepath
            self.mode=m
            self.encoding=encode
            self.x = open(filepath, mode=m, encoding=encode)  #定义结束,以这种方式执行原函数
    
        def write(self,line):   #重新定义写的功能:每行写入的同时也写入时间
            print('f自己的write',line)   #打印要添加的内容
            t=time.strftime('%Y-%m-%d %X')  #时间,格式:年-月-日 具体时间
            self.x.write('%s %s' %(t,line))  #利用原函数写入新内容
    
        def __getattr__(self, item):  #关于其他内容
            print('=------>',item,type(item))
            return getattr(self.x,item)  #返回原函数方法的形式,以原函数对应的这个的方法执行。
    
    #在类中定义好的方法,实例化进行调用
    f=Open('b.txt','a+')  #实例化方法
    print(f)  #查看f的类型
    f.write('hello world!
    ')  #执行绑定方法,在文件中写入内容
    f.close()  #关闭文件,此方法未定义,执行__getattr__方法,利用原函数的方法
    
    #未在类中定义的方法,通过触发__getattr__的方式,来执行原函数的功能!
    f=Open('b.txt','r+')  #实例化方法
    print(f.read)   #查看未定义方法read 的类型
    print(f.read())  #执行方法 self.x.read()
    print('=-=====>',f.read()) #文件读完之后打印
    f.seek(0)    #将光标移动到初始开始位置
    print(f.read()) #再次打印文件中的内容
    # f.flush()
    f.close()  #关闭文件,此方法未定义,执行__getattr__方法,利用原函数的方法
    
    #执行结果:
    <__main__.Open object at 0x00000000028AA908>
    f自己的write hello world!
    
    =------> close <class 'str'>
    =------> read <class 'str'>
    <built-in method read of _io.TextIOWrapper object at 0x000000000241AB40>
    =------> read <class 'str'>
    
    2017-09-24 18:31:05 hello world!
    
    =------> read <class 'str'>
    =-=====> 
    =------> seek <class 'str'>
    =------> read <class 'str'>
    
    2017-09-24 22:31:07 hello world!
    
    =------> close <class 'str'>
     
  • 相关阅读:
    POJ 2054 Color a Tree
    UVA12113-Overlapping Squares(二进制枚举)
    UVA690-Pipeline Scheduling(dfs+二进制压缩状态)
    UVA818-Cutting Chains(二进制枚举+dfs判环)
    UVA211-The Domino Effect(dfs)
    UVA225-Golygons(dfs)
    UVA208-Firetruck(并查集+dfs)
    UVA1374-Power Calculus(迭代加深搜索)
    UVA1374-Power Calculus(迭代加深搜索)
    UVA1434-The Rotation Game(迭代加深搜索)
  • 原文地址:https://www.cnblogs.com/SHENGXIN/p/7589073.html
Copyright © 2020-2023  润新知