zoukankan      html  css  js  c++  java
  • Python深入类和对象

    一. 鸭子类型和多态

      1.什么是鸭子类型:

        在程序设计中,鸭子类型(英语:Duck typing)是动态类型和某些静态语言的一种对象推断风格。"鸭子类型"像多态一样工作,但是没有继承。“鸭子类型”的语言是这么推断的:一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就可以被当做鸭子。也就是说,它不关注对象的类型,而是关注对象具有的行为(方法)。

        可以看出,Cat,Dog,Duck中有相同的方法say(),当有一个函数调用Duck类时并调用say()方法,我们传入Cat类和Dog类也行,函数并不会检查对象是不是Duck,而是只要你有这样的方法就能运行。

      

    如,列表的extend()方法只要参数是一个可迭代的对象就可以(list,set,tuple)

        还有前面的例子,只要实现了类中的__getitem__()魔法函数,就可以把类当作一个collection,实现啊__iter__和__next__就可以当作一个iterator。python中的鸭子类型允许我们使用任何提供所需方法的对象,而不需要迫使它成为一个子类。

      2.多态:

        由于python属于动态语言,当你定义了一个基类和基类中的方法,并编写几个继承该基类的子类时,由于python在定义变量时不指定变量的类型,而是由解释器根据变量内容推断变量类型的(也就是说变量的类型取决于所关联的对象),这就使得python的多态不像是c++或java中那样,定义一个基类类型变量而隐藏了具体子类的细节。

    二. 抽象基类(abc模块)

      1.在某些情况下判断某个对象的类型:

        

      2.强制某个子类必须实现某些方法:

        

      3.模拟抽象基类:

         3.1利用内置抛错模拟:(但只有调用某些方法时才会抛异常)

          

     1 class CacheBase():
     2     def get(self,key):
     3         #默认抛出异常(Python内置错误)
     4         raise NotImplementedError
     5     def set(self,key,value):
     6         raise NotImplementedError
     7 #继承重写就不会抛异常
     8 class Rediscatche(CacheBase):
     9     def get(self,key):
    10         pass
    11     def set(self,key,value):
    12         pass
    13 cachebase=Rediscatche()
    14 cachebase.set("key","value")

        3.2利用内置的abc模块:

     

        3.3通用的抽象基类(collections.abc模块,推荐使用多继承mixin,以防抽象基类设计过度):

          有可遍历,可哈希的等等抽象基类

     

            这些抽象基类都有一个魔法函数__subclasshook__():

          作用:Comp()没有继承Sized,但是却能判断出是Sized类型。

             __subclasshook__()会判断传入的C是否有“__len__”这个方法,有就返回为True

    三. 使用isintance而不是type

       isinstance内部会去检查它的继承链,就可以判断它是A的类型,而type是指向B那个对象,判断是否和B是同一个对象。尽量应使用isinstance,而不是type,以免误判。

      

       is和==:

          is是判断两者是不是一个对象(即id是否相同),而==是判断值是否相同。如type(b)指向的是B这个对象,虽然B继承于A,但是A和B是两个不同的对象。         

    四. 类变量和对象变量

      注:1.魔法函数__init__中self是实例化对象,中的参数是对象变量,在实例化后调用变量是向上查找(即先查找对象变量,后查找类变量),类变量可以直接通过类访问;

        2.类变量是所有实例共享的

    通过类修改类变量

    通过实例对象修改变量


    五. 类属性和实例属性以及查找顺序

      1.向上查找,即先查找对象变量(实例属性),后查找类属性:

      2.多继承采用MRO(【Method Resolution Order】:方法解析顺序)算法:

        Python语言包含了很多优秀的特性,其中多重继承就是其中之一,但是多重继承会引发很多问题,比如二义性,Python中一切皆引用,这使得他不会像C++一样使用虚基类处理基类对象重复的问题,但是如果父类存在同名函数的时候还是会产生二义性,Python中处理这种问题的方法就是MRO。

    DFS:深度优先算法,这样查询顺序为A->B->D->C->E

     这样就会出现问题(菱形继承):如果C继承D覆盖D中的某方法,在调用时是先查询D,然后查询C,则查询的方法是D中的,而不是C中重写的,因此在Python2

       后改成了广度优先的算法。

    广度优先算法,这就解决了菱形继承,但是在第一种又出现了问题

    如D和C中如果有个同名的方法,则会调用C中的方法,而不是D中的,而B是继承D的,因此从Python2.3后都统一为C3算法

      3.C3算法(参考:https://www.cnblogs.com/LLBFWH/p/10009064.html):  

        求某一类在多继承中的继承顺序:
        类的mro == [类] + [父类的继承顺序] + [父类2的继承顺序]
        如果从左到右的第一个类在后面的顺序中出现,那么就提取出来到mro顺序中
        [ABCD] + [EO] --> A = [BCD] + [EO]
        如果从左到右的第一个类在后面的顺序中出现,且在后面的顺序中也是第一位,那么就提出来到mro顺序中
        [ABCD] + [AEO] --> A = [BCD] + [EO]
        如果从左到右的第一个类在后面的顺序中出现,但不是在第一位,那么应该继续往后找,找到符合规则的项目
        [ABCD] + [EAO] --> E = [ABCD] + [AO]
        [ABCD] + [EAO] + [GEO] --> G = [ABCD] + [EAO] + [EO]
        [ABCD] + [EAO] + [EO] --> GE = [ABCD] + [AO] + [O]
        关键结论:
            这个类没有发生继承,他的顺序永远是[类o]
            只要是单继承,不是多继承,那么mro顺序就是从子类到父类的顺序

      4.查找顺序:

        4.1菱形继承:(Python2.3以前为经典类,默认不继承object(D),而2.3以后为新式类,默认继承object,即最后查找object类)

        

        4.2分别继承:

        

    六. 静态方法、类方法以及对象方法

      1.实例方法:self为实例对象

      

      2.静态方法:(相当于普通的函数)

        (注:采用硬编码,如果类名改变,相应的静态方法中也要改变,如下面的Date改变,则parse_from_string中Date也相应改变)

        

    利用外部对参数处理传入(每次都需要处理,麻烦)

     

    利用静态方法

        静态方法用处:如在判断传入的参数是否为合法字符串,这是不用返回类对象,因此不用传入类(类方法)

          

      3.类方法:(传递的是类cls)

        注:相比静态方法,不是采用硬编码,无论类名称是什么,都不用修改类方法,且传递的是类(cls,只是名称,可以修改)

         

    七. 数据封装和私有属性

      1.私有属性:

        

    无法实例或类直接访问私有属性,只有通过类中的公共方法get_age间接访问

      2.私有属性原理:

        把具有双下划线的属性(如__birthday变为[_classname__attr]即_User__birthday),因此不是从语言层面解决了绝对私有性,只是加了一些小技巧。主要只是让我们书写更加规范,没有绝对的安全,也可以解决同样的变量名冲突的问题。如另一个类继承User,且也有__birthday,则根据规则是不一样的

        

    仍然能访问

    八. python对象的自省机制

      1.概念:

        自省是通过一定的机制查询到对象的内部结构

      2.__dict__,dir的使用:

        2.1通过dict查找属性:

       

    实例的属性,但是通过name属性却能查找到(向上查找,name属性User类这个对象)

     

    类属性,含有模板,文档,属性,弱引用等

         2.2通过__dict__添加修改属性:

          

         2.3通过dir查找属性(会列出所有属性,比__dict__更加详细): 

      

    只有属性名称,没有属性值,还可以对list等使用

    九. super函数

      

      1.如果想调用A中的构造函数:

          Python2:super(B,self).__init__()

              

          Python3中:super().__init__()

      2.既然重写A的构造函数,为什么还要调用super:

        很好的重用代码,如某个参数需要父类的构造函数处理,就可以调用super函数把参数交给父类的构造函数处理

        

    将name交给Thread的构造函数处理

      3.super执行顺序: 

        super并不是直接调用父类,而是根据MRO算法的调用顺序(因此先是C,然后是A)

    十. django rest framework中对多继承使用的经验

      1.建议:

        尽量不要使用多继承,以免造成混乱

      2.mixin多继承案例(如django restframework中的mixins):

        1.mixin类功能单一;

        2.不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化成功;

        3.在mixin中不要使用super函数;

        4.尽量以Mixin结尾

    十一.python中的with语句

      1.try...except语句:

        except语句中将2压入堆栈中,finally又将4压入堆栈中,所以在取数据时直接从栈顶取数据,因此是4,如果没有finally则是前面的(如果要操作数据库,文件等,就需要在try中,except,finally中书写关闭连接,文件的逻辑)。

      2.上下文管理器:

        上下文管理器协议(需要实现两个魔法函数__enter__和__exit__):

          需要在__enter__中获取资源,在__exit__释放资源,只要满足这个协议就可以用with语句使用

           

                                      

    十二. contextlib实现上下文管理器

        相当于简化__enter__和__exit__:@contextlib.contextmanager装饰器将__enter__和__exit__合起来并进行了一系列操作

    十三.参考文献:

      MRO算法介绍

  • 相关阅读:
    day9习题
    生产者消费者模型(吃包子例子)
    map 函数----filter函数
    #返回值包含函数
    #把函数当作参数传给另一个函数
    异常和错误!
    递归调用
    局部和全局案例!!
    全局变量与局部变量2
    全局变量与局部变量
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/10310174.html
Copyright © 2011-2022 走看看