• Python 基础 四 面向对象杂谈


    Python 基础  四  面向对象杂谈

    一、isinstance(obj,cls) 与issubcalss(sub,super)

    isinstance(obj,cls)检查是否obj是否是类 cls 的对象
    class Foo:
        pass
    
    f1=Foo()
    print(isinstance(f1,Foo)) #True
    
    issubclass(sub, super)检查sub类是否是 super 类的派生类
    class Foo:
        pass
    
    class Xoo(Foo):
        pass
    
    print(issubclass(Xoo,Foo)) #True

    二、__getattribute__
        在介绍__getattribute__之前是否还记得之前学过一个叫__getattr__的方法,这两者之间是否存在某种关系呢?实际上呢,还是有关系的,是个什么情况呢,就是大哥和小弟的关系,有大哥在的时候大哥上,大哥不在小弟上,或者大哥不想干了,让给了小弟,那你小弟就必须上了是吧。

    先看一下__getattr__的例子:

    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是我')
            # return self.__dict__[item]
    
    f1=Foo(10)
    print(f1.x) #d当访问的属性存在的时候是不会触发__getattr__
    #f1.xxxxxx #不存在的属性访问,触发__getattr__,若自己没有重写此方法时,就会触发系统默认的此方法,若重写了就会按你自己写的东西执行

    好了,再看一下两个都存在的触发方式及结果:

    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是getattr')
            # return self.__dict__[item]
        def __getattribute__(self, item):
            print('执行的是getattribute')
            raise AttributeError('抛出异常了')
            # raise TabError('xxxxxx')
    f1=Foo(10)
    #f1.x
    f1.xxxxxx #不存在的属性访问,触发__getattr__,若有__getattribute__就先触发此方法(不管属性是否存在),若后面有raise方法抛出异常的时候,就会再次交给__getattr__处理

    三、item系列
      看到这里是否还记得之前说过一个系列叫做:attr,这两者又是什么关系?我们先看看item系列

    class Foo:
        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):
            print('delitem')
            self.__dict__.pop(key)
    
    f1=Foo()
    #print(f1.__dict__) #对象f1的属性字典是空的————》{}
    #f1.name='egon'#---->setattr-------->f1.__dict__['name']='egon'
    #print(f1.__dict__) #{'name': 'egon'}
    f1['name']='egon'#--->setitem--------->f1.__dict__['name']='egon'
    f1['age']=18 #会触发 内部的__setitem__方法
    #
    print('===>',f1.__dict__) # ===> {}查看属性字典却没有添加进去,只是触发了内部的__setitem__方法,若要添加上,就必须对底层进行操作:  self.__dict__[key]=value
    # 这样就把属性字典添加上了 ===> {'name': 'egon', 'age': 18}
    del f1.name # 删除属性值
    print(f1.__dict__)  # {'age': 18}
    # #
    print(f1.age)
    del f1['name'] #触发__delitem__,若要真实的删除属性字典中的值就必须对底层进行操作: self.__dict__.pop(key)
    #print(f1.__dict__)
    View Code

    总结一下item系列的触发是通过字典类型的方式操作完成的,而attr方式的触发是通过对象加点(.)的方式触发的,这就是两者的最大区别,但是两者最终的效果是一样的。

    四 、 _str_ ;_reper_;__format__ 

        _str_ ;_reper_;改变对象的字符串显示方式换句话就是展示print()函数的 执行结果的:

    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __str__(self):
            return '名字是%s 年龄是%s' %(self.name,self.age)
    # #
    f1=Foo('egon',18)
    print(f1) #str(f1)--->f1.__str__() 若没有__str__函数,则执行print()就会执行提供的默认的打印内容
    #比如本实例中打印的内容为:<__main__.Foo object at 0x006166D0>,若我们在定义的时候写了__str__函数就会按照我们自己定义好的方式执行打印操作:名字是egon 年龄是18
    #
    x=str(f1)
    print(x)
    #
    y=f1.__str__()
    print(y)
    class Foo:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __str__(self):
            return '折是str'
        def __repr__(self):
            return '名字是%s 年龄是%s' %(self.name,self.age)
    
    f1=Foo('egon',19)
    #repr(f1)---->f1.__repr__()
    print(f1) #str(f1)---》f1.__str__()------>f1.__repr__()

    总结:当 str 与repr同时存在的时候执行顺序是:先找str 若没有再去找repr,若都没有就去执行默认的print()打印结果。
    __format__ 自定义方式来制定输出格式:

    format_dic={
        'ymd':'{0.year}{0.mon}{0.day}',
        'm-d-y':'{0.mon}-{0.day}-{0.year}',
        'y:m:d':'{0.year}:{0.mon}:{0.day}'
    }
    class Date:
        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day
        def __format__(self, format_spec):
            print('我执行啦')
            print('--->',format_spec)
            if not format_spec or format_spec not in format_dic:
                format_spec='ymd'
            fm=format_dic[format_spec]
            return fm.format(self)
    d1=Date(2016,12,26)
    format(d1) #d1.__format__()
    print(format(d1))
    print(format(d1,'ymd'))
    print(format(d1,'y:m:d'))
    print(format(d1,'m-d-y'))
    print(format(d1,'m-d:y'))
    print('===========>',format(d1,'asdfasdfsadfasdfasdfasdfasdfasdfasdfasd

    五 、__slots__ (省内存)
        1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

        2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)

        3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__

    class Foo:
        __slots__=['name','age']  #{'name':None,'age':None}
        # __slots__='name' #{'name':None,'age':None}
    
    f1=Foo()
    f1.name='egon'
    #print(f1.name)
    
    f1.age=18  #--->setattr----->f1.__dict__['age']=18
    #print(f1.__dict__)  #AttributeError: 'Foo' object has no attribute '__dict__' 为何会出现这个结果,这就是不让各个实例对象自己封存自己的数据属性了。
    print(Foo.__slots__) #['name', 'age']类中指存放了键的属性
    print(f1.__slots__)  # ['name', 'age']
    f1.name='egon'
    f1.age=17
    print(f1.name)
    print(f1.age)
    f1.gender='male' #Foo' object has no attribute 'gender' 在定义数据属性的时候就没有定义性别的属性,so就加不进去
    
    

    六、__doc__

    class Foo:
        '我是描述信息'
        pass
    
    print(Foo.__doc__) # 我是描述信息

    需要注意的是:此属性是不具有继承的:

    class Foo:
        '我是描述信息'
        pass
    
    class Bar(Foo):
        pass
    print(Bar.__doc__) #该属性无法继承给子类 打印结果为:None

    七、__module__和__class__
    _module__ 表示当前操作的对象在那个模块

    __class__     表示当前操作的对象的类是什么

    八、__del__

    析构方法,当对象在内存中被释放时,自动触发执行。

    class Foo:
    
        def __del__(self):
            print('执行我啦')
    
    f1=Foo()
    del f1
    print('------->')
    
    #输出结果
    执行我啦
    ------->

    九 、__call__方法
        对象后面加括号,触发执行。

        注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Foo:
    
        def __init__(self):
            print('你就是大傻逼')
    
        def __call__(self, *args, **kwargs):
    
            print('__call__')
    
    
    obj = Foo() # 执行 __init__
    obj()       # 执行 __call__

    十、用 __next__和__iter__实现面向对象的迭代器协议

    class Foo:
        def __init__(self,n):
            self.n=n
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.n == 13:
                raise StopIteration('终止了')
            self.n+=1
            return self.n
    
    # l=list('hello')
    # for i in l:
    #      print(i)
    f1=Foo(10)
    print(f1.__next__())
    print(f1.__next__())
    print(f1.__next__())
    print(f1.__next__())
    
    for i in f1:  # obj=iter(f1)------------>f1.__iter__() #此时的f1的值为最后一次迭代额值
        print(i)  #obj.__next_()

    十一、描述符(__get__,__set__,__delete__)(这个东西很高大上,不过现在的级别还是没啥卵用,就先简单的了解一下下)
        1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
        __get__():调用一个属性时,触发
        __set__():为一个属性赋值时,触发
        __delete__():采用del删除属性时,触发

        一个小小的例子:

    class Foo: #在python3中Foo是新式类,它实现了三种方法,这个类就被称作一个描述符
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            pass
        def __delete__(self, instance):
            pass

    2 描述符是干什么的:描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

    #描述符Str
    class Str:
        def __get__(self, instance, owner):
            print('Str调用')
        def __set__(self, instance, value):
            print('Str设置...')
        def __delete__(self, instance):
            print('Str删除...')
    
    #描述符Int
    class Int:
        def __get__(self, instance, owner):
            print('Int调用')
        def __set__(self, instance, value):
            print('Int设置...')
        def __delete__(self, instance):
            print('Int删除...')
    
    class People:
        name=Str()
        age=Int()
        def __init__(self,name,age): #name被Str类代理,age被Int类代理,
            self.name=name
            self.age=age
    
    #何地?:定义成另外一个类的类属性
    
    #何时?:且看下列演示
    
    p1=People('alex',18)
    
    #描述符Str的使用
    p1.name
    p1.name='egon'
    del p1.name
    
    #描述符Int的使用
    p1.age
    p1.age=18
    del p1.age
    
    #我们来瞅瞅到底发生了什么
    print(p1.__dict__)
    print(People.__dict__)
    
    #补充
    print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
    print(type(p1).__dict__ == People.__dict__)
    
    描述符应用之何时?何地?
    描述符

    3 描述符分两种
        一 数据描述符:至少实现了__get__()和__set__()

    1 class Foo:
    2     def __set__(self, instance, value):
    3         print('set')
    4     def __get__(self, instance, owner):
    5         print('get')

        二 非数据描述符:没有实现__set__()

    1 class Foo:
    2     def __get__(self, instance, owner):
    3         print('get')

    4 注意事项:
    一 描述符本身应该定义成新式类,被代理的类也应该是新式类
    二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
    三 要严格遵循该优先级,优先级由高到底分别是
    1.类属性
    2.数据描述符
    3.实例属性
    4.非数据描述符
    5.找不到的属性触发__getattr__()

    今天的主要内容就这些,看起来还不错,实践,实践,还是实践!!!

       

       

       

  • 相关阅读:
    python自动生成bean类
    CVPR2021 | SETR: 使用 Transformer 从序列到序列的角度重新思考语义分割
    经典论文系列 | 缩小Anchor-based和Anchor-free检测之间差距的方法:自适应训练样本选择
    单阶段实例分割综述
    CVPR2021提出的一些新数据集汇总
    使用 PyTorch Lightning 将深度学习管道速度提高 10 倍
    C#中使用ref和out传参的方法及区别
    读书笔记《重构 改善既有代码的设计》(第2版本)
    《大话设计模式》等读后感
    OOP、封装、继承、多态,真的懂了吗?
  • 原文地址:https://www.cnblogs.com/haishiniu123/p/6802647.html
Copyright © 2020-2023  润新知