博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python深入类和对象
阅读量:5953 次
发布时间:2019-06-19

本文共 4682 字,大约阅读时间需要 15 分钟。

一. 鸭子类型和多态

  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         pass11     def set(self,key,value):12         pass13 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算法(参考:):  

    求某一类在多继承中的继承顺序:

    类的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__合起来并进行了一系列操作

 

十三.参考文献:

  

转载于:https://www.cnblogs.com/lyq-biu/p/10310174.html

你可能感兴趣的文章
前端性能优化——http首部
查看>>
FreeRTOS(一)——任务管理
查看>>
移动端网页怎么做?
查看>>
第5章 高效的多线程日志
查看>>
协议 - 收藏集 - 掘金
查看>>
Kotlin教程 - 收藏集 - 掘金
查看>>
deferred对象
查看>>
2017年3月份前端资源分享
查看>>
Node学习记录: 图片爬虫
查看>>
cookie与session的区别与联系
查看>>
黄东旭:When TiDB Meets Kubernetes
查看>>
有趣的6种图片灰度转换算法
查看>>
Spring Boot 框架介绍和使用
查看>>
使用Angular与TypeScript构建Electron应用(二)
查看>>
Reactjs不能忽略的key
查看>>
关于lazyman你还应该知道这几件事
查看>>
Gandi下配置Github pages的自定义域名
查看>>
深度学习框架不能“包治百病”,开发者如何选出最适合自己的?
查看>>
Hyperledger Composer评测
查看>>
云监控状态调查:公有云和混合云的监控成熟度落后于传统数据中心
查看>>