继承:

继承是指类与类之间的关系,是一种“什么”是“什么”的关系。

继承的功能之一就是用来解决代码重用问题

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

如下代码所示:

  1. class ParentClass1:
  2. pass
  3.  
  4. class ParentClass2:
  5. pass
  6.  
  7. class SubClass1(ParentClass1): # SubClass1 是 ParentClass1的子类,ParentClass1是SubClass1的父类(基类)
  8. pass
  9.  
  10. class Subclass2(ParentClass1,ParentClass2): # SubClass继承了 ParentClass1和ParentClass1两个父类
  11. pass

  12. # 查看继承
  13. """ 可利用 .__bases__ 的方式查看其基类(父类)(元祖的形式) """
  14. print(SubClass1.__bases__)
  15. print(Subclass2.__bases__)
  16.  
  17. # 打印结果:
  18. # (<class '__main__.ParentClass1'>,)
  19. # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

抽象与继承(先抽象后继承):

抽象:即抽取类似或者说比较像的部分; 抽象最主要的作用是划分类别

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

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

如上一节创建两个英雄类的例子:

  1. class Garen:
  2. camp = "Demacia"
  3.  
  4. def __init__(self,nickname,attack,life):
  5. self.nickname = nickname
  6. self.attack = attack
  7. self.life = life
  8.  
  9. def attacking(self,enemy):
  10. enemy.life -= self.attack
  11.  
  12. class Riven:
  13. camp = "Noxus"
  14.  
  15. def __init__(self,nickname,attack,life):
  16. self.nickname = nickname
  17. self.attack = attack
  18. self.life = life
  19.  
  20. def attacking(self,enemy):
  21. enemy.life -= self.attack
  22.  
  23. # Garen类是英雄类, Riven类也是英雄类, 所以可以把Garen和Riven提取出一个Hero类
  24.  
  25. # 如下代码所示

对上述代码稍作修改后并提取父类,如下:

  1. class Hero:
  2. def __init__(self,nickname,attack,life):
  3. self.nickname = nickname
  4. self.attack = attack
  5. self.life = life
  6.  
  7. def attacking(self,enemy):
  8. enemy.life -= self.attack
  9.  
  10. class Garen(Hero):
  11. pass
  12.  
  13. class Riven(Hero):
  14. pass
  15.  
  16. garen = Garen("草丛伦",30,100)
  17. print(garen.nickname,garen.attack,garen.life)
  18. """ 虽然Garen这个类下面没有代码,更没有__init__函数,但是Garen 继承了Hero这个类的属性 """
    """关于属性查找优先顺序,以 garen.nickname 为例,它会先去garen的命名空间里面去找有没有 nickname,如果没有再去garen的父类(Garen类)里面去找,如果还没有,再去其父类的父类(Hero类)去找...,如果最后都就没找到就报错"""
  1. print(garen.__dict__)
  1. # 运行结果:
    # 草丛伦 30 100
    #{'nickname': '草丛伦', 'attack': 30, 'life': 100}
  1.  

所以,通过继承,子类能够重用父类的属性。

属性查找顺序:

  1. # 情况1:
  2. class Foo:
  3. def f1(self):
  4. print("from Foo.f1")
  5.  
  6. def f2(self):
  7. print("from Foo.f2")
  8. self.f1()
  9.  
  10. class Bar(Foo):
  11. def f2(self):
  12. print("from Bar.f2")
  13.  
  14. b = Bar()
  15. b.f2() # 调用f2时,首先会去b的命名空间里面找,但是,Bar中都没有__init__函数,所以b里面肯定为空;b里面没有就继续往Bar里面找
  16. print(b.__dict__)
  17.  
  18. # 打印结果:
  19. # from Bar.f2
  20. # {}
  21.  
  22. # 情况2:
  23. class Foo:
  24. def f1(self):
  25. print("from Foo.f1")
  26.  
  27. def f2(self):
  28. print("from Foo.f2")
  29. self.f1() # 哪个对象调用f2,self就是谁;在这个例子中self代表b这个对象,所以这句代码代表的含义为: b.f1()
  30.  
  31. class Bar(Foo):
  32. def f1(self):
  33. print("from Bar.f2")
  34.  
  35. b = Bar()
  36. b.f2() # 同理,程序会现在b的命名空间里面查找、调用f2;没找到再往Bar里面去找;也没找到就往Foo里面去找;在Foo中找到后执行f2,此时self.f1()变成了b.f1(),b.f1()在执行时也是先在b里面找,然后在Bar里面找,所以self.f1()执行的是Bar里面的f1,而不是Foo中的
  37.  
  38. # 执行结果:
  39. # from Foo.f2
  40. # from Bar.f2

派生:

  子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),而且,一旦重新定义类自己的属性且与父类重名,name调用新增的属性时,就以自己的为准了。

  在子类中,新建的重名的函数属性,在编辑函数内功能的时候,有可能需要重用父类中重名的那个函数的功能,应该是调用普通函数的方式,即:类名.func(),此时就与调用普通函数无异了,因此即便是self参数也要为其传值。

“指名道姓”式的调用(不依赖继承关系)

  1. class Hero:
  2. def __init__(self,nickname,attack,life):
  3. self.nickname = nickname
  4. self.attack = attack
  5. self.life = life
  6.  
  7. def attacking(self,enemy):
  8. enemy.life -= self.attack
  9.  
  10. class Riven(Hero):
  11. camp='Noxus'
  12. def __init__(self,nickname,attack,life,skin):
  13. Hero.__init__(self,nickname,attack,life) #为了减少重复代码,调用父类的__init__功能 # “指名道姓”式的调用,不依赖继承,同理需要写self
  14. self.skin=skin #新属性
  15. def attacking(self,enemy): #在自己这里定义新的attacking,不再使用父类的attacking,且不会影响父类
  16. Hero.attacking(self,enemy) #调用父类Hero中的attacking功能 # 这是“指名道姓”式的调用,这种调用不依赖于继承关系,即:没有继承关系也能调用 # 因为没有根据继承关系调用,且调用的是其他类(Hero类)中的函数,因为类在调用函数的时候需要传入self这个参数(即对象本身),所以应该写成 Hero.attack(self,enemy),即需要传self
  17. print('from riven')
  18. def fly(self): #在自己这里定义新的
  19. print('%s is flying' %self.nickname)
  20.  
  21. r1=Riven('锐雯雯',57,200,'比基尼')
  1. print(r1.__dict__)
  1. r1.fly()
  2.  
  3. print(r1.skin)
  4.  
  5. # 执行结果:
    # {'nickname': '锐雯雯', 'attack': 57, 'life': 200, 'skin': '比基尼'}
    # 锐雯雯 is flying
    # 比基尼

继承的实现原理:

经典类和新式类:

  1. 只有Python2中才分经典类和新式类,Python3中统一都是新式类

  2. 在Python2中,没有继承object类的类,以及该类的子类,都是经典类

  3. 在Python2中,继承object类的类,以及该类的子类,都是新式类

  4. 在Python3中,无论是否声明继承object,都默认继承object,即Python3中所有类均为新式类

(object类定义了所有类所共有的一些内置方法)

如果没有指定基类,Python的类会默认继承object类,object是所有Python类的基类,它提供了一些常见方法(如 __str__)的实现

继承的实现:对于你定义的每一个类,Python会计算出一个方法解析顺序(MRO),这个MRO(元祖、列表)就是一个简单的所有基类的线性顺序列表(MRO全称 method resolution order) #  object has no attribute 'mro'

继承时,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

MRO列表合并了所有的父类并遵守如下规则:

  1. 对象优先于类被查找,子类会优先于父类被查找

  2. 多个父类会根据他们在列表中的位置顺序被查找

  3. 如果对下一个类存在两个合法的选择,则选择第一个父类

Python中子类可以同时继承多个父类;如果继承了多个父类,那么属性的查找方式有两种:深度优先(经典类)和广度优先(新式类)

深度优先:

广度优先:

示例代码:

  1. class A(object):
  2. def test(self):
  3. print('from A')
  4.  
  5. class B(A):
  6. def test(self):
  7. print('from B')
  8.  
  9. class C(A):
  10. def test(self):
  11. print('from C')
  12.  
  13. class D(B):
  14. def test(self):
  15. print('from D')
  16.  
  17. class E(C):
  18. def test(self):
  19. print('from E')
  20.  
  21. class F(D,E):
  22. # def test(self):
  23. # print('from F')
  24. pass
  25. f1=F()
  26. f1.test()
  27. print(F.__mro__) # .mro() :只有新式类才有这个属性可以查看线性列表,经典类没有这个属性
  28.  
  29. # 执行结果:
  30. # from D
  31. # (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
  32.  
  33. # 新式类继承顺序:F->D->B->E->C->A
  34. # 经典类继承顺序:F->D->B->A->E->C
  35. # python3中统一都是新式类
  36. # python2中才分新式类与经典类

在子类中重用父类的方法或属性,有两种方法:

  1. “指名道姓”式的调用,不依赖继承关系(此方式上面的代码已经写过)

  2. 利用super()方式,依赖继承关系

super()方式:

  1. class Hero:
  2. def __init__(self,nickname,attack,life):
  3. self.nickname = nickname
  4. self.attack = attack
  5. self.life = life
  6.  
  7. def attacking(self,enemy):
  8. enemy.life -= self.attack
  9.  
  10. class Riven(Hero):
  11.  
  12. camp = "Noxus"
  13. def __init__(self,nickname,attack,life,skin):
  14. super(Riven,self).__init__(nickname,attack,life) # super()方法,依赖继承关系 # .__init__函数无需再写self参数
  15.  
  16. # 在Python3中可以简写为:
  17. # super().__init__(nickname,attack,life)
  18.  
  19. self.skin = skin
  20.  
  21. def attacking(self,enemy):
  22. super(Riven,self).attacking(enemy) # 这是super()方法,此方法依赖于继承关系 # super(Riven,self): super中的第一个参数传子类自己的类名,第二个写成self, 这样能够得到一个特殊的对象(其父类Hero的一个对象),这个特殊的对象能够调用Hero类中的属性;super(Riven,self).attacking(enemy): .attacking(enemy)就是前面得到的那个父类的特殊对象在调用Hero中的attacking函数,因为是对象在调用函数,所以不需要再在函数里面传入self。(或者说不是在调用函数,而是在调用绑定方法)
  23.  
  24. """
  25. super(Riven,self)是Python2的写法,在Python3中可以简写为:
  26. super().attacking(enemy)
  27. """
  28.  
  29. print('from riven')
  30. def fly(self):
  31. print('%s is flying' %self.nickname)
  32.  
  33. class Garen(Hero):
  34. camp = "Demacia"
  35.  
  36. r1 = Riven('锐雯雯', 50,80,"比基尼")
  37. print(r1.__dict__)
  38. g1 = Garen("草丛伦",30,100)
  39.  
  40. r1.attacking(g1)
  41. print(g1.life)
  42.  
  43. # 运行结果:
  44. # {'nickname': '锐雯雯', 'attack': 50, 'life': 80, 'skin': '比基尼'}
  45. # from riven
  46. #

super()的运行机制:

  # super()只会沿着基于某个类产生的MRO列表,依次往后寻找

代码示例:

  1. class A:
  2. def f1(self):
  3. print("form A")
  4. super().f1()
  5.  
  6. class B:
  7. def f1(self):
  8. print("from B")
  9.  
  10. class C(A,B):
  11. pass
  12.  
  13. # C.mro()的结果:
  14. # [<class '__main__.C'>,
  15. # <class '__main__.A'>,
  16. # <class '__main__.B'>,
  17. # <class 'object'>]
  18.  
  19. c = C()
  20. c.f1()
  21.  
  22. """
  23. 运行过程分析:c = C()会生成一个基于C类的MRO列表,super()会根据这个基于C类继承关系产生的MRO列表依次查找需要调用的属性(so super()是基于继承关系的,因为继承关系能够产生一个有顺序的MRO列表)
  24. 具体过程: 先在c这个对象中找有没有f1(),再往C这个类中找有没有f1();然后再往A中去找,找到了f1(),执行A的f1()后先打印“from A”,
  25. 然后,super().f1():由于此时程序scan MRO列表已经刚刚找完了<class '__main__.A'>,super()会接着这个基于C产生的MRO列表继续往后寻找f1()
  26. 然后再B中找到了f1(),执行B中的f1()
  27. 所以,虽然A没有继承B,但是super()只会沿着基于C的继承关系产生的MRO列表按顺序往后查询;即 super()不看它们的继承关系,只看基于谁产生的MRO列表顺序。即:只会参照C的MRO列表
  28. """
  29.  
  30. # 运行结果:
  31. # form A
  32. # from B

上述示例的另一种情况:(稍作修改)

1. A和B同时继承G:

  1. class G:
  2. def f1(self):
  3. print("from G")
  4.  
  5. class A(G):
  6. def f1(self):
  7. print("form A")
  8. super().f1()
  9.  
  10. class B(G):
  11. def f1(self):
  12. print("from B")
  13.  
  14. class C(A,B):
  15. pass
  16.  
  17. c = C()
  18. c.f1()
  19. print(C.mro())

# 虽然A继承G,但super().f1()还是执行的B中的f1函数

2. A继承S,B不继承S

  1. class S:
  2. def f1(self):
  3. print("from S")
  4.  
  5. class A(S):
  6. def f1(self):
  7. print("form A")
  8. super().f1()
  9.  
  10. class B:
  11. def f1(self):
  12. print("from B")
  13.  
  14. class C(A,B):
  15. pass
  16.  
  17. c = C()
  18. c.f1()
  19. print(C.mro())
  20.  
  21. # 运行结果:
  22. # form A
  23. # from S
  24. # [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.S'>, <class '__main__.B'>, <class 'object'>]
  25.  
  26. # 分析:新式类:当A和B没有同时继承同一个父类时,先把A进行一遍广度优先,再把B进行一遍广度优先

面向对象:继承(经典类&新式类继承原理、属性查找)、派生的更多相关文章

  1. 继承 派生 super()经典类 新式类

    '''1什么是继承? 继承一种新建类的方式,在python中支持一个儿子继承多个爹 新建的类称为子类的或者派生类 父类有可以称为基类或者超类 子类会‘遗传’父类的属性 2 为什么要用继承 减少代码冗余 ...

  2. 洗礼灵魂,修炼python(43)--巩固篇—经典类/新式类

    经典类 1.什么是经典类 就是在使用class关键词时,括号内不添加object类的就叫经典类,前面的博文里是绝对解析过的,所以你应该知道,经典类现在已经仅存在于python2了,因为python3不 ...

  3. Python 中的经典类新式类

    Python 中的经典类新式类 要知道经典类和新式类的区别,首先要掌握类的继承 类的继承的一个优点就是减少代码冗余 广度优先和深度优先,这主要是在多类继承的时候会使用到 经典类和新式类的主要区别就是类 ...

  4. 第二十天- 多继承 经典MRO 新式MRO super()

    # 多继承:# 在继承关系中.⼦类自动拥有⽗类中除私有属性外其他所有内容.python⽀持多继承.子类可拥有多⽗类. class ShenXian: # 神仙 def fei(self): print ...

  5. 面向对象 - 1.面向过程/2.面向对象/3.如何使用类/4.__init__方法/5.属性查找/6.补充知识/7.可扩展性高

    编程范式(流派): 面向对象编程,面向过程编程 各有用处!! 编程:语法+数据结构(list dict)+算法(逻辑)-----------------------------------1.面向过程 ...

  6. Python中新式类和经典类的区别,钻石继承

    1)首先,写法不一样: class A: pass class B(object): 2)在多继承中,新式类采用广度优先搜索,而旧式类是采用深度优先搜索. 3)新式类更符合OOP编程思想,统一了pyt ...

  7. 【python】-- 类的多继承、经典类、新式类

    继承知识点补充 在python还支持多继承,但是一般我们很少用,有些语言干脆就不支持多继承,有多继承,就会带来两个概念,经典类和新式类. 一.多继承 之前我们都是讲的单继承,那么什么是多继承呢?说白了 ...

  8. 24 类:组合 继承 super关键字 面向对象的三大性

    组合 组合:自定义类的对象作为另外一个类的属性 class Teacher: def __init__(self, name, age): self.name = name self.age = ag ...

  9. 面向对象【day07】:新式类和经典类(八)

    本节内容 1.概述 2.类的多继承 3.经典类VS新式类 4.总结 一.概述 在python还支持多继承,但是一般我们很少用,有些语言干脆就不支持多继承,有多继承,就会带来两个概念,经典类和新式类,下 ...

随机推荐

  1. 【转】JAVA的静态变量、静态方法、静态类

    转自:http://blog.csdn.net/zhandoushi1982/article/details/8453522/ 静态变量和静态方法都属于静态对象,它与非静态对象的差别需要做个说明. ( ...

  2. 配置Oracle监听器

    Oracle的监听和网络服务都可以在Net Manager中配置,如下图.也可以在上面的那个Net Configuration Assistant中配置,只是Net Manager比较方便些. Ora ...

  3. AJPFX关于IO流的简单总结

    IO流的分类:1.根据流的数据对象来分:高端流:所有的内存中的流都是高端流,比如:InputStreamReader  低端流:所有的外界设备中的流都是低端流,比如InputStream,Output ...

  4. php接收json格式数据(text/xml)

    在API服务中,目前流行采用json形式来交互. 给前端调用的接口输出Json数据,这个比较简单,只需要组织好数据,用json_encode($array) 转化一下,前端就得到json格式的数据. ...

  5. Node.js——render封装

    封装 挂在到res上

  6. informix数据库的日志

    看到一句sql,没见过: CREATE TABLE aaad ( chianm ), course ), score INTEGER ) IN adbs EXTENT SIZE LOCK MODE R ...

  7. codevs 1082 线段树练习 3 --分块练习

    时间限制: 3 s  空间限制: 128000 KB  题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[ ...

  8. HTTP/2 带来的新颖

    Binary Frames HTTP2 在TCP层之上 HTTP下增加了Binary Frames协议, 基于该协议HTTP Request/Response 可以被分拆成由Frame组成的Strea ...

  9. 火狐删除配置文件 会删除目录下所有文件 切记不要把配置文件建立在桌面 恢复软件:易我数据恢复向导 9.0 DiskGenius500

    火狐删除配置文件 会删除目录下所有文件 切记不要把配置文件建立在桌面 恢复软件:易我数据恢复向导 9.0  DiskGenius500 结果:由于时间比较常 恢复文件均失败了~

  10. faster rcnn一些博客

    这个是对faster 问题的一个总结 http://blog.csdn.net/u010402786/article/details/72675831?locationNum=11&fps=1 ...