详析Python面向对象中的继承

目录
  • 一单继承
    • 1.继承的基本语法格式如下
    • 2.查看类继承情况
    • 3.继承中的属性和方法
    • 4.初始化函数__init__()和super
  • 二多层继承
  • 三多重继承

一 单继承

类继承作为python的三大特性之一,在我们学习python的时候是必不可少的。使用类继承,能够大大减少重复代码的编写。现来记录下,python中关于类继承的一些知识点。
类的继承有单继承,多层继承以及多重继承,先来看看单继承。

1. 继承的基本语法格式如下

#类继承语法格式,B类继承A类
class A():
    类属性
    类方法
    ...
class B(A):
    类属性
    类方法
    ...

单继承的话一般类A是没有继承其他派生类的,只继承了基类。因为在python新式类中,一个类会默认去继承基类object的,基类object是顶级类。

2. 查看类继承情况

class Father():
    #这是父类
    name1 = 'father_name'
    age1 = 'father_age'
    def father_method(self):
        print('我是父亲')

class Son(Father):
    #这是子类
    name2 = 'son_name'
    age2 = 'son_age'
    def son_method(self):
        print('我是孩子')

if __name__ == '__main__':
    A = Father()
    B = Son()
    #单继承
    print(B.__class__.__mro__)
    #或者Son.mro()
    print(Son.mro())

如上:我们定义了一个父类Father,一个子类Son,并且子类Son继承父类Father,它们都有自己的属性和方法。我们可以通过打印B.__ class__.__mro __ 或者Son.mro()来查看Son类的继承情况,如下:

>>>
(<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>)
[<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>]

可以看到,Son类确实继承了Father类,并且继承基类object。

3. 继承中的属性和方法

如果一个类继承了另外一个类,那么这个类是可以调用其继承类的属性和方法的(子类可以调用父类的属性和方法),如下

class Father():
    #这是父类
    name1 = 'father_name'
    age1 = 'father_age'
    def father_method(self):
        print('我是父亲')

class Son(Father):
    #这是子类
    name2 = 'son_name'
    age2 = 'son_age'
    def son_method(self):
        print('我是孩子')

    def fun1(self):
        #调用父类属性和方法
        print(self.age1, self.name1)
        self.father_method()

if __name__ == '__main__':
    A = Father()
    B = Son()
    #单继承
    # print(B.__class__.__mro__)
    B.fun1()

结果如下:

>>>
father_age father_name
我是父亲

当子类中的属性名方法名和父类中的属性名方法名同名时,在该子类中会覆盖父类的属性名和方法名(重写)。

class Father():
    #这是父类
    name1 = 'father_name'
    age1 = 'father_age'
    def father_method(self):
        print('我是父亲')

class Son(Father):
    #这是子类
    name1 = 'son_name'
    age1 = 'son_age'
    def son_method(self):
        print('我是孩子')

    def father_method(self):
        #和父类方法同名,将以子类方法为准
        print("与父类方法同名,但是我是子类")

    def son_fun1(self):
        #调用父类属性
        print("子类属性和父类属性同名,以子类为准:", self.name1, self.age1)

if __name__ == '__main__':
    A = Father()
    B = Son()
    #单继承
    # print(B.__class__.__mro__)
    B.father_method()
    B.son_fun1()

输出如下:

>>>
与父类方法同名,但是我是子类
子类属性和父类属性同名,以子类为准: son_name son_age

4. 初始化函数__init__()和 super

上面写的子类和父类都是不需要传参数的,而当我们需要给类传参数时,往往都是要初始化的,下面来看看子类继承父类时,参数初始化的几种情况。

子类无新增参数:

class Father():
    #父类
    def __init__(self, name='张三', age=23):
        self.name = name
        self.age = age

class Son(Father):
    #子类
    def son_fun1(self):
        print(self.name, self.age)

if __name__ == '__main__':
    B = Son()
    B.son_fun1()

输出:

>>>
张三 23

如上,在子类无新增参数时,无需进行__init__ 初始化,直接调用父类的对象属性即可。因为子类Son是继承自父类Father的,所以在调用时会首先去调父类的__init__ 进行初始化

子类有新增参数:

当子类有新增参数时,该怎么初始化呢?先来看看这个对不对

class Father():
    #父类
    def __init__(self, name='张三', age=23):
        self.name = name
        self.age = age

class Son(Father):
    #子类
    def __init__(self, height):
        self.height = height

    def son_fun1(self):
        print(self.height)
        print(self.name, self.age)

if __name__ == '__main__':
    B = Son(170)
    B.son_fun1()

输出:

>>>
AttributeError: 'Son' object has no attribute 'name'
170

上面子类Son新增了一个height参数,然后用__init__ 进行初始化。但是从输出结果可以看出,height参数是正常打印的,打印name和age参数时就报错:子类Son没有属性’name’,因为这个时候就不会去调用父类的__init__ 进行初始化,而是直接调用子类中的__init__ 进行初始化,这时,子类初始化就会覆盖掉父类的__init__ 初始化,从而报错。

正确的初始化有两种方法,如下:

#方法1
def __init__(self, 父类参数1, 父类参数2, ..., 子类参数1, 子类参数2, ...)
    父类名.__init__(self, 父类参数1, 父类参数2, ...)
    self.子类属性 = 子类属性

class Father():
    #父类
    def __init__(self, name='张三', age=23):
        self.name = name
        self.age = age

class Son(Father):
    #子类
    def __init__(self, name, age, height):
        #方法1
        Father.__init__(self, name, age)
        self.height = height

    def son_fun1(self):
        print(self.height)
        print(self.name, self.age)

if __name__ == '__main__':
    B = Son('李四', 24, 170)
    B.son_fun1()

>>>
175
李四 24

从结果可以看出,调用父类初始化后,结果就正常输出。这是在子类__init__初始化的时候,调用了Father.__ init __(self, name, age)对父类对象属性进行了初始化。

现在来看看另外一个初始化方法super()初始化,不过使用super()的时候要注意python的版本,因为python2和python3的使用是不同的,如下

#方法2
def __init__(self, 父类参数1, 父类参数2, ..., 子类参数1, 子类参数2, ...): 
    #python2的super初始化
    super(子类名, self).__init__(父类类参数1, 父类参数2, ...)
    self.子类属性 = 子类属性
    #python3的super初始化
    super().__init__(父类类参数1, 父类参数2, ...)
    self.子类属性 = 子类属性

class Father():
    #父类
    def __init__(self, name='张三', age=23):
        self.name = name
        self.age = age

class Son(Father):
    #子类
    def __init__(self, name, age, height):
        #方法2
        #python2的super初始化
        super(Son, self).__init__(name, age)
        self.height = height
        #python3的super初始化
        # super().__init__(name, age)
        #或者 super(Son, self).__init__(name, age)
        
    def son_fun1(self):
        print(self.height)
        print(self.name, self.age)

if __name__ == '__main__':
    B = Son('李四', 24, 175)
    B.son_fun1()

结果:

>>>
175
李四 24

上面使用的是super(Son, self).__ init __ (name, age) 来对父类对象属性进行初始化。不过这是要区分python版本的,在python3中使用super(Son, self).__ init__(name, age)或者super().__ init__(name, age)都是可以的,但是在python2的版本中,要使用super(Son, self).__ init__(name, age)来进行初始化,而且父类必须继承object,否则就会报错。

二 多层继承

上面的记录的是单继承,现在来看看多层继承。多层继承:子类继承自多个父类,父类只继承自object顶级类。

class Father():
    def __init__(self):
        print("enter father")
        print("leave father")

    def fun(self):
        print("这是father")

class Mother():
    def __init__(self):
        print("enter mother")
        print("leave mother")

    def fun(self):
        print("这是mather")

class Son(Father, Mother):
    def __init__(self):
        print("enter son")
        super().__init__()
        print("leave son")

if __name__ == '__main__':
    B = Son()
    B.fun()
    print(Son.mro())

输出:

>>>
enter son
enter father
leave father
leave son
这是father

继承关系:

[<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>]

这里有两个父类Father,Mother,一个子类Son,子类Son继承了类Father和Mother,继承顺序是Father在前,Mother在后。从上面输出结果来看,子类初始化过程是先进入子类Son的 __ init __方法,其次调用super(). __init __()进行父类对象属性初始化,最后初始化完成。
从结果可以知道上面super初始化调用的是父类Father的 __ init __方法。
。也可以看出,类Father和类Mother都有一个fun方法(同名),但在子类调用时调的是父类father的fun方法。这是为什么呢?

这里就涉及到super的继承机制,即super会根据MRO机制,从左到右依次调用父类的属性和方法, 当父类中有同属性名,同方法名时,以靠左的那个父类为准。

下面我们把Father和Mother位置换一下,如下:

class Father():
    def __init__(self):
        print("enter father")
        print("leave father")

    def fun(self):
        print("这是father")

class Mother():
    def __init__(self):
        print("enter mother")
        print("leave mother")

    def fun(self):
        print("这是mather")

class Son(Mother, Father):#这里变动,变换Father和Mother的位置
    def __init__(self):
        print("enter son")
        super().__init__()
        print("leave son")

if __name__ == '__main__':
    B = Son()
    B.fun()
    print(Son.mro())

结果:

>>>
enter son
enter mother
leave mother
leave son
这是mather
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]

继承关系:

>>>
[<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>]

可以看出,当Mother在前时,调用的就是类Mather的初始化方法和fun方法。所以在多层继承时,一定要注意父类的位置顺序。

三 多重继承

其实super的产生就是用来解决多重继承问题的,什么是多重继承呢?即,子类继承多个父类,而父类又继承自其它相同的类,这种又被称为菱形继承或者砖石继承,

如下:

A
   /   \
  /     \
 B       C
  \     /
   \   /
     D

先来看看如下的一个菱形继承:

class A():
    def __init__(self):
        print("enter A")
        print("leave A")

class B(A):
    def __init__(self):
        print("enter B")
        # super().__init__()
        A.__init__(self)
        print("leave B")

class C(A):
    def __init__(self):
        print("enter C")
        # super().__init__()
        A.__init__(self)
        print("leave C")

class D(B, C):
    def __init__(self):
        print("enter D")
        B.__init__(self)
        C.__init__(self)
        # super().__init__()
        print("leave D")

if __name__ == '__main__':
    d = D()

输出结果:

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

上面用的是父类名. __ init__ () 来进行子类初始化的,但是从结果来看,类A的初始化方法是被执行了两次的,一次是类B的调用,一次是类C的调用。也可以看出子类D的初始化 __ init __ 调用了B.__ init __() 和C. __init __() 。现在D类继承的父类只有B和C,但是当代码比较复杂,继承的类比较多时,就得的一个一个写,如果类B和类C也是继承自多个类,那它们的初始化方法也得重新写,这样就比较繁琐,也容易出错。

下面来使用super初始化父类看看:

class A():
    def __init__(self):
        print("enter A")
        print("leave A")

class B(A):
    def __init__(self):
        print("enter B")
        super().__init__()
        print("leave B")

class C(A):
    def __init__(self):
        print("enter C")
        super().__init__()
        print("leave C")

class D(B, C):
    def __init__(self):
        print("enter D")
        super().__init__()
        print("leave D")

if __name__ == '__main__':
    d = D()
    print(D.mro())

输出结果:

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

可以看到输出结果是不一样的,类A的初始化只执行了一次,而且当D类有新增父类时,初始化方法也不用动,从而避免重写初始化方法出错,代码也变得整洁。
这里还是和super的继承机制有关,上面说过当子类继承自多个父类时,super会根据MRO机制,从左到右依次调用父类的属性和方法,而且使用super初始化父类时,会一次性初始化所有的父类,所以上面的类A初始化方法不会被调用两次。

所以在类的多重继承中,一般建议使用super来初始化父类对象属性。

到此这篇关于详析Python面向对象中的继承的文章就介绍到这了,更多相关Python面向对象继承内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python面向对象编程之类的继承

    目录 1.对继承的理解 2.类继承的构建 3.Python中最基础的类 4.ython类的重载 4.1 属性重载 4.2 方法重载 5.类的多继承 1.对继承的理解 继承(Inheritance) :代码复用的高级抽象 继承是面向对象设计的精髓之一 实现了以类为单位的高级抽象级别代码复用 继承是新定义类能够几乎完全使用原有类属性与方法的过程 不管是基类还是派生类,只是一种继承说法,这都是普通的Python类 也可以按子类.父类和超类划分. 最基础的类是基类,经过一次继承得出派生类,还可以再一次继

  • Python 面向对象编程的三大特性之继承

    目录 Python  面向对象编程的三大特性之继承 一.继承 1.继承的实际栗子 2.继承的好处 3.继承的使用场景 4.继承有几种? 5.Python 中继承的简单语法 二.不使用继承.使用继承的区别 1.需求背景 2.不使用继承 2.使用继承 三.继承的传递性 1.什么是传递性 四.继承和抽象 1.继承的重点 Python  面向对象编程的三大特性之继承 一.继承 继承也是面向对象编程三大特性之一 继承是类与类的一种关系 定义一个新的 class 时,可以从某个现有的 class 继承 新的

  • Python面向对象封装继承和多态示例讲解

    面向对象的三大特征:封装,继承,多态 1.封装: 提高程序的安全性 将数据(属性)和行为(方法)包装到类对象中,在方法内部对属性进行对象的外部调用方法. 这样无需关心内部的具体实现. 在python中没有专门的修饰符用于属性的私有,如果属性不希望被访问,前面使用两个下划线 2.继承: 提高代码的复用性 3.提高程序的可拓展性和可 维护性. 1. 封装 我们来看看私有方式的使用: # 作者:互联网老辛 # 开发时间:2021/4/4/0004 22:11 class Student: def __

  • Python面向对象三大特征 封装、继承、多态

    目录 1.封装 1.1 私有属性和私有方法 2.继承 2.1 方法的重写 2.2 在子类方法中调用父类方法 2.3 多继承 2.4 新式类和经典类 3.多态 1.封装 封装: 根据 职责 将 属性 和 方法 封装到一个 抽象的类 中将数据(属性)和行为(方法)包装到类对象中.在方法内部对属性进行操作,在类对象的外部调用方法.这样无需关心方法 内部的具体实现,从而隔离了复杂度 封装是面向对象编程的一大特点 面向对象编程的第一步.将属性和方法封装到一个抽象的类中 外界使用类创建对象,然后让对象调用方

  • python面向对象之类的继承详解

    一.概述 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为"子类"或"派生类",被继承的类称为"基类"."父类"或"超类",继承的过程,就是从一般到特殊的过程.在某些 OOP 语言中,一个子类可以继承多个基类.但是一般情况下,一个子类只能有一个基类,要实

  • Python中关于面向对象中继承的详细讲解

    目录 1.继承 2.单继承 3.多继承 4.子类重写父类的同名属性和方法 5.子类调用父类同名属性和方法 6.多层继承 7.调用父类方法super() 8.案例 1.继承 在程序中,继承描述的是多个类之间的所属关系. 如果一个类A里面的属性和方法可以复用,则可以通过继承的方式,传递到类B里. 那么类A就是基类,也叫做父类:类B就是派生类,也叫做子类. 案例: # 父类 class A(object): def __init__(self): self.num=50 def print_num(s

  • 详析Python面向对象中的继承

    目录 一单继承 1.继承的基本语法格式如下 2.查看类继承情况 3.继承中的属性和方法 4.初始化函数__init__()和super 二多层继承 三多重继承 一 单继承 类继承作为python的三大特性之一,在我们学习python的时候是必不可少的.使用类继承,能够大大减少重复代码的编写.现来记录下,python中关于类继承的一些知识点.类的继承有单继承,多层继承以及多重继承,先来看看单继承. 1. 继承的基本语法格式如下 #类继承语法格式,B类继承A类 class A():     类属性

  • Python面向对象类的继承实例详解

    本文实例讲述了Python面向对象类的继承.分享给大家供大家参考,具体如下: 一.概述 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为"子类"或"派生类",被继承的类称为"基类"."父类"或"超类",继承的过程,就是从一般到特殊的过程.在某些 OOP

  • Python面向对象程序设计之继承、多态原理与用法详解

    本文实例讲述了Python面向对象程序设计之继承.多态原理与用法.分享给大家供大家参考,具体如下: 相关内容: 继承:多继承.super.__init__.重写父类变量或函数 多态 继承: 在Python3中,不写基类的类默认继承object 继承就是子类获得了父类的全部功能:比如学生和老师都有"姓名,性别,年龄.ID"等学校人员属性,如果学生和老师都直接继承学校人员的"姓名,性别,年龄.ID",那么就不必要在学生类和老师类中重复定义了. 继承指定类的方式是:cla

  • Python面向对象程序设计之继承与多继承用法分析

    本文实例讲述了Python面向对象程序设计之继承与多继承.分享给大家供大家参考,具体如下: 1. 继承 在C++和Java中,使用继承时,子类的构造函数会自动调用父类的构造函数,但在Python中,子类必须显式的在__init__()函数中再次调用父类中的__init__()函数.如下例: class Employee(object): def __init__(self, name, salary = 0): self.name = name self.salary = salary def

  • Python面向对象程序设计多继承和多态用法示例

    本文实例讲述了Python面向对象程序设计多继承和多态用法.分享给大家供大家参考,具体如下: 多继承 就是一个子类继承多个父类: 多继承的例子,如下: # -*- coding:utf-8 -*- #! python3 class Base(object): def test(self): print("------base") class A(Base): def test1(self): print("-----test1") class B(Base): de

  • 详解python程序中的多任务

    现实生活中,有很多场景中的事情是同时进行的,比如开车的时候,手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的. 以上这些可以理解为多任务.那在程序中怎么能做到多任务,它有什么好处? 接下来我们来看看没有多任务的程序是什么效果. import time def sing(): for i in range(5): print("正在唱...") time.sleep(1) def dance(): for i in range(5): print("正在跳...")

  • 详解python数组中的符号...与:符号的不同之处

    不知道大家有没有见过在python数组中使用...符号,因为前段时间读别人代码的时候遇到了这个符号立刻就云里雾里,于是这里特此记录一下.先来看一段代码: import numpy as np x = np.array([[1, 3], [5, 6], [8, 10]]) print("使用'...'符号的结果为:") print(x[..., 0]) print("使用':'符号的结果为:") print(x[:, 0]) """ 使用

  • 详解python requests中的post请求的参数问题

    问题:最新在爬取某站点的时候,发现在post请求当中,参数构造正确却获取不到数据,索性将post的参数urlencode之后放到post请求的url后面变成get请求,结果成功获取到数据,对此展开疑问. 1.http请求中Form Data和Request Playload的区别: Ajax post请求中常用的两种参数形式:form data 和 request payload get请求的时候,我们的参数直接反映在url里面,为key1=value1&key2=value2形式,如果是pos

  • 详解Python自动化中这八大元素定位

    一.find_element_by_id() find_element_by_id() 1.从上面定位到的元素属性中,可以看到有个id属性:id="kw",这里可以通过它的id属性定位到这个元素. 2.定位到搜索框后,用send_keys()方法,就可以输入文本. from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.baidu.com") # 通过id定

随机推荐