Python-面向对象之继承
面向对象阶段最重要的知识点:
面向对象的三大特性
- 继承(组合)
- 封装
- 多态
继承(灵魂三拷问)
什么是继承?
继承指的是定义类的方法,新定义的类称之为子类或者派生类
子类继承的类叫做父类,也叫基类/超类
继承的特性:
- 子类可以继承父类的属性(特征与技能)
- 并且可以派生出自己的属性(特征和技能)
- 在python中一个子类可以继承多个父类,其他语言只能继承一个父类
为什么要继承
减少代码的冗余(减少重复代码)。
如何实现继承
-
首先确定好谁是子类,谁是父类
-
在定义类时,子类(),()内写上父类名,实现继承
继承初体验:
# 父类1 class ParentClass1: pass # 父类2 class ParentClass2: pass # 子类1 class SubClass1(ParentClass1): # 继承父类1 pass # 子类2 class SubClass2(ParentClass1,ParentClass2): # 继承父类1,父类2 pass # 查看继承的父类:__bases__是类的属性,用来查找当前类的父类 print(SubClass1.__bases__) # (<class '__main__.ParentClass1'>,) print(SubClass2.__bases__) # (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
寻找继承关系
如何寻找继承关系
要想寻找继承关系,首先要“先抽象,在继承”
抽象与继承
先抽象
抽象是一种思想
奥巴马 --> 属于人类 --> 属于动物类
哈士奇 --> 属于狗类 --> 属于动物类
把相同的属性(特征和技能)抽象出来,定义动物类,称之为父类。
动物类:
特征:
眼睛、鼻子、耳朵
技能:
吃、喝、拉、撒
再继承
继承在程序中实现
奥巴马 (对象)--> 调用人类 --> 继承动物类
哈士奇 (对象)--> 调用狗类 --> 继承动物类
继承的关系
- 对象是特征与技能的结合体.
- 类是一系列对象相同的特征与技能的结合体.
- 继承是一系列类相同的特征与技能的结合体.
上代码
# 父类
class OldboyPeople:
# 定义相同的属性
school = "oldboy"
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
class OldboyTeacher(OldboyPeople):
def change_score(self):
print(f"老师{self.name} 修改分数")
class OldboyStudent(OldboyPeople):
def choose_course(self):
print(f"学生{self.name}选择课程")
stu1 = OldboyStudent("qinyj",18,"female")
tea1 = OldboyTeacher("tank",18,"female")
print(stu1.name,stu1.age,stu1.sex)
print(tea1.name,tea1.age,tea1.sex)
继承下对象属性查找顺序
在继承背景下,对象属性的查找顺序为:
-
对象查找属性会先从对象的名称呢过空间中查找
-
若对象中没有,则会去类里面查找
-
若当前子类里面有就返回,如果没有会去父类里面找
注意:若子类定义和父类相同的属性,会优先使用子类的。
# 验证对象属性的查找顺序:
class Foo:
def f1(self):
print("Foo.f1")
def f2(self):
print("Foo.f2")
self.f1()
class Soo(Foo):
def f1(self):
print("Soo.f1")
s = Soo()
s.f2()
# Foo.f2
# Soo.f1
# Soo-->Foo 在Soo类中重新定义了f1方法,此时优先使用子类中的f1方法,这时候对象去子类的名称空间里找就会找到,打印Soo.f1
# 查看对象名称空间 __dict__
print(s.__dict__)
# __class__:查看对象的属性,查看当前对象的类
print(s.__class__) # <class '__main__.Soo'>
# 查看子类名称空间
print(s.__class__.__dict__)
# {'__module__': '__main__', 'f1': <function Soo.f1 at 0x0000000009FF3BF8>, '__doc__': None}
# 查看父类的名称空间
# __bases__:查看继承的父类
print(s.__class__.__bases__[0].__dict__)
# {'__module__': '__main__', 'f1': <function Foo.f1 at 0x0000000009FF3B70>, 'f2': <function Foo.f2 at 0x0000000009FF3BF8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
派生
什么是派生
- 派生指的是子类继承父类的属性,并且派生出新的属性
- 子类派生出新的属性,若与父类属性相同,则调用使用子类的
- 继承指的是类与类的关系,子类与父类是从属的关系
子类派生新的属性并重用父类的属性
方式一:
直接调用父类的的__init__(self)
方法,把__init__(self)
当做普通函数使用,传入对象与继承的属性
方式二:
使用super函数,super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过“.” 指向父类的名称空间,将本身传入__init__(self)
函数当中的一个参数
注意:两种方式不要混合使用。
class OldboyPeople:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
# 方式一:
# 直接调用父类的__init__(self)函数
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level = level
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex,course):
OldboyPeople.__init__(self, name, age, sex)
self.course = course
stu1 = OldboyStudent("qinyj",18,"man","python")
tea1 = OldboyTeacher("tank",18,"man","10")
print(tea1.name,tea1.level)
print(stu1.name,stu1.course)
# 方式二:
# 使用super()函数
class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
super().__init__(name,age,sex)
self.level = level
class OldboyStudent(OldboyPeople):
def __init__(self,name,age,sex,course):
super().__init__(name, age, sex)
self.course = course
stu1 = OldboyStudent("qinyj",18,"man","python")
tea1 = OldboyTeacher("tank",18,"man","10")
print(tea1.name,tea1.level)
print(stu1.name,stu1.course)
新式类与经典类
继承了 object 类的是新式类,没有继承的是经典类
新式类:python3中都是新式类,在python3中默认继承object类
经典类:python2中凡是没有继承object类的都是经典类
mro函数
mro函数属于object类,在多继承情况下,用来查看当前类的继承顺序的
class A:
x = 2
pass
class B:
x = 3
pass
class C(A,B):
x = 1
pass
# mro函数
print(C.mro())
# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
# 继承顺序:
# 先在自己类中找--》A--》B--》object
c = C()
print(c.x)
钻石继承(菱形继承)
在多继承的情况下形成的钻石继承
针对于新式类和经典类而言:
经典类:深度优先
新式类:广度优先
验证:
# coding=utf-8
### 新式类继承
# 继承顺序:F-D->B->E->->C->A->object-->若没有报错
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 = F()
f.test()
### 经典类继承
# 继承顺序:F->D->B->A->E->C-->若没有报错
class A:
# 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 = F()
f.test()
实战-通过继承实现修改json模块支持的数据类型
import json
from datetime import datetime,date
print(json.JSONEncoder)
'''
json 支持的python数据类型
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
'''
print(datetime.today())
print(date.today())
# 开发者的角度来说,我们想要把date.today() 或者datetime.today() 当成json 的value值保存为json文件
# 但json数据类型只支持字符串,那么我们可以这么做,将执行结果强转为str
dic = {
"name":"qinyj",
"today":str(date.today())
}
print(json.dumps(dic)) # {"name": "qinyj", "today": "2019-10-10"}
# 我们从源码角度来看,有可能以后会自己修改源码
class MyJson(json.JSONEncoder):
# 子类重新派生出来default功能,优先用子类的
def default(self, o):
if isinstance(o,date):
return o.strftime("%Y-%m-%d %X")
else:
# 不满足条件还是继承父类的default方法的功能
return super().default(self,o)
dic = {
"name":"qinyj",
"today":date.today()
}
# isinstance() :判断一个对象是否是一个已知的类型
print(isinstance(dic,date))
print(isinstance(dic.get("today"),date))
print(json.dumps(dic,cls=MyJson)) # 默认cls=None,默认指向的是原json的JSONEncoder
# {"name": "qinyj", "today": "2019-10-10 00:00:00"}
小结
1.什么是继承?
继承指的是新建类的方法, 新建的类称之为子类或者派生类,子类继承的类叫做父类,也称之为基类或超类.
继承的特征:
子类可以继承父类的属性(特征与技能), 并且可以派生出自己的属性(特征与技能).
2.继承的目的:
继承的目的是为了减少代码冗余(减少重复代码).
3.什么是抽象?
抽象指的是抽取相似的部分,称之为抽象.
4.继承的关系:
对象是特征与技能的结合体.
类是一系列对象相同的特征与技能的结合体.
继承是一系列类相同的特征与技能的结合体.
5.在继承背景下,对象属性的查找顺序:
1.对象查找属性会先从对象的名称空间中查找.
2.若对象没有,则会去类里面找.
3.若当前类是子类,并且没有对象找的属性,会去父类中查找
6.什么是派生?
派生指的是子类继承父类的属性,并且派生出新的属性.(*****)
子类派生出新的属性,若与父类的属性相同,则以子类的为准.
继承是谁与谁的关系, 指的是类与类的关系,子类与父类是从属关系.
7.子类派生出新的属性,并重用父类的属性:
- 直接通过 父类.(调用)__init__,把__init__当做普通函数使用,传入对象与继承的属性.
- super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,
8.什么经典类与新式类:
继承object的类都称之为新式类.
在python2中,凡是没有继承object的类都是经典类.
9.在多继承的情况下形成的钻石继承 (继承顺序)
- 经典类:
深度优先
- 新式类:
广度优先