『无为则无心』Python面向对象 — 54、重写和super()函数

目录
  • 1、重写
  • 2、super()函数
    • 方式一
    • 方式二
    • __mro__内置类属性说明

1、重写

在子类中如果有和父类同名的方法,则通过子类实例去调用该方法时,会调用子类中的该方法而不是父类的方法,这个特点我们成为叫做方法的重写(覆盖:override)。

故事继续:徒弟掌握了师父和学院派技术后,自己潜心钻研出自己的独门配方的一套全新的煎饼果子技术。

# 1.创建师父类,属性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

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


# 2.创建学院派类 属性和方法
class School(object):
    def __init__(self):
        self.kongfu = '[学院派煎饼果子配方]'

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


# 3.独创配方
class Prentice(School, Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

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


# 4. 用徒弟类创建对象,调用实例属性和方法
tudi = Prentice()
print(tudi.kongfu)
tudi.make_cake()
"""
输出结果
[独创煎饼果子配方]
运用[独创煎饼果子配方]制作煎饼果子
"""

结论:子类和父类具有同名属性和方法,默认使用子类的同名属性和方法。

拓展:

当我们调用一个对象的方法时,会优先去当前对象中寻找是否具有该方法,如果有则直接调用。

如果没有,则去当前对象的父类中寻找,如果父类中有则直接调用父类中的方法。

如果没有,则去父类的父类中寻找,以此类推,直到找到object,如果依然没有找到,则报错。

2、super()函数

在类的继承中,如果重定义某个方法,该方法会覆盖父类的同名方法,但有时,我们希望能同时实现父类的功能,这时,我们就需要调用父类的方法了,可通过使用 super() 函数来实现。

实际需求就是:子类调用父类的同名方法和属性。

方式一

故事继续:虽然自己独创的煎饼果子配方非常受欢迎,但是还是有顾客都希望也能吃到古法和学院技术的煎饼果子。

# 1.创建师父类,属性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

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


# 2. 定义徒弟类,继承师父类
#   添加和父类同名的属性和方法
class Prentice(Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

    # 子类的make_cake(self)方法
    def make_cake(self):
        """
        加自己的初始化的原因:
        如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,
        如果不加这个自己的初始化,
        kongfu属性值是上一次调用的init内的kongfu属性值

        执行方式如下:
        # 创建一个子类对象
        tudi = Prentice()
        # 调用执行父类中的make_cake()方法
        tudi.make_master_cake()
        # 调用子类中的make_cake()
        tudi.make_cake()
        就能看到劫、结果。

        所以在调用属性前,先调用自己子类的初始化,要记住。
        """
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    # 调用父类的make_cake()方法
    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
    # 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def make_master_cake(self):
        # 父类类名.函数()
        # 再次调用初始化的原因:这里想要调用父类的同名方法和属性,
        # 属性在init初始化位置,所以需要再次调用init
        Master.__init__(self)
        Master.make_cake(self)


# 3 . 用徒弟类创建对象,调用实例属性和方法
# 创建一个子类对象
tudi = Prentice()

# 调用执行父类中的make_cake()方法
tudi.make_master_cake()

# 调用子类中的make_cake()
tudi.make_cake()

"""
输出结果:
运用[古法煎饼果子配方]制作煎饼果子
运用[独创煎饼果子配方]制作煎饼果子
"""

方式一:make_master_cake()方法中的代码冗余;父类类名如果变化,那么在子类中会涉及多处修改,另外,Python是允许多继承的语言,如上所示的方法在多继承时就需要重复写多次,显得累赘。为了解决这些问题,Python提供了super()函数解决此问题。

方式二

使用super()函数调用父类中的同名方法。

# 1.创建师父类,属性和方法
class Master(object):
    def __init__(self):
        self.kongfu = '[古法煎饼果子配方]'

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


# 2. 定义徒弟类,继承师父类
#   添加和父类同名的属性和方法
class Prentice(Master):
    def __init__(self):
        self.kongfu = '[独创煎饼果子配方]'

    # 子类的make_cake(self)方法
    def make_cake(self):
        self.__init__()
        print(f'运用{self.kongfu}制作煎饼果子')

    # 调用父类的make_cake()方法
    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
    # 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def make_master_cake(self):
        # 方法二: super()
        # super函数需要传递的参数:(当前类名, self)前边是类名,后边是对象
        # 方式一: super(当前类名, self).函数()
        # super(Prentice, self).__init__()
        # super(Prentice, self).make_cake()

        # 方式二; 简便方法,使用super().函数(),参数可省略。
        super().__init__()
        super().make_cake()


# 3 . 用徒弟类创建对象,调用实例属性和方法
# 创建一个子类对象
tudi = Prentice()

# 调用执行父类中的make_cake()方法
tudi.make_master_cake()

# 调用子类中的make_cake()
tudi.make_cake()

"""
输出结果:
运用[古法煎饼果子配方]制作煎饼果子
运用[独创煎饼果子配方]制作煎饼果子
"""

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

__mro__内置类属性说明

在Python中快速查看一个类的所继承的父类,以及父类的层级关系的方法,返回的结果是一个元组。

# 如上边示例
# 语法:类名.__mro__
print(Prentice.__mro__)

"""
输出结果:
(, , )

可以看到返回的是一个元组,
`super()`函数在这个元组的类中,依次查找相关属性或方法。
"""

你可能感兴趣的:(『无为则无心』Python面向对象 — 54、重写和super()函数)