60 类的派生

467次阅读
没有评论

共计 2758 个字符,预计需要花费 7 分钟才能阅读完成。

一. 什么是派生

  • 子类中 新定义的属性 的这个过程就叫做派生
  • 注意 : 当子类在使用派生属性的时候始终以自己的属性为准
  • 父类 -----> 称为 基类 或者超类
  • 子类 -----> 称为 派生类
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 : 上面这个示例不懂不要紧, 可查看菱形继承问题之后再来思考

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

正文完
 
shawn
版权声明:本站原创文章,由 shawn 2023-06-16发表,共计2758字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)