面向对象阶段最重要的知识点:

面向对象的三大特性

  1. 继承(组合)
  2. 封装
  3. 多态

继承(灵魂三拷问)

什么是继承?

继承指的是定义类的方法,新定义的类称之为子类或者派生类

子类继承的类叫做父类,也叫基类/超类

继承的特性:

  • 子类可以继承父类的属性(特征与技能)
  • 并且可以派生出自己的属性(特征和技能)
  • 在python中一个子类可以继承多个父类,其他语言只能继承一个父类

为什么要继承

减少代码的冗余(减少重复代码)。

如何实现继承

  1. 首先确定好谁是子类,谁是父类

  2. 在定义类时,子类(),()内写上父类名,实现继承

    继承初体验:

    # 父类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)

继承下对象属性查找顺序

在继承背景下,对象属性的查找顺序为:

  1. 对象查找属性会先从对象的名称呢过空间中查找

  2. 若对象中没有,则会去类里面查找

  3. 若当前子类里面有就返回,如果没有会去父类里面找

    注意:若子类定义和父类相同的属性,会优先使用子类的。

# 验证对象属性的查找顺序:

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"}
02-13 15:06