面向对象三大特性之继承
面向对象三大特性之继承
1、什么是继承
继承是一种新建类的方式,新建的类称为子类或者派生类,被继承的类称为父类、基类或者超类
在python中,一个子类可以继承多个父类,其他语言中,一个子类只能继承一个父类
2、继承的作用
减少类与类之间的代码冗余
子类可以继承父类的所有属性,因此继承可以解决类与类之间的代码冗余问题
3、如何实现继承
- 先确定谁是子类,谁是父类
- 在定义子类时语法:子类(父类):
子类可以通过 子类.__ bases__ 查看父类
4、如何寻找继承关系
确定谁是子类
确定谁是父类,先抽象再继承
抽象总结出对象之间相似的部分,再总结出类
抽象总结类之间相似的部分,再总结出父类
# 解决代码冗余
# 父类
class People:
country = 'China'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
# 子类1
class Student(People):
def learn(self):
return '学习技能'
# 子类2
class Teacher(People):
def teach(self):
return '教书技能'
# 子类可以继承父类的属性
stu_obj = Student('shen', 18, 'male')
print(stu_obj.name, stu_obj.age, stu_obj.sex, stu_obj.country, stu_obj.learn()) # shen 18 male China 学习技能
tea_obj = Teacher('wang', 24, 'female')
print(tea_obj.name, tea_obj.age, tea_obj.sex, tea_obj.country, tea_obj.teach()) # wang 24 female China 教书技能
5、在继承的背景下,对象属性的查找顺序
程序的执行是从上往下执行的,父类必须定义在子类上面
在继承的背景下,对象的属性查找顺序:
- 先从对象自身的名称空间中查找
- 再去子类的名称空间中查找
- 再从父类的名称空间中查找,最后从object中找,若没有会报错
python3中只有新式类,广度优先查找:继承多个父类时一直到继承object的类(根父类)不会从中找会回头找,第二个继承的父类。。。
python2中有经典类和新式类,深度优先查找:一直找到继承object的类(根父类)从中找不到才会找继承的第二个父类。。。
# 父类
class Foo:
x = 10
#子类
class Goo(Foo):
x = 20
pass
obj_goo = Goo()
# 注意此处的x是通过对象来添加属性,是给对象添加属性,不是修改子类的属性
obj_goo.x = 30
print(obj_goo.x)
6、派生
指的是子类继承父类的属性与方法,并且派生出自己独有的属性与方法
若子类中的方法与父类的相同,优先用子类的
# 父类
class Foo:
def f1(self):
print('from Foof1...')
def f2(self):
print('from Foof2...')
# 注意此处查找的f1应该先优先从子类找,最后从父类找
self.f1()
# 子类
class Goo(Foo):
def f1(self):
print('from Goof1...')
goo_obj = Goo()
goo_obj.f2()
# from Foof2...
# from Goof1...
7、子类继承父类并重用属性与方法
方式一:
直接引用父类的__ init__(self)为其传参,并添加子类的属性
方式二:
通过super来指向父类的属性,用super().init()为其传参添加子类的属性
super()是一个特殊类,调用super得到一个对象,该对象指向父类的名称空间
注意:使用哪一种方式都可以,但是不要混用
# 需求:学生有女朋友属性,老师有薪资属性
# 父类
class Foo:
country = 'China'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
# 子类
class Student(Foo):
def __init__(self, name, age, sex, gril):
# 方式一:直接引用父类的,需要传入self和其他参数
Foo.__init__(self, name, age, sex)
self.gril = gril
class Teacher(Foo):
def __init__(self, name, age, sex, sal):
# 方式二:对象本身当做self传入,不用super不要传入self,传入其他参数
super().__init__(name, age, sex)
self.sal = sal
stu_obj = Student('shen', 18, 'male', 'gril')
print(stu_obj.country, stu_obj.name, stu_obj.age, stu_obj.sex, stu_obj.gril)
# China shen 18 male gril
tea_obj = Teacher('wang', 24, 'female', 200000)
print(tea_obj.country, tea_obj.name, tea_obj.age, tea_obj.sex, tea_obj.sal)
# China wang 24 female 200000
8、经典类与新式类
新式类:
1. 凡是继承了object的类或子孙类都是新式类
2.在python3中所有的类都默认继承了object
经典类:
1. 在python2中才会有经典类与新式类之分
2.在python2中,凡是没有继承object的类,都是经典类
# python3中
class Foo: # 默认继承了object(Foo(object):)
x = 1
pass
class Goo(Foo):
pass
print(Foo.__dict__)
print(Goo.__dict__)
# {'__module__': '__main__', 'x': 1, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
# {'__module__': '__main__', '__doc__': None}
9、mro继承序列
python3中提供了一个查找新式类查找顺序的内置方法,通过类调用mro()得到的返回结果是一个继承序列
super的继承顺序严格按照mro()继承序列,会基于当前位置按照mro序列顺序继续往后查找
class Foo1:
def res(self):
print('res from Foo1')
# super会按照mro中序列基于当前位置继续往后查找
super().res()
class Foo2:
def res(self):
print('res from Foo2')
class C(Foo1, Foo2):
pass
obj_c = C()
print(C.mro()) # [<class '__main__.C'>, <class '__main__.Foo1'>, <class '__main__.Foo2'>, <class 'object'>]
obj_c.res()
#res from Foo1
# res from Foo2
10、钻石继承(菱形继承)
多继承情况下的菱形继承
mro的查找顺序:
新式类:广度优先
经典类:深度优先
新式类的广度优先:
# 新式类:
class A(object):
def test(self):
print('from A')
pass
class B(A):
# def test(self):
# print('from B')
pass
class C(A):
# def test(self):
# print('from C')
pass
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()) #[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj = F()
obj.test()
经典类的深度继承:
11、继承json模块,并派生出新功能
json模块正常无法存入类,转成json数据格式的,先如果需要存入一个类的数据则有两种方式
import json
from datetime import datetime
dic = {'time': datetime.now()}
print(type(datetime.now())) # <class 'datetime.datetime'>
# datetime.now()是一个类<class 'datetime.datetime'>正常无法转换存成json数据格式的
json.dumps(dic) # TypeError: Object of type datetime is not JSON serializable
方法一:将类转换成字符串存入
import json
from datetime import datetime
# 方法一:转换成字符串存入
dic = {'time': str(datetime.now())}
res = json.dumps(dic)
print(res) # {"time": "2019-11-27 14:38:01.079162"}
方法二:继承json模块派生出新功能来存取
import json
from datetime import datetime
# 方法二:继承json模块派生出新功能来存取
class MyJson(json.JSONEncoder):
def default(self, o):
# isinstance: 判断一个对象是否是一个类的实例
if isinstance(o, datetime):
return datetime.strftime(o, '%Y-%m-%d %X')
else:
return super().default(self, o)
dic = {'time': datetime.now()}
# cls=自定义的类
res = json.dumps(dic, cls=MyJson)
print(res) # {"time": "2019-11-27 14:38:01"}