继承

 

面向对象的三大特性:继承、多态、封装。

一个类:可以被多个类继承;
一个类,可以继承多个类,这是python独有

什么是继承

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

 

class A:pass #被称为父类、基类、超类
class B:pass
class A_son(A,B):pass#被称为子类或者派生类

class AB_son(A,B):pass#注意前几节课的时候,讲过在定义类的时候都不带括号,这里可以明白,加括号以后就是父类


print(A_son.__bases__)#查找A_son的继承关系
print(AB_son.__bases__)#查找AB_son的继承关系
print(A.__bases__)#由于A在当前页面中它是父类,所以求他自身的父类就是对象'object',也就是说'object'是所有类的父类,当然这个结果只在python3中有

#所以开头的class A:就等价于class A(object),只不过object被省略

继承与抽象(先抽象再继承)

抽象

抽象即抽取类似或者说比较像的部分。

抽象分成两个层次: 

1.将奥巴马和梅西这俩对象比较像的部分抽取成类; 

2.将人,猪,狗这三个类比较像的部分抽取成父类。

抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)

可以形象的理解为:抽象就是将具体的事物不断的抽象成一个一个的大类,一个一个的大类又可以被抽象成其他的大类,这个过程就是抽象。

继承:

继承是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。

可以形象的理解为:将一个大类“实例化”多个小类的过程就是继承,而将类实例化成具体的事物的过程就是真实的实例化。

那么继承到底有什么作用呢?

以人狗大战的代码为例,下面是对人和狗代码初始化的代码

class Person:
    def __init__(self,name,blood,agger,sex):
        self.name=name
        self.blood = blood
        self.agger = agger
        self.sex = sex
    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))


class Dog:
    def __init__(self,name,blood,agger,sex):
        self.name = name
        self.blood = blood
        self.agger = agger
        self.sex = sex

如果仔细观察,会发现任何狗的代码里面有重复的地方,可否将这些重复的地方合起来,减少代码量?答案是显而易见的

class Animal:#定义一个父类,将子类中所有重复的代码可以放在这里,然后下面如果使用再进行调用
    def __init__(self,name,blood,agger,sex):
        self.name=name
        self.blood = blood
        self.agger = agger
        self.sex = sex

class Person(Animal):#通过继承的方式,继承父类的相关方法
    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))

class Dog(Animal):

显而易见,通过继承来写代码,代码的数量明显减少。使整个页面简洁。

单继承

class Animal:
    def __init__(self):
        print('执行Animal.__init__')
        self.func()
    def eat(self):
        print('%s eating'%self.name)
    def drink(self):
        print('%s drinking'%self.name)

    def func(self):
        print('Animal.func')

class Dog(Animal):
    def gurad(self):
        print('gurading')
    def func(self):
        print('Dog.func')#当运行代码的时候执行的使这里的def func(self),而非父类Animal中的def func(self)。主要过程可这么理解,首先当执行dog这个类的时候,自身没有 __init__(self),
        #那么就去他的父类Animal中找,且父类中的self也是Dog这个类中的self,所以self.func()中的self也是Dog中的self,所以结果是'Dog.func'而非'Animal.func'

dog=Dog()

class Bird(Animal):
    def __init__(self,name):
        self.name = name
    def lay(self):
        print('laying')

进阶,继续以人狗大战的代码为例

class Animal:
    def __init__(self,name,blood,agger):
        self.name=name
        self.blood = blood
        self.agger = agger

class Person(Animal):
    def __init__(self,name,blood,agger,sex):
        self.sex = sex

    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))

class Dog(Animal):
    def __init__(self,name,blood,agger,kind):#由于Dog类中已经有了__init__初始化,那么即使有了父类,也不会去父类中在进行初始化,所以当对Dog实例化以后,执行JACK.name,代码报错
        self.kind = kind

JACK = Dog('jack', 1000, 100, 'Siberian husky')
print(JACK.name)#由于再Dog

进一步修改

class Animal:
    def __init__(self,name,blood,agger):
        self.name=name
        self.blood = blood
        self.agger = agger

    def eat(self):
        print('吃药回血')
        self.blood+=100

class Person(Animal):
    def __init__(self,name,blood,agger,sex):
        Animal.__init__(self, name, blood, agger)
        self.sex = sex

    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))

class Dog(Animal):
    def __init__(self,name,blood,agger,kind):#由于Dog类中已经有了__init__初始化,那么即使有了父类,也不会去父类中在进行初始化,所以当对Dog实例化以后,执行JACK.name,代码报错
        Animal.__init__(self,name,blood,agger)#这里调用 Animal的__init__属性,同时也直接将Dog的self传给Animal,这样在调用Animal类中的属性的时候,就直接调用的是Dog的self
        self.kind = kind#这个属性是dog独有的属性,所以也叫做派生属性

    def eat(self):#在Dog类中也定义一个eat方法,这个方法执行后,表示狗会长两颗牙。由于父类中也有eat方法,子类中这里也有,所以要想同时执行这两个eat方法,就需要
         Animal.eat(self)
         self.teeth=2

JACK = Dog('jack', 1000, 100, 'Siberian husky')
Alex=Person('alex', 1000, 100, 'Siberian husky')
print(JACK.name)
JACK.eat()
print(JACK.blood)#调用Animal父类中的eat共用方法增加了JACK的血量
print(JACK.teeth)

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py
jack
吃药回血
1100
2

Process finished with exit code 0

 super

super是另外一种来在子类中调用父类的方法

在子类内部调用

class Animal:
    def __init__(self,name,blood,agger):
        self.name=name
        self.blood = blood
        self.agger = agger

    def eat(self):
        print('吃药回血')
        self.blood+=100

class Person(Animal):
    def __init__(self,name,blood,agger,sex):
        # Animal.__init__(self,name, blood, agger)#等价于下面这段代码
        super().__init__( name, blood, agger)#注意这里本来是super(Dog,self)但是Dog和self被省略;其次super只能在新式类中有用,python3中所有的类都是新式类
        self.sex = sex

    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))

Alex=Person('alex', 1000, 100, 'Siberian husky')
print(Alex.name)

在子类外部调用

class Animal:
    def __init__(self,name,blood,agger):
        self.name=name
        self.blood = blood
        self.agger = agger

    def eat(self):
        print('吃药回血')
        self.blood+=100

class Person(Animal):
    def __init__(self,name,blood,agger,sex):
        # Animal.__init__(self,name, blood, agger)#等价于下面这段代码
        super().__init__( name, blood, agger)#注意这里本来是super(Dog,self)但是Dog和self被省略;其次super只能在新式类中有用,python3中所有的类都是新式类
        self.sex = sex

    def attack(self,Dog):
        Dog.blood=Dog.blood-self.agger
        print('%s被咬了,掉了%s点血'%(self.name,Dog.agger))

    def eat(self):print('eating')


Alex=Person('alex', 1000, 100, 'Siberian husky')
print(Alex.name)
super(Person,Alex).eat()#这里是super在类的外部使用,执行的是父类中的eat函数,必须要要传递类名和对象名
Alex.eat()#这里执行的是子类中的eat函数

正常的代码中,单继承是减少了代码的重复
继承表达的是一种子类和父类的关系

多继承:

导引

class A:
    def func(self):print('A')
class B:
    def func(self):print('B')
class C:
    def func(self):print('C')
class D(A,B,C):
    def func(self):print('D')

d=D()
d.func()

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py
D

Process finished with exit code 0

如上图中代码所示,当第一次执行d.func()的时候,结果是D,但是如果注释def func(self):print('D'),ze

class A:
    def func(self):print('A')
class B:
    def func(self):print('B')
class C:
    def func(self):print('C')
class D(A,B,C):pass
    # def func(self):print('D')


d=D()
d.func()

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py
A

Process finished with exit code 0

结果是A.结论,当多继承的时候,如果当前类中没有,则从其括号中从左到右开始执行。

钻石继承问题:很经典

class A:
    def func(self):print('A')
class B(A):
    def func(self):print('B')
class C(A):
    def func(self):print('C')
class D(B,C):
    def func(self):print('D')

d=D()
d.func()

代码的结果首先是执行D内部的函数,其次再依次是B、C中的函数,最后是A中的函数

原理:广度优先算法

mro()函数

class A:
    def func(self):print('A')
class B(A):
    def func(self):print('B')
class C(A):
    def func(self):print('C')
class D(B,C):
    def func(self):print('D')

d=D()
d.func()
print(D.mro())#能够形象的描述出继承执行的顺序

D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py
D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]


多继承中,我们子类的对象调用方法,默认是就近原则,找的顺序是广度优先
新式类中执行顺序:广优先
经典类执行顺序:深度优先
python2.7中,新式类和经典类共存,新式类要继承object
python3只有新式类,默认继承object
经典类和新式类还有一个区别,mro方法只在新式类中存在
super 只在python3中存在,其本质不是直接找父类,而是根据调用者的节点位置的广度优先顺序来的

class A:
    def func(self):
        print('A')
class B(A):
    def func(self):
        super().func()
        print('B')
class C(A):
    def func(self):
        super().func()
        print('C')
class D(B,C):
    def func(self):
        super().func()
        print('D')

d=D()
d.func()
print(D.mro())#能够形象的描述出继承执行的顺序


D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/3.二分查找算法.py
A
C
B
D
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

Process finished with exit code 0

注意上面super的执行结果

 总结

继承:描述的是什么是什么的关系;
单继承:
先抽象再继承,几个类之间相同的代码抽象出来,成为父类,
子类自己没有的名字,就可以使用复利的方法和属性,
如果子类自己有,一定先用自己的,
在类中使用self的时候一定要看清楚self指向谁
多继承:
新式类和经典类:
新式类和经典类在python中本身存在,只是在继承这里的区分比较明显而已。
多继承寻找名字的顺序:新式类广度优先,经典类深度优先;
python3中有一个super方法,根据广度优先的继承顺序查找上一个类

posted @ 2019-03-07 22:22  舒畅123  阅读(120)  评论(0)    收藏  举报