🍔类的派生

一.什么是派生

  • 子类中新定义的属性的这个过程就叫做派生
  • 注意 : 当子类在使用派生属性的时候始终以自己的属性为准
  • 父类----->称为基类或者超类
  • 子类----->称为派生类
class Animal:              # 父类(基类)
    def run(self):
        print("奔跑")
    
    def eat(self):
        print("吃东西")
        
class Duck(Animal):        # 子类(派生类)
    bread = "可爱鸭"        # 派生属性(品种)
    
    def speak(self):       # 派生方法(说话)
        print("嘎嘎嘎")

二.派生类中使用父类的属性和方法

1.方式一 : 直接使用 self.[属性或方法] 来调用

  • 存在的问题 : 如果子类与父类中有相同的属性或方法, 那么就无法使用父类中的属性或方法 (因为优先使用自己的)
  • 也有可能产生递归调用
当父类与派生类中的属性和方法不重复时 (没有问题)
class Animal:
    def run(self):
        print("奔跑(Animal)")

    def eat(self):
        print("吃东西(Animal)")

class Duck(Animal):
    bread = "可爱鸭(Duck)"

    def speak(self):
        self.eat()            # 调用父类中的 "eat()" 方法
        print("嘎嘎嘎(Duck)")

duck1 = Duck()
duck1.speak()
'''输出
吃东西(Animal)
嘎嘎嘎(Duck)
'''

当派生类中也存在相同方法 "eat()" 的时候, (优先调用自己的方法)
class Animal:
    def run(self):
        print("奔跑(Animal)")

    def eat(self):
        print("吃东西(Animal)")

class Duck(Animal):
    bread = "可爱鸭(Duck)"

    def eat(self):           # 子类中存在 "eat()" 方法
       # self.eat()          # 这种情况为递归调用,会超过最大深度,报错
        print("吃东西(Duck)")
        
    def speak(self):
        self.eat()            # 想调用父类中的 "eat()" 方法
        print("嘎嘎嘎(Duck)")

duck1 = Duck()
duck1.speak()
'''输出
吃东西(Duck)
嘎嘎嘎(Duck)
'''

2.方式二 : 指名道姓的使用父类中的方法

  • 指名道姓的调用, ''名''指的是父类名, 当类来调用方法的时候, 就是在调用普通的函数, 有几个值就要传几个值, 所以要传 "self"
class Animal:
    def run(self):
        print("奔跑(Animal)")

    def eat(self):
        print("吃东西(Animal)")

class Duck(Animal):
    bread = "可爱鸭(Duck)"

    def eat(self):
        print("吃东西(Duck)")

    def speak(self):
        Animal.eat(self)   # 指名道姓的调用父类中的"eat()"方法, 必须需要添加"self"
        print("嘎嘎嘎(Duck)")

duck1 = Duck()
duck1.speak()
'''输出
吃东西(Animal)
嘎嘎嘎(Duck)
'''

3.方式三 : 通过 super() 调用

  • 严格依赖继承属性来查找关系

  • super() 的返回值是一个特殊的对象, 该对象专门用来调用父类中的属性

  • super().__init__() 不用再传入 "self", 因为自动传入

  • python2中需要为 super 传参 super(自己的类名,self)

  • python3中优化可以不用传参 super()

class Animal:
    def run(self):
        print("奔跑(Animal)")

    def eat(self):
        print("吃东西(Animal)")

class Duck(Animal):
    bread = "可爱鸭(Duck)"

    def eat(self):
        print("吃东西(Duck)")

    def speak(self):
        super(Duck, self).eat()  # python2需要传参 (使用super就不用再传入"self")
        super().eat()            # python3中可以不传 (使用super就不用再传入"self")
        print("嘎嘎嘎(Duck)")

duck1 = Duck()

duck1.speak()
'''输出
吃东西(Animal)
吃东西(Animal)
嘎嘎嘎(Duck)
'''
  • 示例2
class People(object):
    school = "蟹堡王餐厅"

    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

class Staff(People):
    def __init__(self,name,age,sex,id):
       # People.__init__(self,name,age,sex)         # 指名道姓的写法
       # super(Staff, self).__init__(name,age,sex)  # python2中super调用的写法
        super().__init__(name,age,sex)              # Python3中super调用的写法
        self.id = id

    def sell(self):
        print(f"{self.name}正在卖蟹堡")

S1 = Staff("派大星",22,"man",1)

print(S1.__dict__)  # {'name': '派大星', 'age': 22, 'sex': 'man', 'id': 1}

三.关于 super 调用父类方法的查找顺序

class F1:
    def s1(self):
        print('F1:s1')
    def s2(self):
        print('F1:s2')

class F2(F1):
    # def s1(self):
    #     print('F2:s1')
    def s2(self):
        print('F2:s2')

class F4():
    def s1(self):
        print('F4:s1')
    def s2(self):
        print('F4:s2')

class F3(F2,F4):
    def s1(self):
        super().s1()  # 调用父类的s1方法,到底使用了哪个父类的s1
    def s2(self):
        print('F3:s2')


f1 = F3()
f1.s1()  # F1:s1
# 这个示例, 子类最终没有继承同一个父类, 所以是非菱形结构, 每一条路都会找到底
# 查找顺序 : F3 ===> F2 ===> F1

super 总结

  • super( ) 代指父类对象, 严格按照 mro 列表查找
  • super( ).[属性] : 从父类开始找, 并且是按照 mro 列表查找
  • self.[属性] : 先从自己找, 自己没有就按照 mro 列表找

ps : 上面这个示例不懂不要紧, 可查看菱形继承问题之后再来思考

关于多继承的调用涉及到菱形继承问题, 有不同的情况讨论菱形继承问题

原文地址:https://www.cnblogs.com/songhaixing/p/14183260.html