面向对象之继承
今日学习总结:
一、继承介绍:
注意:在python中,一个子类可以继承多个父类。在其他语言中,一个子类只能继承一个父类。
1.什么是继承:
继承是一种新建类的方式,新建的类称之为子类或派生类,继承的父类,称之为基类或超类。
2. 继承的作用:减少代码的冗余
3.如何实现继承:
先确认谁是子类,谁是父类(面试)
在定义子类时:子类名(父类名)
4.如何寻找继承关系:
- 胡晨阳对象 ---> 人子类 ---> 动物父类
- 猪坚强对象 ---> 猪子类 ---> 动物父类
- 哈士奇对象 ---> 狗子类 ---> 动物父类
01.确认谁是子类:
人、猪、狗都是子类
02.确认谁是父类:
动物类是父类
03.要先抽象,再继承
抽取对象之间相似的部分,总结出类
抽出类之间相似的部分,总结出父类
例子:
子类._ _bases_ _ #表示查看父类
class Father1: x=1 pass class Father2: x=10 pass class Father3: x=100 pass #子类 class Boby(Father1,Father2,Father3): pass print(Boby.x) #1 print(Boby.__bases__) #(<class '__main__.Father1'>, <class '__main__.Father2'>, <class '__main__.Father3'>)
二、在继承背景下,对象属性的查找顺序:
1.先从对象自己的名称空间中查找
2.对象中没有,从子类的名称空间中查找
3.子类中没有,从父类的名称空间中查找
ps:程序的执行顺序是由上到下,父类必须定义在子类之上
对象添加属性的操作, 并不是修改子类的属性
class Goo: x=10 pass class Foo(Goo): x=100 pass foo_obj=Foo()
print('对象的名称空间',foo_obj.__dict__)
print('子类的名称空间',Foo.__dict__)
print('父类的名称空间',Goo.__dict__)
foo_obj.x=10000 print(foo_obj.x) #先从对象自己的名称空间中找。 print('对象的名称空间',foo_obj.__dict__) print('子类的名称空间',Foo.__dict__) print('父类的名称空间',Goo.__dict__) 结果: 10000 对象的名称空间 {'x': 10000} 子类的名称空间 {'__module__': '__main__', 'x': 100, '__doc__': None} 父类的名称空间 {'__module__': '__main__', 'x': 10, '__dict__': <attribute '__dict__' of 'Goo' objects>, '__weakref__': <attribute '__weakref__' of 'Goo' objects>, '__doc__': None}
三、派生:
指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法。
若子类中的方法名与父类的相同,优先用子类的
派生后继承关系查找验证:
class Foo: def f1(self): print('from Foo.f1...') def f2(self): print('from Foo.f2...') self.f1() # 子类 class Bar(Foo): # 重写 def f1(self): print('from Bar.f1..') def func(self): print('from Bar.func...') bar_obj = Bar() #调用子类产生一个 子类的对象 bar_obj.f2() #调用属性 f2(),因为 子类对象和子类中都没有,所以去父类找,就执行函数 f2(), #在调用函数f2()时候,把子类对象本身bar_obj当成参数传入给了self 结果: from Foo.f2... from Bar.f1..
四、子类继承父类并重用父类的属性与方法
子类继承父类,派生出自己的属性与方法,并且重用父类的属性与方法。是指:子类重新定义一份与父类相同的方法,重用父类的功能,并且派生出自己的属性或方法。
解决问题: 子类重用父类的属性,并派生出新的属性。
两种方式:
方法1.直接引用父类的__init__为其传参,并添加子类的属性。
方法2.通过super来指向父类的属性。
super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。
注意: 使用哪一种都可以,但不能两种方式混合使用。
例子:方法1
class OldboyPeople: school = 'oldboy' def __init__(self, name, age, sex): # self == tea1, name == 'tank', age == 17, sex == 'male' self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): def __init__(self, name, age, sex, sal): OldboyPeople.__init__(self, name, age, sex) #用_ _init_ _来调用父类的属性 重用了父类的属性或方法 self.sal = sal def change_score(self): print(f'老师 {self.name} 修改分数...') class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, girl): OldboyPeople.__init__(self, name, age, sex) self.girl = girl def choose_course(self): print(f'学生 {self.name} 选择课程...') tea1 = OldboyTeacher('tank', 17, 'male', 15000000) print(tea1.name, tea1.age, tea1.sex, tea1.sal) stu1 = OldboyStudent('姚玉鑫', 28, 'male', '凤姐') print(stu1.name, stu1.age, stu1.sex, stu1.girl)
例子:方法2class OldboyPeople:
school = 'oldboy' # self == tea1 def __init__(self, name, age, sex): # self == tea1, name == 'tank', age == 17, sex == 'male' self.name = name self.age = age self.sex = sex class OldboyTeacher(OldboyPeople): def __init__(self, name, age, sex, sal): super().__init__(name, age, sex) # super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间。 self.sal = sal def change_score(self): print(f'老师 {self.name} 修改分数...') class OldboyStudent(OldboyPeople): def __init__(self, name, age, sex, girl): super().__init__(name, age, sex) # super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间
self.girl = girl def choose_course(self): print(f'学生 {self.name} 选择课程...') tea1 = OldboyTeacher('tank', 17, 'male', 15000000) print(tea1.name, tea1.age, tea1.sex, tea1.sal) stu1 = OldboyStudent('姚玉鑫', 28, 'male', '凤姐') print(stu1.name, stu1.age, stu1.sex, stu1.girl)
五、经典类与新式类: (了解)
- 工作中遇不到
- 面试有可能会问
- 新式类:
1.凡是继承object的类或子孙类都是新式类。
2.在python3中所有的类都默认继承object。
- 经典类:
1.在python2中才会有经典类与新式类之分。
2.在python2中,凡是没有继承object的类,都是经典类。
class User(object): pass class Sub(User): pass print(User.__dict__) #查看 User类的名称空间 print(object) 结果: {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None} <class 'object'> class User: pass class Sub(User): pass print(User.__dict__) #查看 User类的名称空间 print(object) 结果: {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'User' objects>, '__weakref__': <attribute '__weakref__' of 'User' objects>, '__doc__': None} <class 'object'>
六、super()会严格按照mro列表的顺序往后查找
在python3中提供了一个查找新式类查找顺序的内置方法.
mro(): 会把当前类的继承关系列出来。
mro列表格式: 类名.mro()
class Father1: # x = 10 pass class Father2: # x = 20 pass # 多继承的情况下: 从左到右 class Sub(Father1, Father2): pass print(Sub.mro()) #调用mro返回的是一个继承序列 结果: [<class '__main__.Sub'>, <class '__main__.Father1'>, <class '__main__.Father2'>, <class 'object'>]
例子:
class A: def test(self): print('from A.test') class B: def test(self): print('from B.test') class C(A, B): pass c = C() print(C.mro()) 结果: [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
七、多继承情况下造成 “钻石继承”
mro的查找顺序:
- 新式类:- 广度优先
- 经典类:- 深度优先
新式类:
class A(object): def test(self): print('from A') class B(A): def test(self): print('from B') class C(A): def test(self): print('from C') class D(B): def test(self): print('from D') pass class E(C): def test(self): print('from E') pass class F(D, E): def test(self): print('from F') pass # F-->D-->B-->E-->C-->A-->object print(F.mro()) obj = F() obj.test() 结果: [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] from F

浙公网安备 33010602011771号