目录

Python面向对象(3)继承

Python面向对象(3)继承

学习目标

  • 继承的概念

(以下是写法和特点的学习)

  • 单继承
  • 多继承
  • 子类重写父类的同名属性和方法
  • 子类调用父类的同名属性和方法
  • 多层继承

(另外两种情况)

  • super() 关键字
  • 私有属性和私有方法

继承的概念

生活中的继承,一般指的是子女继承父辈的财产,在python中继承先提一下这个拓展知识:

经典类(py2+)的写法:

1
2
class 类名:
  代码......

新式类的写法:

1
2
class 类名(object):
   代码......

写法上经典类与新式类的区别就是新式类多了一个括号,object意思就是一个类不去继承自己写的类,则默认继承所有类的顶级类,就是object类,也叫基类。3.5以上以新式类解释。

在Python中面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法,具体如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class A(object):
    def __init__(self):
        self.num = 123
    def print_info(self):
        print(self.num)

class B(A):     # B直接继承A类
    pass

result = B()
result.print_info() # 子类创建的对象仍然可以用父类中的方法

单继承

不同的继承有不同的特点,最简单的是单继承,案例:假如一个师傅有一个煎饼果子的秘方,如果他要继承给徒弟,那么他应该怎么做才能,单个徒弟继承单个师傅的所有属性和方法,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Master(object):
    def __init__(self):
        self.mifang = "[古法煎饼果汁]"

    def make_cake(self):
        print(f"运用{self.mifang}制作煎饼果子")

class Prentice(Master):
    pass

tudi1 = Prentice()
print(tudi1.mifang)

多继承

如果上个徒弟实例化的对象,想学习更多的煎饼果子技术,去请教更多的师傅,于是所谓的多继承就是一个子类同时继承多个父类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作煎饼果子')

class School(object):
    def __init__(self):
        self.mifang = '学校的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作的煎饼果汁')


class Prentice(School, Master):
    pass # 先去学校学习,然后再去师傅那拜师学艺

tudi = Prentice()
print(tudi.mifang)      # 学校的秘方
tudi.make_cake()        # 运用学校的秘方来制作的煎饼果汁

⚠️注意:多继承时,子类优先继承的第一个父类同名属性和方法。


子类重写父类同名方法和属性

如果徒弟掌握了学校和师傅的技术,自己潜心钻研自己的独门秘方。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作煎饼果子')

class School(object):
    def __init__(self):
        self.mifang = '学校的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作的煎饼果汁')


class Prentice(School, Master):
    def __init__(self):
        self.mifang = '独创的秘方'
    def make_cake(self):
        print(f"运用{self.mifang}来制作的煎饼果汁")

tudi = Prentice()
print(tudi.mifang)      # 独创的秘方
tudi.make_cake()        # 运用独创的秘方来制作的煎饼果汁

⚠️注意:如果子类和父类拥有同名的属性和方法,那么优先调用子类里的同名属性和方法。

如何查看子类的层级关系,查看继承了谁,代码如下:

1
2
print(Prentice.__mro__)
# (<class '__main__.Prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

子类调用父类的同名方法和属性

如果希望调用子类同名属性和方法的同时也能调用到父类的同名属性和方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作煎饼果子')

class School(object):
    def __init__(self):
        self.mifang = '学校的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作的煎饼果汁')


class Prentice(School, Master):
    def __init__(self):
        self.mifang = '独创的秘方'
    def make_cake(self):
        self.__init__()     # 这里需要自己加初始化,是因为在调用父类的属性和方法后,属性会被初始化为父类的,调用自己的就不是自己的初始化属性
        print(f"运用{self.mifang}来制作的煎饼果汁")
    def make_school_cake(self): #  把父类的同名属性和方法再次封装
        School.__init__(self) # 这里需要调用父类的同名方法和属性,属性在父类的初始化位置,需要再次初始化,否则是子类的初始化
        School.make_cake(self)
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

tudi = Prentice()
tudi.make_school_cake() # 运用学校的秘方来制作的煎饼果汁
tudi.make_master_cake() # 运用师傅的秘方来制作煎饼果子
tudi.make_cake()    # 运用独创的秘方来制作的煎饼果汁

我们可以把父类的属性和方法作为函数封装起来,但是需要重新初始化一下父类的属性,别忘对于子类自己的属性也要重新初始化。


多层继承

父类A继承给子类B,子类B又想继承给子类C,这种两个类继承关系以上的继承关系叫多层继承。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作煎饼果子')

class School(object):
    def __init__(self):
        self.mifang = '学校的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作的煎饼果汁')


class Prentice(School, Master):
    def __init__(self):
        self.mifang = '独创的秘方'
    def make_cake(self):
        self.__init__()     # 这里需要自己加初始化,是因为在调用父类的属性和方法后,属性会被初始化为父类的,调用自己的就不是自己的初始化属性
        print(f"运用{self.mifang}来制作的煎饼果汁")
    def make_school_cake(self): #  把父类的同名属性和方法再次封装
        School.__init__(self) # 这里需要调用父类的同名方法和属性,属性在父类的初始化位置,需要再次初始化,否则是子类的初始化
        School.make_cake(self)
    def make_master_cake(self):
        Master.__init__(self)
        Master.make_cake(self)

class Tusun(Prentice):  # 这个类是徒弟的徒弟
    pass

tu = Tusun()
tu.make_master_cake()   # 运用师傅的秘方来制作煎饼果子
tu.make_school_cake()   # 运用学校的秘方来制作的煎饼果汁
tu.make_cake()      # 运用独创的秘方来制作的煎饼果汁

super()调用父类方法

如果学校类和师傅类还有徒弟类是一个多层继承关系,如师傅继承学校类,徒弟继承师傅类,那么如果徒弟想要一次性使用师傅类和学校类的属性和方法,那么封装父类代码的函数将变得十分繁琐,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class School(object):
    def __init__(self):
        self.mifang = '学校的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作的煎饼果汁')

class Master(School):
    def __init__(self):
        self.mifang = '师傅的秘方'
    def make_cake(self):
        print(f'运用{self.mifang}来制作煎饼果子')

class Prentice(Master):
    def __init__(self):
        self.mifang = '独创的秘方'
    def make_cake(self):
        self.__init__()     # 这里需要自己加初始化,是因为在调用父类的属性和方法后,属性会被初始化为父类的,调用自己的就不是自己的初始化属性
        print(f"运用{self.mifang}来制作的煎饼果汁")
    def make_master_cake(self): #  把父类的同名属性和方法再次封装
        Master.__init__(self) # 这里需要调用父类的同名方法和属性,属性在父类的初始化位置,需要再次初始化,否则是子类的初始化
        Master.make_cake(self)
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)
    def make_old_cake(self):
        # 一次性使用,如果类名修改这里也要修改,如果类过多这里代码量会很大
        Master.__init__(self)
        Master.make_cake(self)
        School.__init__(self)
        School.make_cake(self)

tudi = Prentice()
tudi.make_old_cake()

如果使用super()关键字,那么调用父类的方法将不用多次写父类名,直接默认调用父类的属性和方法,super有两种写法,一种带参数,一种不带参数。

带参数的:

1
2
3
4
5
6
7
    def make_old_cake(self):
        # super(当前类名, self).函数()
        super(Prentice, self).__init__()
        super(Prentice, self).make_cake()

tudi = Prentice()
tudi.make_old_cake() # 运用师傅的秘方来制作煎饼果子

不带参数:

1
2
3
4
5
6
    def make_old_cake(self):
        # super(当前类名, self).函数()
        super().__init__()
        super().make_cake()
tudi = Prentice()
tudi.make_old_cake()			# 运用师傅的秘方来制作煎饼果子

⚠️注意:使用super()可以自动查找父类,调用顺序遵循__mro__类属性的顺序,比较适合单继承使用。


私有权限

定义私有属性和私有方法

在继承关系中,默认子类可以继承父类所有的属性和方法。如果父类某些属性和方法不想被子类继承,则父类可以设置私有属性和方法,比如师傅想把秘方继承给徒弟,但是不想把钱继承给徒弟,则可以设置钱为私有属性。设置私有权限的方法是:在属性名和方法名前面加上两个下划线。这样的属性和方法是私有属性和方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
        self.__money = 2000000 # 师傅的钱,为私有属性
    def cook_cake(self):
        print(f'运用{self.mifang}制作的煎饼果汁')
    def __private_method(self):
        print('私有方法')

class Prentice(Master):
    pass

tudi = Prentice()
# print(tudi.__money) # 报错,私有属性继承关系访问不到
tudi.__private_method() # 报错访问不到

获取和修改私有属性

私有属性和私有方法只能在类里面进行访问和修改。在python中,一般定义函数名为get_xx来获取私有属性,定义set_xx用来修改私有属性值,这个是工作习惯。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Master(object):
    def __init__(self):
        self.mifang = '师傅的秘方'
        self.__money = 2000000 # 师傅的钱,为私有属性
    def cook_cake(self):
        print(f'运用{self.mifang}制作的煎饼果汁')
    def __private_method(self):
        print('私有方法')
    def get_money(self):# 获取私有属性的方法
        return self.__money
    def set_money(self): # 修改私有属性的方法
        self.__money = 1000000

class Prentice(Master):
    pass

tudi = Prentice()
print(tudi.get_money()) # 可以获取到私有属性了 2000000
tudi.set_money() # 修改一下
print(tudi.get_money()) # 再看一下 1000000

总结

  • 继承有三个特点:
    • 第一个就是子类默认拥有父类的所有属性和方法,语法是使用新式类的写法类A(父类B):,多继承时子类对象调用同名方法时优先调用第一个父类属性和方法。
    • 第二个是子类重写父类同名方法和属性时,默认是优先只调用子类的属性和方法。
    • 第三个就是子类可以通过父类名调用父类属性和方法,当然可以使用super() 关键字。
  • 继承关系有:单继承、多继承、多层继承。
  • super()关键字可以使用参数和无参数的方式。
  • 如果父类有不想继承给子类的属性和方法则需要添加私有属性和方法。语法是__属性或者函数名