python多继承super,python继承super__init__

  python多继承super,python继承super__init__

  本文主要介绍了python语法的super()学习、继承和派生。继承是创建新类的一种方式。具体super()的推导细节,请参考下面的文章。

  00-1010 1什么是继承?2继承规则3继承原则4多继承属性查询序列5搜索过程属性搜索6继承概念的实现7私有属性私有方法在继承中的表现8派生类的覆盖9属性(派生属性)10父属性(方法)的重用11继承派生机制的功能12Super()

  

目录

  继承是一种创建新类的方式;

  在Python中,新创建的类可以继承一个或多个父类。新创建的类可以称为子类或派生类,父类可以称为基类或超类。

  继承可以用来解决类之间的代码重用问题;

  Classparentclass13360 #定义父类

  及格

  Classparentclass 23360 #定义父类

  及格

  class subclass 1(parent class 1): #单一继承

  及格

  Classsubclass 2(父类1,父类2) : #多重继承

  及格

  注意:

  在python中,一个子类可以继承多个父类;在其他语言中,一个子类只能继承一个父类;

  python中的继承分为单继承和多继承;

  通过类的内置属性__bases__可以查看该类继承的所有父类。

  子类2。_ _ bases _ _

  (class __main__.ParentClass1 ,class __main__.ParentClass2 )

  在Python3中,只有一个新类。即使没有显式继承object,默认情况下也会继承这个类。

  

1 什么是继承?

  子类继承父类的成员变量和成员方法:

  子类不继承父类的构造函数,但可以继承父类的析构函数。子类不能删除父类的成员,但是可以重定义父类的成员。子类可以增加自己的成员示例:.

  #在Python中,子类继承父类成员变量之间的值逻辑。

  类人():

  def __init__(本人,姓名,年龄,性别):

  self.name=jasn

  self.age=18

  自我.性=性

  定义对话(自我):

  打印(‘我想对你说点什么!)

  汉语班(人):

  def __init__(本人,姓名,年龄,性别,语言):

  人。_ _ init _ _ (self,name,age,sex) #用父类的名称、年龄、性别覆盖子类的属性。

  Self.age=age #覆盖父类的年龄,值是传递给子类实例的年龄参数。

  self.language=chinese

  定义对话(自我):

  打印(‘我说普通话!)

  Person.talk(自我)

  obj=Ch

  inese("nancy",18,male,"普通话")

  print(obj.name) # 对应场景A

  print(obj.age) # 对应场景B

  print(obj.language) # 对应场景C

  obj.talk() # 对应场景D

  # 总结:

  # A:若父类中初始化了成员变量,子类调用父类构造方法未覆盖属性(self.name),则调用子类属性时取值为父类中初始化的成员变量;

  # B:若父类中初始化了成员变量,若子类调用父类构造方法覆盖属性(self.age)则取值为子类实例中传入参数

  # C:若父类未初始化该成员变量,则无论子类中有无进行对父类构造方法进行属性的覆盖,均取子类实例中传入的参数

  # D:对于方法,如果子类有这个方法则直接调用,如果子类没有则去父类查找。父类没有则报错

  

  

3继承原理

  对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。

  

>>> D.mro() # 新式类内置了mro方法可以查看线性列表的内容,经典类没有该内置方法

  [<class __main__.D>, <class __main__.B>, <class __main__.C>, <class __main__.A>, <class object>]

  python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止,而这个MRO列表的构造是通过一个C3线性化算法来实现的。

  实际上就是合并所有父类的MRO列表并遵循如下三条准则:

  

  • 子类会先于父类被检查
  • 多个父类会根据它们在列表中的顺序被检查
  • 如果对下一个类存在两个合法的选择,选择第一个父

  Python 中的 MRO —— 方法搜索顺序

  Python中针对类提供了一个内置属性mro可以查看方法搜索顺序

  MRO 是 method resolution order,主要用于在多继承时判断方法、属性 的调用路径。

  

print(C.__mro__) #C是多继承后的类名

  输出结果:

  

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

  

  在搜索方法时,是按照mro的输出结果 从左至右 的顺序查找的;

  如果在当前类中找到方法,就直接执行,不再搜索;如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索。

  如果找到最后一个类,还没有找到方法,程序报错。

  

  

4多继承属性查询顺序

  (1)多继承结构为菱形结构

  如果继承关系为菱形结构,那么经典类与新式类会有不同MRO。

  

  箭头表示搜索顺序

  (2)多继承结构为非菱形结构

  会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性。

  

  

  

5查找流程

  ① 由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去。

  ② 由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去。

  主要知识点:类的__mro__ 属性的用法;

  

  

属性查找

  有了继承关系,对象在查找属性时,先从对象自己的__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

  # 运行流程分析:

  b.f2()会在父类Foo中找到f2,先打印Foo.f2,然后执行到self.f1(),即b.f1(),仍会按照:对象本身->类Bar->父类Foo的顺序依次找下去,在类Bar中找到f1,因而打印结果为Bar.f1

  父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的:

  

class Foo:

   def __f1(self): # 变形为_Foo__f1

   print(Foo.f1)

   def f2(self):

   print(Foo.f2)

   self.__f1() # 变形为self._Foo__f1,然后回到Bar类中找,没有,再到Foo中找到了

  class Bar(Foo):

   def __f1(self): # 变形为_Bar__f1

   print(Bar.f1)

  b=Bar() # Bar类处于执行阶段,_Bar__f1变为__f1

  b.f2() # 在父类中找到f2方法,进而调用b._Foo__f1()方法,是在父类中找到的f1方法

  # 运行结果:

  Foo.f2

  Foo.f1

  

  

6继承概念的实现

  方式主要有2类:

  

  • 实现继承
  • 接口继承

  ① 实现继承是指使用基类的属性和方法而无需额外编码的能力。

  ② 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。

  在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是属于关系。

  例如:Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类,但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

  

  

7私有属性私有方法在继承中的表现

  父类中的私有方法和私有属性都是不能被子类继承下来的;

  测试示例:父类中的私有属性和私有方法是否能被继承下来?

  

class Perpon:

   num = 20

   __num1 = 12

   def __test1(self):

   print(__test1....)

   def test2(self):

   print(test2...)

  class Student(Perpon):

   def test(self):

   print(num...)

   print(self.num)

   # print(Student.__num1)

   self.test2()

   # self.__test1()

  student = Student()

  student.test()

  student.test2()

  # student.__test1() # 报错

  num...

  20

  test2...

  test2...

  

  

  

8派生类

  1)在父类的基础上产生子类,产生的子类就叫做派生类

  2)父类里没有的方法,在子类中有了,这样的方法就叫做派生方法。

  3)父类里有,子类也有的方法,就叫做方法的重写(就是把父类里的方法重写了)

  

class Hero:

   def __init__(self, nickname,aggressivity,life_value):

   self.nickname = nickname

   self.aggressivity = aggressivity

   self.life_value = life_value

   def attack(self, enemy):

   print(Hero attack)

  class Garen(Hero):

   camp = Demacia

   def attack(self, enemy): #self=g1,enemy=r1

   # self.attack(enemy) #g1.attack(r1),这里相当于无限递归

   Hero.attack(self,enemy) # 引用 父类的 attack,对象会去跑 父类的 attack

   print(from garen attack) # 再回来这里

   def fire(self):

   print(%s is firing % self.nickname)

  class Riven(Hero):

   camp = Noxus

  g1 = Garen(garen, 18, 200)

  r1 = Riven(rivren, 18, 200)

  g1.attack(r1)

  # print(g1.camp)

  # print(r1.camp)

  # g1.fire()

  

  

9属性的覆盖(派生属性)

  子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类);

  需要注意的是:一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了(属性的覆盖)。

  示例:

  

# 派生属性: 子类中自己添加的新属性

  # 属性的覆盖: 子类和父类有相同属性,调用自己的

  class Perpon:

   num = 20

   def __init__(self, name):

   print(person...)

  class Student(Perpon):

   num = 10 # 把父类中的20覆盖

   def __init__(self, name, age): # age 为派生属性

   super().__init__(name)

   self.name = name

   self.age = age

   print(student...)

   def study(self):

   print(super().num)

   pass

  student = Student(赵四, 23)

  print(student.name) # person...

  print(student.age)

  print(student.num)

  student.study()

  person...

  student...

  赵四

  23

  10

  20

  

  

  

10父类属性(方法)的重用

  指名道姓的重用

  

class A:

   def __init__(self):

   print(A的构造方法)

  class B(A):

   def __init__(self):

   print(B的构造方法)

   A.__init__(self)

  class C(A):

   def __init__(self):

   print(C的构造方法)

   A.__init__(self)

  class D(B,C):

   def __init__(self):

   print(D的构造方法)

   B.__init__(self) # 先找到B,B调用A,等这个线性任务处理完之后,在继续下一行代码

   C.__init__(self) # 先找到C,C里面也调用A的方法

   pass

  f1=D() #A.__init__被重复调用

  D的构造方法

  B的构造方法

  A的构造方法

  C的构造方法

  A的构造方法

  

  Super()方法重用

  

class A:

   def __init__(self):

   print(A的构造方法)

  class B(A):

   def __init__(self):

   print(B的构造方法)

   super(B,self).__init__()

  class C(A):

   def __init__(self):

   print(C的构造方法)

   super(C,self).__init__()

  class D(B,C):

   def __init__(self):

   print(D的构造方法)

   super().__init__() # super(D,self).__init__()

  f1=D() #super()会基于mro列表,往后找

  D的构造方法

  B的构造方法

  C的构造方法

  A的构造方法

  # super() 语法

  # super(type[, object-or-type]) type 当前类,object-or-type 为实例化对象,一般默认为self,不过该参数在python3中默认

  super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。

  派生与继承解决问题:子类重用父类的属性,并派生出新的属性。

  注意:使用哪一种都可以,但不能两种方式混合使用!

  

  

11继承派生机制的作用

  可以将一些共用的功能加在基类中,实现代码的共享;

  在不改变基类的基础上改变原有的功能;

  练习:

  list类里只有append向末尾加一个元素的方法,但没有向列表头部添加元素的方法,试想能否为列表在不改变原有功能的基础上添加一个inster_head(x)方法,此方法能在列表的前部添加元素?

  

class Mylist(list):

   def insert_head(self,x):

   # self.reverse()

   # self.append(x)

   # self.reverse()

   self.insert(0,x) #直接在最开始插入x

  myl = Mylist(range(3,6))

  print(myl) #[3.4.5]

  myl.insert_head(2)

  print(myl) #[2,3,4,5]

  myl.append(6)

  print(myl) #[2,3,4,5,6]

  

  

12Super()

  super(cls,obj)返回被绑定超类的实例(要求obj必须为cls类型的实例)

  super() 返回被绑定超类的实例,等同于:super(class,实例方法的第一个参数,必须在方法内调用)

  格式:

  

父类类名.方法名称(self) 或者 super().方法名称()或者super(本类类名,对象名)

  作用:借助super()返回的实例间接调用父类的覆盖方法;

  示例:

  

#此示例示意用super函数间接调用父类的

  class A:

   def work(self):

   print(A.work被调用)

  class B(A):

   B类继承子A类

   def work(self):

   print(B.work被调用)

   def super_work(self):

   #调用b类自己的work方法

   self.work()

   #调用父类的work

   super(B,self).work()

   super().work() #此种调用方式只能在实例方法内调用

  b = B()

  # b.work() #B.work被调用!!

  # super(B,b).work() #A.work被调用

  b.super_work()

  # super_work() #出错

  调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且继承顺序严格遵循mro继承序列;

  示例:

  

class Father1:

   x =10

   pass

  class Father2:

   x = 20

   pass

  #多继承的情况下,从左到右

  class Sub(Father1,Father2):

   def __init__(self): #注意__int__不是__init__

   print(super().__delattr__)

  print(Sub.mro()) # [<class __main__.Sub>, <class __main__.Father1>, <class __main__.Father2>, <class object>]

  obj = Sub()

  print(object) #<class object>

  mro():会把当前类的继承关系列出来,严格按照mro列表的顺序往后查找

  

class A: #默认继承object

   def test(self):

   print(from A.test)

   super().test()

  class B:

   def test(self):

   print(from B.test)

  class C(A, B):

   pass

  c = C()

  #检查super的继承顺序

  #mro(): 会把当前类的继承关系列出来。

  print(C.mro()) #[<class __main__.C>, <class __main__.A>, <class __main__.B>, <class object>]

  c.test() #from A.test

   #from B.test

  使用super调用父类中的方法,注意分析程序的执行顺序。

  

class Parent(object):

   def __init__(self, name, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数

   print(parent的init开始被调用)

   self.name = name

   print(parent的init结束被调用)

  class Son1(Parent):

   def __init__(self, name, age, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数

   print(Son1的init开始被调用)

   self.age = age

   super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数

   print(Son1的init结束被调用)

  class Son2(Parent):

   def __init__(self, name, gender, *args, **kwargs): # 为避免多继承报错,使用不定长参数,接受参数

   print(Son2的init开始被调用)

   self.gender = gender

   super().__init__(name, *args, **kwargs) # 为避免多继承报错,使用不定长参数,接受参数

   print(Son2的init结束被调用)

  class Grandson(Son1, Son2):

   def __init__(self, name, age, gender):

   print(Grandson的init开始被调用)

   # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍

   # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因

   # super(Grandson, self).__init__(name, age, gender) 效果和下面的一样

   super().__init__(name, age, gender)

   print(Grandson的init结束被调用)

  print(Grandson.__mro__) #搜索顺序

  gs = Grandson(grandson, 12, 男)

  print(姓名:, gs.name)

  print(年龄:, gs.age)

  print(性别:, gs.gender)

  结果如下:

  (<class __main__.Grandson>, <class __main__.Son1>, <class __main__.Son2>, <class __main__.Parent>, <class object>)

  Grandson的init开始被调用

  Son1的init开始被调用

  Son2的init开始被调用

  parent的init开始被调用

  parent的init结束被调用

  Son2的init结束被调用

  Son1的init结束被调用

  Grandson的init结束被调用

  姓名:grandson

  年龄:12

  性别:男

  

  

注意:在上面模块中,当在子类中通过super调用父类方法时,parent被执行了1次。

  

  super调用过程:上面gs初始化时,先执行grandson中init方法, 其中的init有super调用,每执行到一次super时,都会从__mro__方法元组中顺序查找搜索。

  所以先调用son1的init方法,在son1中又有super调用,这个时候就就根据__mro__表去调用son2的init,然后在son2中又有super调用,这个就根据mro表又去调用parent中的init,直到调用object中的init。

  所以上面的打印结果如此,要仔细分析执行过程。

  重点提示:

  

  • 1)super().__init__相对于类名.init,在单继承上用法基本无差
  • 2)但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果。
  • 3)多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错。
  • 4)单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错。
  • 5)多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法。

  这也是为何多继承需要全部传参的一个原因

  练习一

  子类调用自己的方法的时候同时调用父类的方法

  

class Dog(Animal):

   def sleep(self):

   # 方法一 父类.方法名(对象)

   # Animal.eat(self)

   # 方法二 super(子类,对象名).方法名()

   super(Dog,dog).sleep()

   print(去狗窝)

   def look_door(self):

   print(看门狗)

  dog = Dog(哈巴狗,23)

  dog.sleep()

  睡觉

  去狗窝

  

  练习二

  在类的外部调用super()

  

# 类外部调用super()

  dog = Dog(哈巴狗, 23)

  super(Dog, dog).sleep() # super(子类,对象名).方法名()

  dog.sleep()

  睡觉

  去狗窝

  

  到此这篇关于python语法学习之super(),继承与派生的文章就介绍到这了,更多相关python继承与派生内容请搜索盛行IT软件开发工作室以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT软件开发工作室!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: