继承
继承就是一种新建类的方式, 新建出来的类我们称为是'子类或者叫派生类',被继承的类我们称为是'父类或者是基类'
那么我们为什么要用继承呢?
首先我们知道类解决的是对象与对象之间的代码冗余问题,那么继承就是解决类与类之间的代码冗余问题。
怎么使用继承呢?
在python2中区分经典类和新式类,而在python3中所有类都是新式类,也就是说在python3中默认的类都是继承了object类、
经典类:没有继承object类的子子孙孙类
新式类:继承了object类的子子孙孙类
class ParentClass1: #定义父类
pass
class ParentClass2: #定义父类
pass
class SubClass1(ParentClass1): #单继承
pass
class SubClass2(ParentClass1,ParentClass2): #多继承
pass
通过类的内置属性__bases__可以查看类继承的所有父类
SubClass2.__bases__
# (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
属性查找
单继承下的属性查找
先从对象自己名称空间中查找,然后去产生这个对象的类中查找,最后去继承的父类中查找
>>> class Foo:
... def f1(self):
... print('Foo.f1')
... def f2(self):
... print('Foo.f2')
... self.f1()
...
>>> class Bar(Foo):
... def f1(self):
... print('Foo.f1')
...
>>> b=Bar()
>>> b.f2()
Foo.f2
Foo.f1
b.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Foo.f1
父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的
>>> class Foo:
... def __f1(self): # 变形为_Foo__fa
... print('Foo.f1')
... def f2(self):
... print('Foo.f2')
... self.__f1() # 变形为self._Foo__fa,因而只会调用自己所在的类中的方法
...
>>> class Bar(Foo):
... def __f1(self): # 变形为_Bar__f1
... print('Foo.f1')
...
>>>
>>> b=Bar()
>>> b.f2() #在父类中找到f2方法,进而调用b._Foo__f1()方法,同样是在父类中找到该方法
Foo.f2
Foo.f1
多继承下的属性查找
多继承下分菱形查找和非菱形查找
菱形查找分 经典类 和 新式类
经典类:按照深度优先的查找顺序
新式类:按照广度优先查找
经典类查找
class G: # 在python2中,未继承object的类及其子类,都是经典类
def test(self):
print('from G')
class E(G):
def test(self):
print('from E')
class F(G):
def test(self):
print('from F')
class B(E):
def test(self):
print('from B')
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B,C,D):
# def test(self):
# print('from A')
pass
obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
# 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试
新式类查找
class G(object):
def test(self):
print('from G')
class E(G):
def test(self):
print('from E')
class F(G):
def test(self):
print('from F')
class B(E):
def test(self):
print('from B')
class C(F):
def test(self):
print('from C')
class D(G):
def test(self):
print('from D')
class A(B,C,D):
# def test(self):
# print('from A')
pass
obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object
# 可依次注释上述类中的方法test来进行验证
"""python3中都是新式类,都是广度优先查询"""
super的使用
#A没有继承B,但是A内super会基于C.mro()继续往后找
class A:
def test(self):
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass
c=C()
c.test() #打印结果:from B
mro列表是通过一个C3算法得出来的,我们无需明白底层原理,只需要知道每个类的mro列表到底是什么,然后按照这个列表去查找就行
多态和鸭子类型
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪
class Animal: # 同一类事物:动物
def talk(self):
pass
class Cat(Animal): # 动物的形态之一:猫
def talk(self):
print('喵喵喵')
class Dog(Animal): # 动物的形态之二:狗
def talk(self):
print('汪汪汪')
class Pig(Animal): # 动物的形态之三:猪
def talk(self):
print('哼哼哼')
# 实例化得到三个对象
>>> cat=Cat()
>>> dog=Dog()
>>> pig=Pig()
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有talk方法,于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
>>> cat.talk()
喵喵喵
>>> dog.talk()
汪汪汪
>>> pig.talk()
哼哼哼
多态带来的特性:在不考虑对象类型的情况下,直接调用对象的方法或者属性
>>> def Talk(animal):
... animal.talk()
...
>>> Talk(cat)
喵喵喵
>>> Talk(dog)
汪汪汪
>>> Talk(pig)
哼哼哼
面试题:请举出Python中使用多态的例子:len
def len(obj):
return obj.__len__
len('helloworld')
len([1,2,3,4])
len((1,2,3,4))
综上我们得知,多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
Python术语表中的鸭子类型 duck-typing :
鸭子类型是一种编程风格,决定一个对象是否有正确的接口,关注点在于它的方法或属性,而不是它的类型(“如果它看起来像鸭子,像鸭子一样嘎嘎叫,那么它一定是鸭子。”)。通过强调接口而不是特定类型,设计良好的代码通过多态提高了灵活性。鸭子类型无需使用 type() 或 isinstance() 进行检查(注意,鸭子类型可以用抽象基类来补充),相反,它通常使用 hasattr() 来检查,或是 EAFP 编程。
# 二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: # Txt类有两个与文件类型同名的方法,即read和write
def read(self):
pass
def write(self):
pass
class Disk: # Disk类也有两个与文件类型同名的方法:read和write
def read(self):
pass
def write(self):
pass