文章目录
一、继承介绍
1.什么是继承?
-
继承是一种新建类的方式, 新建的类可以继承一个或多个父类, 那么这时这个新建的类就被称之为子类或派生类, 父类又被成之为基类或超类,
-
继承的特性:子类会遗传父类的属性
-
继承是类与类之间的关系
-
注意: python支持多继承, 在python中新创建的类可以继承一个或多个父类(补充: python中支持多继承, 其它大部分语言都不支持.)
类(对象)的继承基本语法格式
-
python中一切皆对象,类也是对象
-
python中支持一个类同时继承多个父类(多继承)
class Parent1(object): x = 1111 class Parent2(object): pass class Sub1(Parent1): # 单继承 pass class Sub2(Parent1, Parent2): # 多继承 pass
-
查看当前类的所继承的父类: 通过内置属性
类名.__bases__
可以查看类继承的所有父类print(Sub1.__bases__) # (<class '__main__.Parent1'>,) print(Sub2.__bases__) # (<class '__main__.Parent1'>, <class '__main__.Parent2'>) print(Sub2.x) # 1111
-
补充宝典:python为类内置的特殊属性
类名.__name__ # 类的名字(字符串) 类名.__doc__ # 类的文档字符串 类名.__base__ # 类的第一个父类 类名.__bases__ # 类所有父类构成的元组,仅查看直接父类。 类名.__dict__ # 类的属性字典 类名.__module__ # 类定义所在的模块 类名.__class__ # 实例对应的类(仅新式类中)
2.为什么要用继承?
- 继承的作用:用来解决类与类之间代码冗余问题
- 类的作用:用来解决对象与对象之间代码冗余问题
3.继承的实现
- 不使用继承时编写多个类,类与类之间存在冗余问题
class Animal:
def run(self):
print("奔跑")
def eat(self):
print("吃东西")
class Duck:
def run(self):
print("奔跑")
def eat(self):
print("吃东西")
def speak(self):
print("嘎嘎嘎")
class Pig:
def run(self):
print("奔跑")
def eat(self):
print("吃东西")
def speak(self):
print("咕咕咕")
class Person:
def run(self):
print("奔跑")
def eat(self):
print("吃东西")
def speak(self):
print("呵呵呵")
-
使用使用继承编写多个类,解决类与类之间的冗余问题
# 可以明显感觉到代码量减少 class Animal: def run(self): print("奔跑") def eat(self): print("吃东西") class Duck(Animal): def speak(self): print("嘎嘎嘎") class Pig(Animal): def speak(self): print("咕咕咕") class Person(Animal): def speak(self): print("呵呵呵")
4.python中的多继承
在平时应用中:
不建议使用多继承, 有可能会引发可恶的菱形问题. 如果真的涉及到一个子类不可避免地重用多个父类的属性, 也不应该直接使用多继承, 而是应该使用Mixins
机制
优点:
- 子类可以同时遗传多个父类的属性, 最大限度的解决类与类之间的代码重用问题
缺点:
继承应该表达的是一种什么"是"什么的关系. 违背了这种思维逻辑就会引发下面的问题.
- 多继承违背了人的思维逻辑习惯
- 代码的可读性会变差
- 扩展性变差(可读性和扩展性是相辅相成的)
二、属性查找顺序
1.单继承背景下的属性查找
- 对象的属性 (attribute) : 我们一般是指对象的属性和方法
- 多层继承关系的属性查找顺序, 永远是从最下层开始找,调用自己
self.[属性]
- 以下示例的数查找顺序 : 对象自己----->对象的类----->父类----->父类…
- 强调:谁来调用,查找方式就以谁作为起始位置开始查找
ps : Ctrl + 鼠标右键 快速跳转到属性 / 方法位置, 有时并不准确
'''
由对象发起的属性查找: 会从对象自身的属性里检索, 没有则会按照对象的类"类名.mro()"规定的顺序依次找下去
由类发起的属性查找: 会按照当前类"类名.mro()"规定的顺序依次找下去
'''
# 示例一:
class Foo:
def f1(self):
print("FOO.f1")
def f2(self):
print("FOO.f2") # obj.f1() # "obj"来调用, 执行查找方式就以"obj"作为起始位置开始查找
self.f1()
class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2()
# 查找顺序: obj.f2 -> Bar.f2 -> Foo.f2 --> Foo中找到了f2 -> obj.f1 -> Bar.f1 -> Bar中找到了f1
'''输出结果
FOO.f2
Bar.f1
'''
# 示例二:
class Foo:
def f1(self):
print("FOO.f1")
def f2(self):
print("FOO.f2") #调用当前类中的f1
Foo.f1(self)
class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2()
'''输出结果
FOO.f2
FOO.f1
'''
# 示例三
class Foo:
def __f1(self): # _Foo__f1
print("FOO.f1")
def f2(self):
print("FOO.f2") # 调用当前类中的f1
self.__f1() # self.__f1
class Bar(Foo):
def __f1(self): # _Bar__f1
print("Bar.f1")
obj = Bar()
obj.f2()
'''输出结果
FOO.f2
FOO.f1
'''
2.多继承属性查找顺序
-
多继承存在属性重复问题, 多个父类中存在着相同的属性, 那么对象该选择哪个属性使用呢?
-
按照右边顺序查找 : 对象自己----->类----->父类 (多个父类,从左到右的先后顺序来找)
class Wo: def foo(self): print("哎呀!") class Yao: def foo(self): print("吃多了长肉") class Jian: def foo(self): print("少吃又不瘦") class Fei: def foo(self): print("我还是吃吧~~~") class People(Wo, Yao, Jian, Fei): # 继承了四个父类,查找顺序:从左到右 pass abandon = People() abandon.foo() # 哎呀! (找到的是最左边的)
三、新生类与经典类
1.新生类
- 继承了
object
的类以及该类的子类子子类, 都是新式类 (Python3中统一都是新式类) - 在 Python3 中如果一个类没有继承任何类, 则默认会继承
object
类, 也就是Python3中所有的类都是新式类
#在python3中
class Student(object): # Student就叫新式类
pass
class Teacher(): #默认继承"object"
pass
class School(Student, Teacher):
pass
# 查看一个类,继承了哪些父类
print(School.__bases__) # (<class '__main__.Student'>, <class '__main__.Teacher'>)
print(Teacher.__bases__) # (<class 'object'>,)
print(Student.__bases__) # (<class 'object'>,)
2.经典类
-
没有继承
object
的类以及该类的子类子子类, 都是经典类 (只有Python2中才区分新式类和经典类) -
在 Python2 中如果一个类没有继承任何类, 它不会继承
object
类, 所以Python2 中才有经典类# 如果在python2中 #coding:utf-8 #在python2中需要在最顶行指定字符编码 class Student(object): # 新式类 pass class Teacher(): # 经典类 pass #py2的语法 print Student.__bases__ #(<type 'object'>,) print Teacher.__bases__ #()
四、继承与抽象
- 继承描述的是子类与父类之间的关系,是一种什么是什么的关系
- 要找出这种关系,必须先抽象再继承,抽象即抽取类似或者说比较像的部分
1.抽象
- 将佩奇和乔治两对象比较像的部分抽取成类
- 将人,鸭,猪这三个类比较像的部分抽取成父类
- 抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
2.继承
- 基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构
继承应用:
class People:
school = "清华大学"
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
class Student(People):
def choose(self):
print(f"{self.name}正在选课~~~")
class Teacher(People):
def teacher(self):
print(f"I am {self.name} teacher")
# Teacher类内并没有定义__init__方法,但是会从父类中找到__init__,因而仍然可以正常实例化,如下
teacher1 = Teacher('淘小欣', 'male', 18)
print(teacher1.__dict__) # {'name': '淘小欣', 'sex': 'male', 'age': 18}