• 面向对象程序设计(高级)


    isinstance和issubclass

      isinstance()判断一个对象是不是这个类的对象,传两个参数(对象,类)

      issubclass()判断一个类是不是另一类的子类,传两个参数(子类,父类)

    class Foo:
        pass
    
    class Son(Foo):
        pass
    
    s = Son()
    #判断一个对象是不是这个类的对象,传两个参数(对象,类)
    print(isinstance(s,Son))
    print(isinstance(s,Foo))
    #type更精准
    print(type(s) is Son)
    print(type(s) is Foo)
    
    #判断一个类是不是另一类的子类,传两个参数(子类,父类)
    print(issubclass(Son,Foo))
    print(issubclass(Son,object))
    print(issubclass(Foo,object))
    print(issubclass(int,object))
    test

    反射

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

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

      四个可以实现反射的函数:hasattr,getattr,setattr,delattr。

      hasattr判断object,类中有没有一个name字符串对应的方法或属性,返回bool。

    class People:
        country='China'
        def __init__(self,name):
            self.name=name
        # def walk(self):
        #     print('%s is walking' %self.name)
    p=People('egon')
    #hasattr是否有这个属性
    print('name' in p.__dict__)#True
    print(hasattr(p,'name'))#True
    print(hasattr(p,'name1213'))#False
    hasattr

      getattr,通过字符串的方式直接操作。

    res=getattr(p,'country') #拿到返回值相当于res=p.country
    print(res)#China
    
    f=getattr(p,'walk') #t=p.walk
    print(f)#<bound method People.walk of <__main__.People object at 0x000000000221DE10>>
    
    f1=getattr(People,'walk')
    print(f1)#<function People.walk at 0x000000000221BA60>
    
    f()#egon is walking
    f1(p)#egon is walking
    getattr
    print(getattr(p,'xxxxxxxx','这个属性确实不存在'))
    p.sex='male'
    print(p.sex)#male
    print(p.__dict__)#{'name': 'egon', 'sex': 'male'}
    
    setattr(p,'age',18)
    print(p.__dict__)#{'age': 18, 'name': 'egon', 'sex': 'male'}
    print(p.age)#18
    print(getattr(p,'age'))#18
    setattr
    # print(p.__dict__)
    # del p.name
    # print(p.__dict__)
    
    print(p.__dict__)#{'name': 'egon', 'sex': 'male', 'age': 18}
    delattr(p,'name')
    print(p.__dict__)#{'sex': 'male', 'age': 18}
    delattr
    #反射当前模块的属性也就是模块级别的反射
    import sys
    x=1111
    class Foo:
        pass
    def s1():
        print('s1')
    
    def s2():
        print('s2')
    
    
    # print(__name__)
    
    this_module = sys.modules[__name__]#获取当前模块的对象
    print(this_module)#拿到当前的模块了
    
    print(hasattr(this_module, 's1'))#模块有没有这个属性
    print(getattr(this_module, 's2'))
    print(this_module.s2)#下面两者等价于上面
    print(this_module.s1)
    模块级别的反射
    import sys
    def add():
        print('add')
    
    def change():
        print('change')
    
    def search():
        print('search')
    
    
    def delete():
        print('delete')
    
    this_module=sys.modules[__name__]
    while True:
        cmd=input('>>:').strip()
        if not cmd:continue
        if hasattr(this_module,cmd):
            func=getattr(this_module,cmd)
            func()
        # if cmd in func_dic: #hasattr()
        #     func=func_dic.get(cmd) #func=getattr()
        #     func()
    
    
    
    #
    func_dic={
        'add':add,
        'change':change,
        'search':search,
        'delete':delete
    }
    
    
    while True:
        cmd=input('>>:').strip()
        if not cmd:continue
        if cmd in func_dic: #hasattr()
            func=func_dic.get(cmd) #func=getattr()
            func()
    早前就一直使用的函数反射

      总的来说反射其实就是在找他们的名称空间是否有这些名称,也就是是否有我们需要的属性,然后返回相关的值。

    class Foo:
        x=1
        def __init__(self,name):
            self.name=name
        def walk(self):
            print('walking......')
    f=Foo('egon')
    Foo.__dict__={'x':1,'walk':....}
    
    'x' in Foo.__dict__ #hasattr(Foo,'x')
    Foo.__dict__['x'] #getattr(Foo,'x')
    
    print(Foo.x) #'x' in Foo.__dict__
    反射就是找名称空间

    可插拔机制

      在现实生产环境经常会遇到两名程序员共同完成一个项目的情况,比如一个人写server端另一个人写client端,但是server的这个程序员某些功能还没写完就请假了,client的程序员需要用到他还未完成的功能,

    使用反射机制可以实现可插拔机制。反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。

    class FtpClient:
        'ftp客户端,但是还么有实现具体的功能'
        def __init__(self,addr):
            print('正在连接服务器[%s]' %addr)
            self.addr=addr
        def test(self):
            print('test')
    
        def get(self):
            print('get------->')
    ftpclient
    import ftpclient
    #
    # print(ftpclient)
    # print(ftpclient.FtpClient)
    # obj=ftpclient.FtpClient('192.168.1.3')
    #
    # print(obj)
    # obj.test()他还没写test所以会出错
    
    
    
    #
    f1=ftpclient.FtpClient('192.168.1.1')
    if hasattr(f1,'get'):#对方实现我就用
        func=getattr(f1,'get')
        func()
    else:
        print('其他逻辑')
    ftpserver

    字符串导入模块

    #不推荐
    m=input("请输入你要导入的模块:")
    
    m1=__import__(m)
    print(m1)
    print(m1.time())
    __import__
    #推荐使用方法
    import importlib
    t=importlib.import_module('time')
    print(t.time())
    导入模块

    __setattr__,__getattr__,__delattr__

      __setattr__为对象设置修改属性时触发运行。

    class Foo:
        def __init__(self,x):
            self.name=x
        #
        def __setattr__(self, key, value):
            # self.key=value#字符串类型不能使用这个方式要使用反射
            # setattr(self,key_str,value) #self.key_attribute=value相当于再次触发了 __setattr__产生递归
            self.__dict__[key]=value#设置时加入dict
    
    f1=Foo('egon') #f1.name='egon'
    
    f1.age=18#设置时触发 __setattr__
    __setattr__

      __delattr__删除对象属性时触发。

        def __delattr__(self, item):
            print('delattr:%s' %item)
            print(type(item))
            # delattr(self,item)
            # del self.item
            self.__dict__.pop(item)
    print(f1.__dict__)
    del f1.age
    print(f1.__dict__)
    print(f1.age)
    __delattr__

      __getattr__只有当查找属性不存在时才会触发,存在就直接返回了。

    class Foo:
        def __init__(self,x):
            self.name=x
    
        #属性不存在的情况下才会触发
        def __getattr__(self, item):
            print('getattr-->%s %s' %(item,type(item)))
    
    
    f=Foo('egon')
    print(f.name)#返回x
    
    print(f.xxxxxxx)#调用__getattr__
    __getattr__

    定制自己的数据类型

      我们之前学习的列表,字典等都是数据类型,我们可以通过继承派生出自己的数据类型。

    class List(list):
        pass
    l=List([1,2,3])
    print(l)
    l.append(4)
    print(l)
    list

      当然我们定制不仅仅是继承,还要派生自己的属性。

    class List(list):
        def append(self, p_object):
            # print('--->',p_object)
            if not isinstance(p_object,int):#只让append数字
                raise TypeError('must be int')
            # self.append(p_object)递归
            super().append(p_object)
        def insert(self, index, p_object):
            if not isinstance(p_object,int):
                raise TypeError('must be int')
            # self.append(p_object)
            super().insert(index,p_object)
    
    l=List([1,2,3])
    # print(l)
    # l.append(4)
    # print(l)
    
    # l.append('5')
    print(l)
    # l.insert(0,-1)
    l.insert(0,'-1123123213')
    print(l)
    list++

      注:__annotations__可以看到你要求的类型。

    def test(x:int,y:int)->int:
        return x+y
    print(test.__annotations__)
    #{'x': <class 'int'>, 'return': <class 'int'>, 'y': <class 'int'>}
    print(test(1,2))
    __annotations__

      不能用继承,来实现open函数(不是类)的功能,授权的方式实现定制自己的数据类型。

    import time
    
    
    class Open:
        def __init__(self,filepath,m='r',encode='utf-8'):
            self.x=open(filepath,mode=m,encoding=encode)#正常打开文件的操作保存给open类的私有属性
    
            self.filepath=filepath
            self.mode=m
            self.encoding=encode
    
        def write(self,line):
            print('f自己的write',line)
            t=time.strftime('%Y-%m-%d %X')
            self.x.write('%s %s' %(t,line))#self.x就是文件句柄
    
        def __getattr__(self, item):#授权,找不到属性就找他
            # print('=------>',item,type(item))
            return getattr(self.x,item)
    #
    # f=Open('b.txt','w')
    # # print(f)
    # f.write('111111
    ')
    # f.write('111111
    ')
    # f.write('111111
    ')
    
    
    f=Open('b.txt','r+')
    # print(f.write)
    print(f.read)
    
    
    res=f.read() #self.x.read()
    print(res)
    
    print('=-=====>',f.read())
    f.seek(0)
    print(f.read())
    # f.flush()
    # f.close()
    自制open类

      item系列,把对象操作属性模拟成字典的格式。用.方法调用的就是__attr__系列,用[key]方式就是调用__item__系列。

    class Foo:
        def __init__(self,name):
            self.name=name
        def __setattr__(self, key, value):#与他的区别就在于item是调用的k,v的格式
            print('setattr===>')
        def __getitem__(self, item):
            # print('getitem',item)
            return self.__dict__[item]
        def __setitem__(self, key, value):
            print('setitem-----<')
            self.__dict__[key]=value
        def __delitem__(self, key):
            self.__dict__.pop(key)
            # self.__dict__.pop(key)
        # def __delattr__(self, item):
        #     print('del obj.key时,我执行')
        #     self.__dict__.pop(item)
    
    f=Foo('egon')#调用__setattr__
    # f.name='egonlin'
    f['name']='egonlinhai'#调用__setitem__
    # print(f.name)
    # f.name='egonlin'
    # f['age']=18
    # print(f.__dict__)
    #
    del f['age'] #del f.age
    print(f.__dict__)
    
    print(f['name'])#__getitem__
    item系列

      __str__打印时触发,__repr__与前者差不多,前者输出更友好。

      __format__自定制格式化字符串

      __slots__

    class People:
        __slots__=['x','y','z']#对象不会再创建名称空间,对象都用类的名称空间
    
    p=People()
    # print(People.__dict__)没dict
    p.x=1
    p.y=2
    p.z=3
    p.a=4#__slots__使类只开辟x,y,z空间,对象名称不能加入
    print(p.x,p.y,p.z,p.a)
    # print(p.__dict__)
    
    p1=People()
    p1.x=10
    p1.y=20
    p1.z=30
    print(p1.x,p1.y,p1.z)
    print(p1.__dict__)
    __slots__

      __iter__,__next__

    class Foo:
        def __init__(self,start):
            self.start=start
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.start > 10:
                raise StopIteration
            n=self.start
            self.start+=1
            return n
    f=Foo(0)
    print(next(f)) #f.__next__()
    for i in f: # res=f.__iter__() #next(res)
        print(i)
    View Code
    class Range:
        '123'
        def __init__(self,start,end):
            self.start=start
            self.end=end
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.start == self.end:
                raise StopIteration
            n=self.start
            self.start+=1
            return n
    
    for i in Range(0,3):
        print(i)
    自制range

      __doc__,__moudle__,__class__

    class Foo:
        '我是描述信息'
        pass
    
    class Bar(Foo):
        pass
    print(Bar.__doc__) #None该属性无法继承给子类
    
    b=Bar()
    print(b.__class__)#<class '__main__.Bar'>
    print(b.__module__)#__main__
    print(Foo.__module__)#__main__
    print(Foo.__class__) #<class 'type'>
    View Code

      __del__析构方法,当对象在内存中被释放时,自动触发执行,此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    f=open('a.txt') #做了两件事,在用户空间拿到一个f变量,在操作系统内核空间打开一个文件
    del f #只回收用户空间的f,操作系统的文件还处于打开状态
    
    #所以我们应该在del f之前保证f.close()执行,即便是没有del,程序执行完毕也会自动del清理资源,于是文件操作的正确用法应该是
    f=open('a.txt')
    读写...
    f.close()#清理的时候就调用析构方法
    很多情况下大家都容易忽略f.close,这就用到了with上下文管理
    import time
    class Open:
        def __init__(self,filepath,mode='r',encode='utf-8'):
            self.f=open(filepath,mode=mode,encoding=encode)
    
        def write(self):
            pass
    
        def __getattr__(self, item):
            return getattr(self.f,item)
    
        def __del__(self):
            print('----->del')
            self.f.close()
    
    f=Open('a.txt','w')
    f1=f
    del f
    
    print('=========>')
    f.close

      __enter__和__exit__

      with open('a.txt') as f:叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            # return self
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('with中代码块执行完毕时执行我啊')
    
    
    with Open('a.txt') as f:
        print('=====>执行代码块')
        # print(f,f.name)
    上下文管理协议

      __exit__()中的三个参数分别代表异常类型,异常值和追溯信息,with语句中代码块出现异常,则with后的代码都无法执行。

      如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    class Foo:
        def __enter__(self):
            print('=======================》enter')
            return 111111111111111
    
        def __exit__(self, exc_type, exc_val, exc_tb):#with代码块已执行完就会触发__exit__
            print('exit')
            print('exc_type',exc_type)
            print('exc_val',exc_val)
            print('exc_tb',exc_tb)
            return True
    
    
    # with Foo(): #触发res=Foo().__enter__()拿到返回值
    #     pass
    
    with Foo() as obj: #res=Foo().__enter__() #obj=res
        print('with foo的自代码块',obj)#obj就是__enter__的返回值
        raise NameError('名字没有定义')
        print('************************************')#他是不会执行的抛异常已经触发__exit__了
    
    print('1111111111111111111111111111111111111111')#返回ture异常解决,否则抛异常不执行了
    上下文管理协议

      1.使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预,

      2.在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关心这个问题,这将大有用处。

    import time
    class Open:
        def __init__(self,filepath,mode='r',encode='utf-8'):
            self.f=open(filepath,mode=mode,encoding=encode)
    
        def write(self,line):
            self.f.write(line)
    
    
        def __getattr__(self, item):#这个步骤是授权的到open函数的其他方法
            return getattr(self.f,item)
    
        def __del__(self):
            print('----->del')
            self.f.close()
    
        def __enter__(self):
            return self.f#self是Open产生的实例,self.f是open函数的真实句柄两者都可以
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.f.close()
    
    
    with Open('egon.txt','w') as f:#触发__enter_-拿到真实的文件句柄self.f,与自制open类的write方法没什么关系
        f.write('egontest
    ')
        f.write('egontest
    ')
        f.write('egontest
    ')
        f.write('egontest
    ')
        f.write('egontest
    ')
    上下文管理打开文件

      __call__在对象后面加括号,触发执行。

    class People:
        def __init__(self,name):
            self.name=name
        #
        def __call__(self, *args, **kwargs):
            print('call')
        #
    
    p=People('egon')
    print(callable(People))
    print(callable(p))
    
    p()#对象加()去找自己类里面的__call__方法,没有这个方法就不能实例化。有这个对象就可以实例化了
    __call__

    元类

       类也是对象

      python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,可以:

      把类赋值给一个变量

      把类作为函数参数进行传递

      把类作为函数的返回值

      在运行时动态地创建类 

      上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

    class Foo:
        pass
    f1=Foo()
    print(type(f1)) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
    print(type(Foo)) # 输出:<type 'type'> 

      什么是元类?

      元类是类的类,是类的模板。

      元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为,元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)。type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象。

      创建类的两种方式:

      1.使用class关键字创建,我们一般就是这么创建类的,所以就不多说了。

      2.手动模拟class创建类的过程,这需要将创建类的步骤拆开手动完成。

      接下来我们就自己手动创建一个类。

      在此之前要明确创建类主要分为三个部分的创建,类名,类的父类,类体。

      第一步,先处理类体->名称空间,类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典。

       第二步调用元类type(也可以自定义)来产生类,

      type 接收三个参数:

      第 1 个参数是字符串 ‘Foo’,表示类名,

      第 2 个参数是元组 (object, ),表示所有的父类,

      第 3 个参数是字典。

    class Foo:
        x=1
        def run(self):
            pass
    print(type(Foo))
    
    
    
    #type成为元类,是所有类的类,利用type模拟class关键字的创建类的过程
    def run(self):
        print('%s is runing' %self.name)
    
    class_name='Bar'#类名
    bases=(object,)#继承
    class_dic={
        'x':1,
        'run':run
    }#名称空间
    
    Bar=type(class_name,bases,class_dic)#type实例化一个类
    print(Bar)
    print(type(Bar))
    type创建类

      自定制元类

      就是自己定制一个MYtype类继承type的属性。

    class Mymeta(type):
         def __init__(self,class_name,class_bases,class_dic):
                pass
         def __call__(self, *args, **kwargs):#f=Foo('egon')
            # print(self)self是foo
            obj=self.__new__(self)#类加()自动调init?不存在的是他调的
            self.__init__(obj,*args,**kwargs) #obj.name='egon'self是foo所以自动调用foo的构造方法
            return obj#foo产生的空对象完成过初始化
    class Foo(metaclass=Mymeta):
        x=1
        def __init__(self,name):#不能有返回值,返回就与上面的返回值冲突了
            self.name=name #obj.name='egon'
        def run(self):
            'run function'
            print('running')
    # print(Foo.__dict__)
    
    f=Foo('egon')
    
    print(f)
    
    print(f.name)
    自定制type元类
    # 道生一:传入type
    class SayMetaClass(type):
    
        # 传入三大永恒命题:类名称、父类、属性
        def __new__(cls, name, bases, attrs):
            # 创造“天赋”
            attrs['say_'+name] = lambda self,value,saying=name: print(saying+','+value+'!')
            # 传承三大永恒命题:类名称、父类、属性
            return type.__new__(cls, name, bases, attrs)
    
    # 一生二:创建类
    class Hello(object, metaclass=SayMetaClass):
        pass
    
    # 二生三:创建实列
    hello = Hello()
    
    # 三生万物:调用实例方法
    hello.say_Hello('world!')

       两句话说清楚什么是元类

  • 相关阅读:
    JS中解析JSON。
    对不同浏览器实现图片旋转。
    FF和IE内容不透明,字体透明。
    C# 通过身份证查询出生日期
    C# v3微信 access token 过期处理的问题
    C# 微信v3退款
    codesmith生成java类
    IOS调用WCF服务,WCF服务器进行上传图片
    安装VS 2013遇到的问题,及解决方案
    接口,个人理解
  • 原文地址:https://www.cnblogs.com/Jeffding/p/7478566.html
Copyright © 2020-2023  润新知