03 面向对象之:继承

一:什么是面向对象的继承?

比较官方的说法就是:

继承(英语:inheritance)是面向对象软件技术当中的一个概念。如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类别追加新的属性和方法也是常见的做法。 一般静态的面向对象编程语言,继承属于静态的,意即在子类别的行为在编译期就已经决定,无法在执行期扩充。

字面意思就是:子承父业,合法继承家产,就是如果你是独生子,而且你也很孝顺,不出意外,你会继承你父母所有家产,他们的所有财产都会由你使用(败家子儿除外)。

那么用一个例子来看一下继承:

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

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

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

# 继承的用法:
class Aniaml(object):
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex

class Person(Aniaml):
    pass

class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass

继承的优点也是显而易见的:

1、增加了类的耦合性(耦合性不宜多,宜精)。

2、减少了重复代码

3、使得代码更加规范化,合理化

二、继承的分类

就像上面的例子:

Aminal叫做父类,基类,超类

Person,Cat,Dog:子类,派生类

继承:可以分单继承,多继承

这里需要补充一下Python中类的种类(继承需要):

在Python2x版本中存在两种类:

  一个叫经典类。在Python2.2之前,一直使用的是经典类,经典类在基类的根如果什么都不写。

  一个叫新式类。在Python2.2之后出现了新式类。新式类的特点就是基类的根是object类

Python3x版本中只有一种类:

Python3中使用的都是新式类。如果基类谁都不继承,那么这个类就会默认继承object

三、单继承

3.1 类名,对象执行父类方法

class Aniaml(object):
    type_name = ‘动物类‘

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

    def eat(self):
        print(self)
        print(‘吃东西‘)

class Person(Aniaml):
    pass

class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass

# 类名:
print(Person.type_name)  # 可以调用父类的属性,方法。
Person.eat(111)
print(Person.type_name)

# 对象:
# 实例化对象
p1 = Person(‘春哥‘,‘男‘,18)
print(p1.__dict__)
# 对象执行类的父类的属性,方法。
print(p1.type_name)
p1.type_name = ‘666‘
print(p1)
p1.eat()

类名,对象分别调用父类方法

类名,对象分别调用父类方法

3.2 执行顺序

class Aniaml(object):
    type_name = ‘动物类‘
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex

    def eat(self):
        print(self)
        print(‘吃东西‘)

class Person(Aniaml):

    def eat(self):
        print(‘%s 吃饭‘%self.name)

class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass

p1 = Person(‘barry‘,‘男‘,18)
# 实例化对象时必须执行__init__方法,类中没有,从父类找,父类没有,从object类中找。
p1.eat()
# 先要执行自己类中的eat方法,自己类没有才能执行父类中的方法。

3.3 同时执行类以及父类方法

方法一:

如果想执行父类中的func方法,这个方法并且子类中也用,那么就在子类的方法中写上:

父类.func(对象,其它参数)

举例说明:

class Aniaml(object):
    type_name = ‘动物类‘
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex

    def eat(self):
        print(‘吃东西‘)

class Person(Aniaml):
    def __init__(self,name,sex,age,mind):
        ‘‘‘
        self = p1
        name = ‘春哥‘
        sex = ‘laddboy‘
        age = 18
        mind = ‘有思想‘
        ‘‘‘
        # Aniaml.__init__(self,name,sex,age)  # 方法一
        self.mind = mind

    def eat(self):
        super().eat()
        print(‘%s 吃饭‘%self.name)
class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass

# 方法一: Aniaml.__init__(self,name,sex,age)
# p1 = Person(‘春哥‘,‘laddboy‘,18,‘有思想‘)
# print(p1.__dict__)

# 对于方法一如果不理解:
# def func(self):
#     print(self)
# self = 3
# func(self)

方法二:

利用super,super().func(参数)

class Aniaml(object):
    type_name = ‘动物类‘
    def __init__(self,name,sex,age):
            self.name = name
            self.age = age
            self.sex = sex

    def eat(self):
        print(‘吃东西‘)

class Person(Aniaml):
    def __init__(self,name,sex,age,mind):
        ‘‘‘
        self = p1
        name = ‘春哥‘
        sex = ‘laddboy‘
        age = 18
        mind = ‘有思想‘
        ‘‘‘
        # super(Person,self).__init__(name,sex,age)  # 方法二
        super().__init__(name,sex,age)  # 方法二
        self.mind = mind

    def eat(self):
        super().eat()
        print(‘%s 吃饭‘%self.name)
class Cat(Aniaml):
    pass

class Dog(Aniaml):
    pass
# p1 = Person(‘春哥‘,‘laddboy‘,18,‘有思想‘)
# print(p1.__dict__)
复制代码

单继承练习题:

# 1
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)

class Foo(Base):
    pass
obj = Foo(123)
obj.func1() # 123 运?的是Base中的func1  

# 2
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
class Foo(Base):
    def func1(self):
        print("Foo. func1", self.num)
obj = Foo(123)
obj.func1() # Foo. func1 123 运?的是Foo中的func1       

# 3
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
class Foo(Base):
    def func1(self):
        print("Foo. func1", self.num)
obj = Foo(123)
obj.func1() # Foo. func1 123 运?的是Foo中的func1
# 4
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
        self.func2()
    def func2(self):
        print("Base.func2")
class Foo(Base):
    def func2(self):
    print("Foo.func2")
obj = Foo(123)
obj.func1() # 123 Foo.func2 func1是Base中的 func2是?类中的
# 再来
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
        self.func2()
    def func2(self):
        print(111, self.num)
class Foo(Base):
    def func2(self):
        print(222, self.num)
lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
    obj.func2() # 111 1 | 111 2 | 222 3

# 再来
class Base:
    def __init__(self, num):
        self.num = num
    def func1(self):
        print(self.num)
        self.func2()
    def func2(self):
        print(111, self.num)
class Foo(Base):
    def func2(self):
        print(222, self.num)
lst = [Base(1), Base(2), Foo(3)]
for obj in lst:
 obj.func1() # 那笔来吧. 好好算

四、多继承

class ShenXian: # 神仙
    def fei(self):
        print("神仙都会?")
class Monkey: # 猴
    def chitao(self):
        print("猴?喜欢吃桃?")
class SunWukong(ShenXian, Monkey): # 孙悟空是神仙, 同时也是?只猴
    pass
sxz = SunWukong() # 孙悟空
sxz.chitao() # 会吃桃?
sxz.fei() # 会?

此时, 孙悟空是?只猴?, 同时也是?个神仙. 那孙悟空继承了这两个类. 孙悟空?然就可以执?这两个类中的?法. 多继承?起来简单. 也很好理解. 但是多继承中, 存在着这样?个问题. 当两个?类中出现了重名?法的时候. 这时该怎么办呢? 这时就涉及到如何查找?类?法的这么?个问题.即MRO(method resolution order) 问题. 在python中这是?个很复杂的问题. 因为在不同的python版本中使?的是不同的算法来完成MRO的.

这里需要补充一下python中类的种类(继承需要):

在Python2x版本中存在;两种类:

  ?个叫经典类. 在python2.2之前. ?直使?的是经典类. 经典类在基类的根如果什么都不写.
  ?个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。
python3x版本中只有一种类:
python3中使?的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object

4.1 经典类的多继承

虽然在python3中已经不存在经典类了. 但是经典类的MRO最好还是学?学. 这是?种树形结构遍历的?个最直接的案例. 在python的继承体系中. 我们可以把类与类继承关系化成?个树形结构的图. 来, 上代码:

class A:
    pass
class B(A):
    pass
class C(A):
    pass
class D(B, C):
    pass
class E:
    pass
class F(D, E):
    pass
class G(F, D):
    pass
class H:
    pass
class Foo(H, G):
    pass

代码示例

对付这种mro画图就可以了

继承关系图已经有了,那如何进行查找呢?记住一个原则,在经典类中采用的是深度优先,遍历方案。什么是深度优先,就是一条路走到头然后再走回来,继续找下一个。

图中每个圈都是准备送鸡蛋的地址,箭头和黑线表示线路。那些送鸡蛋的顺序告诉你入口在下面R,并且必须从左往右送,那怎么送呢?

如图:肯定是按照123456这样的顺序来送。那这样的顺序就叫做深度优先遍历。而如果是142356呢?这种被称为广度优先遍历。好了,深度优先就说这么多i,那么上面那个图怎么找的呢?MRO是什么呢?很简单,记住从头开始,从左往右,一条路走到头,然后往回走,继续一条路走到头,就是经典类的MRO算法。

类的MRO: Foo-> H -> G -> F -> E -> D -> B -> A -> C. 你猜对了么?

4.2 新式类的多继承

4.2.1 mro序列

MRO是一个有序类别L,在类中被创建时就计算出来了。

通用计算公式为:

mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] )
(其中Child继承自Base1, Base2)

如果继承至另一个基类:class B(A)

这时的B的mro序列为:

mro( B ) = mro( B(A) )
= [B] + merge( mro(A) + [A] )
= [B] + merge( [A] + [A] )
= [B,A]

如果继承多个基类:class B(A1,A2,A3,...)

这时B的mro序列:

mro(B) = mro( B(A1, A2, A3 …) )
= [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] )
= ...

计算结果为列表,列表中至少有一个元素即类自己,如上述示例[A1,A2,A3]。merge操作是C3算法的核心。

4.2.2 表头和表尾

表头:

  列表的第一个元素

表尾:

  列表中表头以外的元素集合(可以为空)

示例:

  列表:[A,B,C]

  表头是A,表尾是B和C

4.2.3 列表之间的+操作

+操作:

[A] + [B] = [A, B]
(以下的计算中默认省略)
---------------------

merge操作示例:

如计算merge( [E,O], [C,E,F,O], [C] )
有三个列表 :  ①      ②          ③

1 merge不为空,取出第一个列表列表①的表头E,进行判断
   各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表
2 取出列表②的表头C,进行判断
   C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除
   merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] )
3 进行下一次新的merge操作 ......
--------------------- 

计算mro(A)方法:

mro(A) = mro( A(B,C) )

原式= [A] + merge( mro(B),mro(C),[B,C] )

  mro(B) = mro( B(D,E) )
         = [B] + merge( mro(D), mro(E), [D,E] )  # 多继承
         = [B] + merge( [D,O] , [E,O] , [D,E] )  # 单继承mro(D(O))=[D,O]
         = [B,D] + merge( [O] , [E,O]  ,  [E] )  # 拿出并删除D
         = [B,D,E] + merge([O] ,  [O])
         = [B,D,E,O]

  mro(C) = mro( C(E,F) )
         = [C] + merge( mro(E), mro(F), [E,F] )
         = [C] + merge( [E,O] , [F,O] , [E,F] )
         = [C,E] + merge( [O] , [F,O]  ,  [F] )  # 跳过O,拿出并删除
         = [C,E,F] + merge([O] ,  [O])
         = [C,E,F,O]

原式= [A] + merge( [B,D,E,O], [C,E,F,O], [B,C])
    = [A,B] + merge( [D,E,O], [C,E,F,O],   [C])
    = [A,B,D] + merge( [E,O], [C,E,F,O],   [C])  # 跳过E
    = [A,B,D,C] + merge([E,O],  [E,F,O])
    = [A,B,D,C,E] + merge([O],    [F,O])  # 跳过O
    = [A,B,D,C,E,F] + merge([O],    [O])
    = [A,B,D,C,E,F,O]
---------------------
复制代码

结果OK. 那既然python提供了. 为什么我们还要如此?烦的计算MRO呢? 因为笔
试.......你在笔试的时候, 是没有电脑的. 所以这个算法要知道. 并且简单的计算要会. 真是项?
开发的时候很少有?这么去写代码.

这个说完了. 那C3到底怎么看更容易呢? 其实很简单. C3是把我们多个类产?的共同继
承留到最后去找. 所以. 我们也可以从图上来看到相关的规律. 这个要?家??多写多画图就
能感觉到了. 但是如果没有所谓的共同继承关系. 那?乎就当成是深度遍历就可以了

原文地址:https://www.cnblogs.com/Big-Dinosaur/p/10484742.html

时间: 2024-10-05 02:01:22

03 面向对象之:继承的相关文章

面向对象之继承

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 面向对象之继承 { class Program { static void Main(string[] args) { //记者:我是记者,我的爱好是偷拍,我的年龄34,我是一个男的 //程序员:我叫孙权 我的年龄23 我是男生 我的工作年限是3年

黑马程序员——Java基础---面向对象之继承

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流!                                                                 面向对象之继承  一:继承(extends) (1)继承的体系结构:就是对要描述的事物进行不断的向上抽取,就出现了体系结构.

javascript面向对象程序设计——继承初步(by vczero)

上一篇说了javascript的封装,没有刻意去说javascript的“共有函数”.“私有函数”,因为个人觉得这只是作用域的问题,我们不能刻意的模仿强类型语言(C++/JAVA)而去编写代码.尊重每一门语言的特性才是最为重要的. 一.基于prototype原型的继承 1 var Person = function(name, age){ 2 this.name = name; 3 this.age = age; 4 } 5 6 Person.prototype = { 7 addUser: f

C++ Primer 学习笔记_69_面向对象编程 --继承情况下的类作用域

面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好像这些成员是派生类成员一样: Bulk_item bulk; cout << bulk.book() << endl; 名字book的使用将这样确定[先派生->后基类]: 1)bulk是Bulk_item类对象,在Bulk_item类中查找,找不到名

面向对象--多继承&amp;派生类对象内存布局分析&amp;各基类指针所指向的位置分析

背景 原文链接:ordeder  http://blog.csdn.net/ordeder/article/details/25477363 关于非虚函数的成员函数的调用机制,可以参考: http://blog.csdn.net/yuanyirui/article/details/4594805 成员函数的调用涉及到面向对象语言的反射机制. 虚函数表机制可以查看下面这个blog: http://blog.csdn.net/haoel/article/details/1948051 总结为: 其一

【面向对象】继承(转载)

[面向对象]继承 1.继承 1)复用:开发只需要定义独有的功能(属性和方法),再复用曾经写过的功能(属性和方法) 2)extends:继承的关键字 格式:class 子类 extends 父类 3)子类可以继承父类的属性和方法 4)继承本质:把父类的属性和方法代码拷贝到子类中,在写子类代码时候,可以减少代码量,只需要添加新的部分 5)java中继承是单继承,但可以层层继承 单继承:子类只能有一个直接父类 案例: public class Demo2 { public static void ma

转 python语法学习面向对象之继承

传送门 python语法学习面向对象之继承 只要涉及到面向对象,”类“是必须出现的一个代名词. 类和对象是面向对象编程的两个主要方面.类创建一个新类型,而对象是这个类的实例. 类的一些概念: 包括初始化方法__init__,可以理解为构造,self,理解为this,等等在前一篇文章中介绍过,现在就学习类的继承. 继承: 面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制.继承完全可以理解成类之间的类型和子类型关系. 需要注意的地方:继承语法   class 派生类

C++ Primer 学习笔记_69_面向对象编程 -继承景况下的类作用域

面向对象编程 --继承情况下的类作用域 引言: 在继承情况下,派生类的作用域嵌套在基类作用域中:如果不能在派生类作用域中确定名字,就在外围基类作用域中查找该名字的定义. 正是这种类作用域的层次嵌套使我们能够直接访问基类的成员,就好像这些成员是派生类成员一样: Bulk_item bulk; cout << bulk.book() << endl; 名字book的使用将这样确定[先派生->后基类]: 1)bulk是Bulk_item类对象,在Bulk_item类中查找,找不到名

轻松学习JavaScript十三:JavaScript基于面向对象之继承(包含面向对象继承机制)

一面相对象继承机制 今天算是什么都没干,尽在了解面向对象三大特性之一的继承了,过去的学习的C++和C#都是正统的面向对象语 言,学习的时候也没有怎么深入了解过,只是简单的学习最基础的继承.下午在看继承机制的时候,看到一个很经典 的继承机制实例.这个实例使用UML很好的解释了继承机制. 说明继承机制最简单的方式是,利用一个经典的例子就是几何形状.实际上,几何形状只有两种,即椭圆形(是圆 形的)和多边形(具有一定数量的边).圆是椭圆的一种,它只有一个焦点.三角形.矩形和五边形都是多边形的一种, 具有