一.什么是派生
- 子类中新定义的属性的这个过程就叫做派生
- 注意 : 当子类在使用派生属性的时候始终以自己的属性为准
- 父类----->称为基类或者超类
- 子类----->称为派生类
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 : 上面这个示例不懂不要紧, 可查看菱形继承问题之后再来思考
关于多继承的调用涉及到菱形继承问题, 有不同的情况讨论菱形继承问题