继承

什么是继承?(What)

  • 继承指的是新建类的方法,新建的类称之为子类或派生类,子类继承的类叫做父类,也称之为基类或超类
  • 继承的特征:子类可以继承父类的属性(特征与技能),并且可以派生出自己的属性(特征与技能)

为什么要继承?(What)

  • 较少代码冗余

如何实现继承(How)

  • 首先确定好谁是子类,谁是父类
  • 在定义类时, 子类 + () , () 内写父类,实现继承

查看继承的父类

class ParentClass1:
    pass

class ParentClass2:
    pass

class SubClass1(ParentClass1):
    pass

print(SubClass1.__bases__)	# __bases__ 是类的属性,用来查找当前类的父类

class SubClass2(ParentClass1,ParentClass2):	#在python中一个子类可以继承多个父类
    pass

print(SubClass2.__bases__)

# __bases__ 是类的属性,用来查找当前类的父类
#在python中一个子类可以继承多个父类---python中独有,其他语言一个子类只继承一个父类,面试会问
'''
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)
'''

寻找继承关系

如何寻找继承关系

  • 先抽象,再继承

什么是抽象

  • 抽取相似的部分
    • 奥巴马-->人类-->动物类
    • 麦兜-->猪类-->动物类
    • 小丁丁-->狗类-->动物类
  • 抽象定义动物类,称之为父类
    • 特征:
      • 眼睛,鼻子,...
    • 技能
      • 吃,喝...
  • 继承
    • 奥巴马对象<--调用人类<--继承动物类
    • 麦兜对象<--调用猪类<--继承动物类
    • 小丁丁对象<--调用狗类<--继承动物类

继承关系

  • 对象是特征与技能的结合体
  • 类是一系列对象中相同的特征与技能的结合体
  • 继承是一系类中相同的特征与技能的结合体

在继承关系下对象属性的查找顺序

  1. 先从对象名称空间查找
  2. 没有再从对象所属子类查找
  3. ,没有最后从父类中查找

在某一层找到就不会继续寻找

# 父类
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} 修改分数...]')


teacher = OldboyTeacher('tank', 17, 'male')

print(teacher.__class__)    #查看对象所属的类
print(teacher.__class__.__dict__)   #查看对象所属类的名称空间

print(teacher.__class__.__bases__)  #查看对象所属的类的父类
print(teacher.__class__.__bases__[0].__dict__)  #查看对象所属的类的第一个父类的名称空间

print(id(teacher.school))
print(id(OldboyPeople.school))
'''
<class '__main__.OldboyTeacher'>

{'__module__': '__main__', 'change_score': <function OldboyTeacher.change_score at 0x00000185B7F9EB70>, '__doc__': None}

(<class '__main__.OldboyPeople'>,)

{'__module__': '__main__', 'school': 'oldboy', '__init__': <function OldboyPeople.__init__ at 0x00000185B7F9EAE8>, '__dict__': <attribute '__dict__' of 'OldboyPeople' objects>, '__weakref__': <attribute '__weakref__' of 'OldboyPeople' objects>, '__doc__': None}

2219246848072
2219246848072
'''

派生

  • 派生指的是子类继承父类的属性并且派生出新的属性
  • 子类派生出新的属性,若与父类的属性相同,则以子类为准
  • 继承指的是子类与父类之间的从属关系

子类派生出新的属性并重用父类

方式一

#通过 父类.__init__ ,把 __init__ 当做普通函数使用,传入对象与继承的函数

# 父类
class OldboyPeople:
    school = 'oldboy'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

# 子类
class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        OldboyPeople.__init__(self,name,age,sex)
        self.level = level
        self.salary = salary

teacher = OldboyTeacher('tank',18,'male',9,'3.0w')
print(teacher.__dict__)

'''
{'name': 'tank', 'age': 18, 'sex': 'male', 'level': 9, 'salary': '3.0w'}
'''

方式二

#super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,该对象通过 "." 指向父类的名称空间

# 父类
class OldboyPeople:
    school = 'oldboy'

    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

# 子类
class OldboyTeacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        super().__init__(name,age,sex)
        self.level = level
        self.salary = salary

teacher = OldboyTeacher('tank',18,'male',9,'3.0w')
print(teacher.__dict__)

'''
{'name': 'tank', 'age': 18, 'sex': 'male', 'level': 9, 'salary': '3.0w'}
'''

super类的使用方法---super(type,object)与super()

super(type,object)

#super(type,object),type作用是定义在__mro__数组中的那个位置开始找,obj定义的是用哪个类的__mro__元素

class A(object):
    def fun(self):
        print('A.fun')

class B(object):
    def fun(self):
        print('B.fun')

class C(object):
    def fun(self):
        print('C.fun')

class D(A,B):
    def fun(self):
        print('D.fun')

class E(B, C):
    def fun(self):
        print('E.fun')

class F(D, E):
    def fun(self):
        print('F.fun')

print(F.mro())
print(B.mro())
super(B , B()).fun()	#会报错

'''
AttributeError: 'super' object has no attribute 'fun'

[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]

[<class '__main__.B'>, <class 'object'>]
'''

super

super()事实上是懒人版的super(type , obj),这种方式只能用在类体内部,Python会自动把两个参数填充上,type指代当前类,obj指导当前类的实例对象,相当于super(class , self)

新式类与经典类

在python2中,才会有新式类与经典类之分,在python3中,所有类都是新式类---面试会问

新式类

  • 继承object的类都称之为新式类
  • python3中所有的类默认继承object
class Foo(object):
    pass

print(Foo.__bases__)

class Foo:
    pass

print(Foo.__bases__)

'''
(<class 'object'>,)
(<class 'object'>,)
'''

经典类

在python2中,凡是没有继承object的类都是经典类

#python2
'''
>>> class Foo(object):
...     pass
...
>>> Foo.__bases__
(<type 'object'>,)
>>> class Foo:
...     pass
...
>>> Foo.__bases__
()
'''

mro()---Method Resolution Order (方法解析顺序)-->属于object-->属于type

python内置的函数用来查看在多继承情况下,当前类的继承顺序

class A:
    x = 'A'
    pass

class B:
    x = 'B'
    pass

class C(A,B):
    pass

print(C.x)
print(C.__mro__)
print(C.mro())

'''
A
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
'''

菱形继承

在多继承的情况下形成(继承顺序)

  • 经典类
    • 深度优先
  • 新式类
    • 广度优先

验证

# python2
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

f1 = F()
f1.test()

#经典类(深度优先): F-->D-->B-->A-->E-->C
#新式类(广度优先): F-->D-->B-->E-->C-->A

通过继承机制修改json数据格式

import json
from datetime import date,datetime

print(date.today())
print(datetime.today())
print(type(date.today()))
print(type(datetime.today()))
print('*'*100)

dic = {'name':'蔡启龙','date':date.today(),'today':datetime.today()}
# print(json.dumps(dic))  #TypeError: Object of type 'date' is not JSON serializable

#开发者角度:直接转成str

#开源者角度:修改源码
#isinstance:python,可以穿两个参数,判断参数一是否是参数二的一个实例

a = date(2019,10,11)
print(type(a))
b = a.strftime('%Y-%m-%d %X')
print(b)
print(type(b))
print('*'*100)

class MyJson(json.JSONEncoder):
    def default(self, o):
        if isinstance(o,type(date.today())):
            return o.strftime('%Y-%m-%d %X')
        if isinstance(o,type(datetime.today())):
            return o.strftime('%Y-%m-%d %X')

        #继承父类的default方法的功能
        return super().default(o)

res = json.dumps(dic,cls=MyJson)
print(res)
print('*'*100)

print(json.loads(res))

'''
2019-10-11
2019-10-11 00:20:52.540284
<class 'datetime.date'>
<class 'datetime.datetime'>
****************************************************************************************************
<class 'datetime.date'>
2019-10-11 00:00:00
<class 'str'>
****************************************************************************************************
{"name": "\u8521\u542f\u9f99", "date": "2019-10-11 00:00:00", "today": "2019-10-11 00:20:52"}
****************************************************************************************************
{'name': '蔡启龙', 'date': '2019-10-11 00:00:00', 'today': '2019-10-11 00:20:52'}
'''