• Python 面向对象进阶


    1 isinstance 和issubclass

    instance:判断该对象是否是类的对象

    isinstance(obj,Foo)

    x = []
    print(isinstance(x,list))
    

    结果:
    True

    issubclass:判断是否是继承

    
    class Foo:
        pass
    class Bar(Foo):
        pass
    print(issubclass(Bar,Foo))
    

    结果:
    True

    通过下面的方式也可以查看
    print(Bar.__bases__)

    2 反射

    反射是Smi首次提出的,主要指程序可以访问、检测和修改它本身状态行为的一种能力,也可以叫做自省

    2.1面向对象中的反射

    面向对象中的反射,是通过字符串形式操作对象的属性,Python中一切都是对象,都可以使用对象

    Python中通过hasattr、getattr、setattr、deltattr实现自省的函数,适用于类和对象(一切都是对象)

    反射就是把字符串反射成相应的命令

    hasattr

    查看类和对象中是否有name,用字符串表示,实例在找的时候首先从自身找,自身没有从类找。

    class Foo:
        name = 'aaa'
    f1 =Foo()
    print(hasattr(Foo,'name'))
    print(hasattr(f1,'name'))
    

    结果:
    True
    True

    setattr

    setattr是设置属性,setattr(x,y,z)

    class Foo:
        name = 'aaa'
    f1 =Foo()
    
    setattr(Foo,'age',18)
    print(Foo,'age')
    print(f1,'age')
    

    deltattr

    print(Foo.__dict__)
    delattr(Foo,'name')
    print(Foo.__dict__)   
    print(hasattr(f1,'name'))
    

    结果:
    False
    通过打印类的名称空间可以查看到name已经没有了

    getattr

    getattr是查找那个属性,并把命名它的值获得,实际的原理是通过获得字典中的key,然后获得value

    class People:
        country = 'china'
        def __init__(self,name):
            self.name=name
    
    p = People('aaa')
    print(hasattr(p,'name'))
    print('name' in p.__dict__)  # 执行的效果是一样的e
    
    print(hasattr(p,'country'))
    print(hasattr(People,'country'))
    
    print(getattr(p,'country'))
    print(getattr(p,'__init__'))
    print(getattr(People,'country'))
    

    2.3模块的反射

    Python中一切皆对象,模块文件等都是对象

    import sys
    
    def s1():
        pass
    def s2():
        pass
    this_module = sys.modules[__name__]
    print(hasattr(this_module,'s1'))
    print(getattr(this_module,'s1'))
    
    • 结果:
      True

    function s1 at 0x00000000005F3E18

     2.4 使用反射的好处

    2.4.1 实现可拔插机制

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

    # 第一个人负责的代码
    class FtpClient:
        def __init__(self,addr):
            print("正在连接服务器%s" % addr)
            self.addr = addr
    
    
    # 第二个人引用的代码
    f1 = FtpClient("192.168.0.1")
    
    if hasattr(f1, 'get'):
        func = getattr(f1, 'get')
        func()
    else:
        print("不存在此方法")
        print("处理其他逻辑")

    第一个人的代码只提供了接口,并没有写完,但是第二个人要用第一个人的代码中,通过自省进行判断,后面的人就可以直接拿来用了

    2.4.2 动态导入模块

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

     

    3 内置 (attr ) __setattr__,__delattr__,__getattr__,

    3.1__getattr__

    getattr只有在调用的属性不存在的时候才会触发

    class Foo:
        def __init__(self,name):
            self.name = name
        def __getattr__(self, item):
            print("getattr--%s %s"%(item,type(item)))
    
    f = Foo('aaa')
    print(f.xxx)
    
    • 结果:
      getattr—xxx class ‘str’
      None

    3.2__setattr__

    setattr添加/修改属性会触发它的执行

    class Foo:
        def __init__(self,x):
            self.name=x
        def __setattr__(self, key, value):  # 与上面的对应关系是self--self,name--key,x--value
            self.__dict__[key]=value  # 真正修改的是__dict__中的内容
    f1 = Foo('aaa')
    f1.x='bbb'   # 修改属性
    f1.age=18  # 增加属性
    print(f1.__dict__) # 在名称对象的名称空间中可以显示
    

    3.3__deltattr__

    只有删除属性的时候才会触发deltatr的执行

    class Foo:
        def __init__(self,x):
            self.name=x
        def __setattr__(self, key, value):  # 与上面的对应关系是self--self,name--key,x--value
            self.__dict__[key]=value  # 真正修改的是__dict__中的内容
        def __delattr__(self, item):
            self.__dict__.pop(item)
    f1=Foo('aaa')
    print(f1.__dict__)
    del f1.name
    print(f1.__dict__)
    

    结果:
    {‘name’: ‘aaa’}
    {}

    4 反射的应用

    4.1 可插拔机制

    #客户端
    class FtpClient:
        def __init__(self,addr):
            print('正在连接服务器[%s]'%addr)
            self.addr = addr
    
        def get(self):
            print('get')
    
        def test(self):
            print('test')
    
    #服务端,需要使用客户端的内容
    import ftpclient  # 导入test模块(这里是文件)
    # f1 = FtpClient('192.168.1.1')  # wrong
    f1=ftpclient.FtpClient('192.168.1.1')   #这里的对象是文件,即ftpclient,对这个对象进行实例化
    if hasattr(f1,'get'):  # 首先看是否有这个功能
        func_get=getattr(f1,'get')  # getattr是获得相应的内存地址
        func_get()
    else:  # else预语言中是真正的逻辑
        print('其他逻辑')
    

    4.2 通过字符串导入模块

    m = input("输入你要导入的模块")
    m1 = __import__(m)
    print(m1)
    
    import importlib
    t = importlib.import_module('time')
    print(t.time)
    

    5 二次加工标准类型(包装)

    Python内部已经有了标准的数据类型和内置方法,但是在很多的情况下,我们需要对数据类型进行自己定制,这几用到了继承和派生的知识

    对append添加限制,只能添加数字类型的

    class List(list): # List 继承了列表list
        def append(self, p_object):  # 仅仅对append进行修改
            if not isinstance(p_object,int):  # 限制数据类型
                raise TypeError('must be int')
            super().append(p_object)  # 真正的修改,super是对父类进行修改
    l = list([1,2,3])
    print(l)
    l.append(4)   # 这里是对属性进行修改,实际是对类进行修改
    print(l)
    

    结果:
    [1, 2, 3]
    [1, 2, 3, 4]

    上面的情况仅仅是对append进行了修改并添加了限制,list的其他的属性如insert是没有限制的

    授权

    授权是包装的一个特性,上面的情况是通过继承来实现的,但是函数是不能继承的

    授权的过程是所有更新的功能都是由新类的某部分来处理,已经存在的就授权给对象的默认属性

    实现授权的关键点就是覆盖getattr方法

    import time
    class Open:
        def __init__(self,filepath,mode='r',encoding='utf-8'):
            self.x = open(filepath,mode=mode,encoding=encoding)  # self.x是文件句柄 拿到文件中的内容
            self.filepath=filepath
            self.mode=mode
            self.encoding=encoding
        def write(self,line):  # 在类中新建write功能
            t=time.strftime('%Y-%m-%d %X')
            self.x.write("%s %s" %(t,line))  # self.x存放的是文件句柄
        def __getattr__(self, item):  # 通过getattr实现的叫做授权
            # print('---->',item,type(item))  # 测试查看类型
            return getattr(self.x,item)  # 从一个文件对象获得 字符串   现在获得的是self.x 相应方法的地址
    f=Open('b.txt','w') #实例化一个对象 传入相应的参数
    f.write('123
    ')   #Open没有实际的writearttribute
    
    f.write('123
    ')
    f.write('123
    ')
    f.write('123
    ')
    f.write('123
    ')
    f.write('123
    ')
    f.write('123
    ')
    

    这样就能在文件中添加了带有时间的内容

    对文件进行读操作:

    f=Open('b.txt','r')  # 重新实例化
    res = f.read()# f.read实际是self.x 的方法
    print(res)
    f.seek(0)  #此时光标位于文件的最后
    print(f.read())  #这样就可以重新读文件
    
  • 相关阅读:
    VS2015调试ArcMap Add-in插件提示尝试运行项目时出错,无法启动程序“路径arcmap.exe”
    c#重命名文件,报错“System.NotSupportedException”类型的未经处理的异常在 mscorlib.dll 中发生”
    C# string contains 不区分大小写
    CSS div 高度满屏
    ArcGIS Server SOE开发之奇怪异常:
    C# 读取XML注释
    .Net程序员之不学Java做安卓开发:奇怪的Java语法
    .Net程序员之不学Java做安卓开发:Android Studio中的即时调试窗口
    JS去遍历Table的所有单元格中的内容
    判断 checkbox 是否选中以及 设置checkbox选中
  • 原文地址:https://www.cnblogs.com/Python666/p/6757870.html
Copyright © 2020-2023  润新知