一. 鸭子类型和多态
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__合起来并进行了一系列操作