Python OOP:继承、单继承、多继承、__mro__、子类重写父类同名属性和方法、子类调用父类同名属性和方法、多层继承、super()、私有(实例)属性和方法、获取修改私有属性值、私有类属性

一、继承

Python⾯向对象的继承指的是多个类之间的所属关系,即⼦类默认继承⽗类的所有属性和⽅法

继承作用:继承可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
在这里插入图片描述
在Python中,所有类默认继承object类,object类是顶级类或基类;其他⼦类叫做派⽣类。

class Being:  # 定义一个类Being
    def __init__(self):  #初始化实例属性
        self.num = 1  #在类里面添加实例属性self.属性名=值

    def info_print(self):  # 定义一个实例方法
        print(self.num)  # 在类里面访问实例属性self.属性名

class People(Being):  # 定义一个类People,继承Being类
    pass

p1 = People()  # 创建People类的一个对象p1
p1.info_print()  # 对象调用父类实例方法
p1.num  # 对象访问父类实例属性

输出:1
class Being(object):
    def __init__(self):
        self.num = 1

    def info_print(self):
        print(self.num)

class People(Being):
    pass

p1 = People()
p1.info_print()

输出:1

二、单继承

单继承:一个子类继承一个父类
在这里插入图片描述

class Master:
    def __init__(self):
        self.kongfu = "古法配方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master):
    pass

daqiu = prentice()
print(daqiu.kongfu)
daqiu.make_cake()

输出:
古法配方
古法配方制作煎饼果子

二、多继承

多继承:一个子类同时继承多个⽗类
当⼀个类有多个父类的时候,默认使用第⼀个父类的同名属性和方法。
在这里插入图片描述

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    pass

daqiu = prentice()
print(daqiu.kongfu)  # 同名属性,访问第一个父类的同名属性
daqiu.make_cake()  # 同名方法,访问第一个父类的同名方法

print(daqiu.age)  # 不同名属性,第一个父类正常
print(daqiu.name)  # 不同名属性,非第一个父类报错

输出:
在这里插入图片描述

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

⼦类和父类具有同名属性和方法,默认使用子类的同名属性和方法。
在这里插入图片描述

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

daqiu = prentice()
print(daqiu.kongfu)  # 同名属性,重写父类
daqiu.make_cake()  # 同名方法,重写父类
#如果子类和父类有同名属性和方法,子类创建对象调用属性和方法,调用的是子类里面的同名属性和方法

# print(daqiu.age)  # 不同名属性,报错
print(daqiu.name)  # 不同名属性,报错

输出:
在这里插入图片描述

四、___mro___

Python的MRO即Method Resolution Order(方法解析顺序),即在调用方法时,会对当前类以及所有的基类进行一个搜索,以确定该方法之所在,而这个搜索的顺序就是MRO。

类名.__mro__,返回一个元组。

可以清晰地看到类的层级关系。

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School(Master):
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))


print(prentice.__mro__)
print(type(prentice.__mro__))
print(list(prentice.__mro__))

输出:
(<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)
<class 'tuple'>
[<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>]

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

子类里如果有与父类同名的属性方法,调用这个属性方法,调用的是子类里面的,调用不到父类里面的属性方法。

如何实现同名属性方法,既能调用子类里面的,也能调用父类里面的呢?

  • 1.在子类里定义一个函数
  • 2.再次初始化父类属性(参数self)
  • 3.调用父类方法(参数self)
    在这里插入图片描述
class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

    def make_master_cake(self):  # 调用父类方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
        Master.__init__(self)
        Master.make_cake(self)
    
    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


daqiu = prentice()
print(daqiu.kongfu)
daqiu.make_cake()
daqiu.make_master_cake()
daqiu.make_school_cake()

print(prentice.__mro__)

输出:
独创配方  # 返回的是子类属性
独创配方制作煎饼果子  # 调用的是子类方法
古法配方制作煎饼果子  # 调用到了父类Master的方法
学校技术制作煎饼果子  # 调用到了父类School的方法
(<class '__main__.prentice'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)
class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

    def make_master_cake(self):  # 调用父类方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
        Master.__init__(self)
        Master.make_cake(self)

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


daqiu = prentice()
daqiu.make_master_cake()
daqiu.make_school_cake()
print(daqiu.kongfu)
daqiu.make_cake()
print(prentice.__mro__)

输出:
古法配方制作煎饼果子
学校技术制作煎饼果子
学校技术  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性
学校技术制作煎饼果子  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性
(<class '__main__.prentice'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)

代码修正,

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

    def make_master_cake(self):  # 调用父类同名属性方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
        Master.__init__(self)  # 父类类名.__init__(self),父类初始化调用一次,注意不要漏写self
        Master.make_cake(self)  # 父类类名.函数(self), 父类方法调用一次,注意不要漏写self

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)


daqiu = prentice()
daqiu.make_master_cake()
daqiu.make_school_cake()
print(daqiu.kongfu)
daqiu.make_cake()

输出:
古法配方制作煎饼果子
学校技术制作煎饼果子
学校技术  # 因为先调用父类,这里子类同名属性仍然被父类属性覆盖
独创配方制作煎饼果子

六、多层继承

在这里插入图片描述
多层继承:大于两层的继承

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(Master, School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

    def make_master_cake(self):  # 调用父类同名属性方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
        Master.__init__(self)  # 父类初始化调用一次,注意不要漏写self
        Master.make_cake(self)  # 父类方法调用一次,注意不要漏写self

    def make_school_cake(self):
        School.__init__(self)
        School.make_cake(self)

class TuShun(prentice):
    pass

xiaoqiu = TuShun()  # 说明子类可以继承父类所有属性和方法
xiaoqiu.make_cake()  # 调用父类prentice方法
xiaoqiu.make_master_cake()  # 调用父类prentice的父类方法
xiaoqiu.make_school_cake()  # 调用父类prentice的父类方法
print(xiaoqiu.make_cake)  # 前一步调用父类prentice的父类school类,school类初始化后,属性是school类的属性

输出:
独创配方制作煎饼果子
古法配方制作煎饼果子
学校技术制作煎饼果子
print(TuShun.__mro__)

输出:
(<class '__main__.TuShun'>, <class '__main__.prentice'>, <class '__main__.Master'>, <class '__main__.School'>, <class 'object'>)

七、super()调用父类方法

方法一:在子类里定义一个函数,在里面初始化父类属性、调用父类方法
⽅法⼀特点:代码冗余;⽗类类名如果变化,这⾥代码需要频繁修改。如果有多个父类,代码量重复增加。

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School(Master):
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

    # def make_master_cake(self):  # 调用父类同名属性方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
    #     Master.__init__(self)  # 父类初始化调用一次,注意不要漏写self
    #     Master.make_cake(self)  # 父类方法调用一次,注意不要漏写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)

daqiu = prentice()
daqiu.make_old_cake()  # 从输出可以看到,两个父类的同名方法都调用到了
daqiu.make_cake()
print(prentice.__mro__)

输出:
古法配方制作煎饼果子
学校技术制作煎饼果子
独创配方制作煎饼果子
(<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

方法二:super()方法,带参数写法, super(当前类名, self).函数()
方法二特点:super里的类名是当前类名,自己可以控制,防止改了类名而未改super里的类名。缺点是仍然需要注意上下保持一致,而且如果有多个父类,需要在多个父类里加入super代码。

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School(Master):
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

    # 方法二(一):super(当前类名,self).函数()
        super(School, self).__init__()
        super(School, self).make_cake()

class prentice(School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

    # def make_master_cake(self):  # 调用父类同名属性方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
    #     Master.__init__(self)  # 父类初始化调用一次,注意不要漏写self
    #     Master.make_cake(self)  # 父类方法调用一次,注意不要漏写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)

    # 方法二(一):super(当前类名,self).函数()
    def make_old_cake(self):  # 需要同时在当前类prentice的父类School里也加super()
        super(prentice, self).__init__()
        super(prentice, self).make_cake()




daqiu = prentice()
daqiu.make_old_cake()
daqiu.make_cake()
print(prentice.__mro__)

输出:
学校技术制作煎饼果子
古法配方制作煎饼果子
独创配方制作煎饼果子
(<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

<推荐使用以下方法>
方法三:super()方法,无参数写法, super().函数()
方法三特点:super()无参数写法,自动查找直接父类
使⽤super() 可以⾃动查找⽗类。调⽤顺序遵循 mro 类属性的顺序。⽐较适合单继承使⽤。
.

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School(Master):
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

    # 方法二(一):super(当前类名,self).函数()
    #     super(School, self).__init__()
    #     super(School, self).make_cake()

    # 方法二(二):super(当前类名,self).函数()
        super().__init__()
        super().make_cake()

class prentice(School):
    def __init__(self):
        self.kongfu = "独创配方"

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

    # def make_master_cake(self):  # 调用父类同名属性方法,为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化,类名.__init__(self)
    #     Master.__init__(self)  # 父类初始化调用一次,注意不要漏写self
    #     Master.make_cake(self)  # 父类方法调用一次,注意不要漏写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)

    # 方法二(一):super(当前类名,self).函数()
    # def make_old_cake(self):  # 需要同时在当前类prentice的父类School里也加super()
    #     super(prentice, self).__init__()
    #     super(prentice, self).make_cake()

    # 方法二(二):super(当前类名,self).函数()
    def make_old_cake(self):
        super().__init__()
        super().make_cake()


daqiu = prentice()
daqiu.make_old_cake()
daqiu.make_cake()
print(prentice.__mro__)

输出:
学校技术制作煎饼果子
古法配方制作煎饼果子
独创配方制作煎饼果子
(<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

八、定义私有(实例)属性和方法

默认情况下,只要继承关系建立,子类默认继承父类所有的属性和方法,这些能够继承给子类的属性方法叫共有权限。

如果有些属性和方法不想继承给子类了,私有权限,可以添加私有属性方法来实现。
在这里插入图片描述
注意:私有属性和私有方法只能在类里面访问和修改。,对某些属性方法起到保护作用。

.

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(School, Master):
    def __init__(self):
        self.kongfu = "独创配方"
        self.__money = "一个亿"  # 定义私有属性

    def __printinfo(self):  # 定义私有⽅法
        print(self.kongfu)
        print(self.__money)

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

class TuShun(prentice):
    pass


xiaoqiu = TuShun()
print(xiaoqiu.kongfu)  # 独创配方
print(xiaoqiu.money)  # AttributeError: 'TuShun' object has no attribute 'money'
print(xiaoqiu.__money)  # AttributeError: 'TuShun' object has no attribute '__money'
xiaoqiu.printinfo  # AttributeError: 'TuShun' object has no attribute 'printinfo'
xiaoqiu.__printinfo  # AttributeError: 'TuShun' object has no attribute '__printinfo'
# ⼦类⽆法继承⽗类的私有属性和私有⽅法

九、获取和修改私有属性值

在Python中,在类里面,定义函数 get_属性名 ⽤来获取私有属性,定义 set_属性名 ⽤来修改私有属性值。

class Master:
    def __init__(self):
        self.kongfu = "古法配方"
        self.age = 100

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class School:
    def __init__(self):
        self.kongfu = "学校技术"
        self.name = "新东方"

    def make_cake(self):
        print("{}制作煎饼果子".format(self.kongfu))

class prentice(School, Master):
    def __init__(self):
        self.kongfu = "独创配方"
        self.__money = "私有:一个亿"

    def get_money(self):
        print(self.__money)  # 或者这里用 return self.__money,调用时用print

    def set_money(self):
        self.__money = "私有更新:十个亿"

    def __printinfo(self):
        print(self.kongfu)
        print(self.__money)

    def make_cake(self):
        self.__init__()  # 如果先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用子类方法前,先调用子类自己的初始化
        print("{}制作煎饼果子".format(self.kongfu))

class TuShun(prentice):
    pass

xiaoqiu = TuShun()
xiaoqiu.get_money()
xiaoqiu.set_money()
xiaoqiu.get_money()

print(prentice.__mro__)

输出:
私有:一个亿
私有更新:十个亿
(<class '__main__.prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)

十、私有类属性

class Dog:
    __age = 3

    @classmethod
    def get_age(cls):
        return cls.__age
    
wangcai = Dog()
result = wangcai.get_age()
print(result)  # 3

十一、继承知识小结

在这里插入图片描述
PS: source,itheima

猜你喜欢

转载自blog.csdn.net/weixin_47008635/article/details/114649315