共计 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 : 上面这个示例不懂不要紧, 可查看菱形继承问题之后再来思考
关于多继承的调用涉及到菱形继承问题, 有不同的情况讨论👉菱形继承问题
正文完