继承

继承用来解决类和类之间的代码冗余问题,是一种创建新类的方式,python支持多继承,即新建的类可以继承一个或多个父类,

在python3中新建一个类,如果这个类没有继承任何类,默认它会继承object类,继承了object类的类及其子类,子子类,叫新式类

object类是python解释器内置的类

属性查找

单继承:有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去对象的类中找,然后再去父类中找……

class Foo:
     def f1(self):
         print('Foo.f1')
     def f2(self):
         print('Foo.f2')
         self.f1()

class Bar(Foo):
    def f1(self):
        print('bar.f1')
 
b=Bar()
b.f2()
'''
Foo.f2
bar.f1
'''

#要访问Foo.f1
class Foo:
    def __f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        # 相当于self._Foo__f1()
        self.__f1()	


class Bar(Foo):
    # 相当于self._Bar__f1()
    def __f1(self):
        print('bar.f1')


b = Bar()
b.f2()
Foo.f2(b)	#类调用自己的方法,要正常传入参数
'''
Foo.f2
Foo.f1

Foo.f2
Foo.f1
'''

不建议使用多继承,可能会引发菱形问题,扩展性变差

多继承是非菱形继承:经典类和新式类的查找顺序一样,类和类的对象访问属性时都是参照该类的方法解析顺序MRO列表

多继承是菱形继承,经典类和新式类的查找顺序不一样,经典类是深度优先,即在第一条线中就去共同的类中查找

新式类是广度优先,即在最后一条线路才去共同的类中查找

新式类内置了mro()方法,经典类没有这个方法

preview

preview

#菱形问题
class A(object):
    def test(self):
        print('from A')


class B(A):
    def test(self):
        print('from B')


class C(A):
    def test(self):
        print('from C')


class D(C,B):
    pass

print(D.mro())
obj = D()
obj.test()

'''
[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
from C
'''
'''
1.子类会先于父类被检查
2.多个父类会根据它们在mro列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
'''
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

Mixin机制

CivilAircraft类和Helicopter类的父类只能有一个,那就是Vehicle类,这个父类有所有子类共同的东西,并且它表示一个事物,子类在继承这个类时,要将其放在括号内的最右边

FlyableMixin类的命名规范:在Flyable后面加上Mixin,这个类里面只定义一种功能(不是只定义一个函数),如果有多种功能,需要定义多个这样的类,这种功能是独立的,这种功能只是某些子类才有的,某个子类继承了这个Mixin类,就有了这个功能,

class Vehicle:  # 交通工具
    pass


class FlyableMixin:
    def fly(self):
        '''
        飞行功能相应的代码        
        '''
        print("I am flying")


class CivilAircraft(FlyableMixin, Vehicle):  # 民航飞机
    pass


class Helicopter(FlyableMixin, Vehicle):  # 直升飞机
    pass


class Car(Vehicle):  # 汽车
    pass

# ps: 采用某种规范(如命名规范)来解决具体的问题是python惯用的套路

在子类派生的新方法中重用父类的功能

在一个类的方法中重用另一个类的功能

这种调用方式不依赖继承关系,因为指明了调用哪个类的哪个方法

类名.类中的方法名

Oldboy._init_(self,name,age,sex)

class OldBoy:
    school = 'OldBoy'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


class Student(OldBoy):
    pass

#Teacher类继不继承OldBoy类,结果是一样的
class Teacher():	
    def __init__(self, name, age, sex, salary, level):
        OldBoy.__init__(self,name,age,sex)  #类调用自己的函数不会有绑定方法的效果,即不会把空对象作为参数传入
       
        self.salary = salary
        self.level = level


teacher1 = Teacher('egon', 18, 'male', 3000, 10)
print(teacher1.__dict__)
'''
{'name': 'egon', 'age': 18, 'sex': 'male', 'salary': 3000, 'level': 10}

'''

super方法依赖继承关系

调用super方法会得到一个特殊的对象,在有直接继承关系时,该对象专门用来引用父类的属性,该对象会参照发起属性查找的类(不一定是super方法所在的类)的MRO列表,去当前类(super方法所在的类,因为发起属性查找的类要重用父类的功能,找到super方法所在的类时,没有这个功能)的下一个类(因为有继承关系,下一个类其实就是父类)中查找属性,


class Oldboy:
    school='Oldboy'
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
class Student(Oldboy):
    pass
class Teacher(Oldboy):
    def __init__(self,name,age,sex,salary,level):
        # 调用super方法会得到一个特殊的对象,对象再调用方法,会自动把对象传进去,不需要写self参数
        super().__init__(name,age,sex)	#python3中的写法
        #super(Teacher,self).__init__(name,age,sex)	#python2中的写法,和上一句等价,super用来获得父类的方法
        self.salary = salary
        self.level = level
        
        
teacher1=Teacher('egon',18,'male',3000,10)
print(Teacher.mro())
print(teacher1.__dict__)
'''
[<class '__main__.Teacher'>, <class '__main__.OldBoy'>, <class 'object'>]
{'name': 'egon', 'age': 18, 'sex': 'male', 'salary': 3000, 'level': 10}
'''

super()是依赖于继承的,即使没有直接继承关系,super()仍然会按照MRO列表继续往后查找(以下例子)

调用super方法会得到一个特殊的对象,在没有直接继承关系时,该对象专门用来引用别类的属性,该对象会参照发起属性查找的类(不一定是super方法所在的类)的MRO列表,去当前类(super方法所在的类,因为发起属性查找类要重用别的类的功能,找到super方法所在的类时,没有这个功能)的下一个类中查找属性,

# A没有继承B
class A:
    def test(self):
        super().test()


class B:
    def test(self):
        print('from B')


class C(A,B):
    pass


print(C.mro())
print(A.mro())
obj=C()		
obj.test()

'''
# 发起属性查找的类是C
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
[<class '__main__.A'>, <class 'object'>]
from B
'''

组合

在一个类中以另外一个类的对象作为数据属性,称为类的组合

多态

多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象

#鸭子类型
#二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
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

#把对象的使用方法统一成一种,即定义一个统一的接口来使用
def read(obj):	
    obj.read()


def write(obj):
    obj.write()


txt1 = Txt()
disk = Disk()

read(txt1)
write(txt1)

read(disk)
write(disk)