面向对象

1 面向过程与面向对象

1.1 面向过程编程与面向对象编程对比

  • 1、面向过程编程
    核心是过程二字,过程指的是解决的步骤,即先干啥、再干啥、后干啥
    基于该思想写程序就在设计一条条的流水线

    优点:复杂的问题流程化、进而简单化
    缺点:牵一发而动全身,扩展差

  • 2、面向对象编程
    核心是“对象”二字,对象指的是盛放相关的数据与功能的容器
    基于该思想编写程序就在创造一个个的容器来把相关的东西盛到一起

    优点:扩展性强
    缺点:加大了编程的复杂度

类是用来解决对象之间代码冗余问题的

2 类与对象

2.1 类定义阶段发生的三件事

  • 1、会执行类体的代码
  • 2、会产生一个类的名称空间,用来将类体代码运行过程中产生的名字都丢进去
  • 3、将名称空间的内存地址绑定给类名

    2.2 调用类阶段发生的三件事

  • 1、会创建空对象(调用__new__方法来创建的空对象)
  • 2、会自动触发类中__init__函数的运行,__init__(空对象,,”egon”,18,”male”),完成对象的初始化操作,注意:__init__不能有返回值
  • 3、返回该对象,赋值给stu1_dic
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    # 版本1---------------------------------------------
    stu1_name = "egon"
    stu1_age = 18
    stu1_gender = "male"
    stu1_courses = []

    stu2_name = "tom"
    stu2_age = 38
    stu2_gender = "female"
    stu2_courses = []

    def choose_course(stu_name,stu_courses,course):
    stu_courses.append(course)
    print('学生 %s 选课成功 %s' %(stu_name,stu_courses))

    choose_course(stu1_name,stu1_courses,"python+go开发")
    choose_course(stu2_name,stu2_courses,"linux运维")


    # # 版本2----------------------------------------------
    stu1_dic = {
    'name':"egon",
    "age":18,
    'gender':"male",
    "Course":[]
    }


    stu2_dic = {
    'name':"tom",
    "age":38,
    'gender':"female",
    "Course":[]
    }

    def choose_course(stu_dic,course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' %(stu_dic["name"],stu_dic["Course"]))


    choose_course(stu1_dic,"python+go开发")
    choose_course(stu2_dic,"linux运维")


    # 版本3
    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))



    stu1_dic = {
    "school": "SH",
    'name': "egon",
    "age": 18,
    'gender': "male",
    "Course": [],
    'choose_course': choose_course

    }

    stu2_dic = {
    "school": "SH",
    'name': "tom",
    "age": 38,
    'gender': "female",
    "Course": [],
    'choose_course': choose_course
    }

    stu1_dic['choose_course'](stu1_dic, "python+go开发") # 谁来调用就传自己

    stu2_dic["choose_course"](stu2_dic, "linux运维")


    # 版本4
    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))

    Student = {
    "school": "SH",
    'choose_course': choose_course
    }

    stu1_dic = {
    'name': "egon",
    "age": 18,
    'gender': "male",
    "Course": [],
    'Student':Student
    }

    stu2_dic = {
    'name': "tom",
    "age": 38,
    'gender': "female",
    "Course": [],
    'Student':Student
    }



    # 类是用来解决对象之间代码冗余问题的
    class Student:
    school = "SH"

    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))

    print(Student.__dict__)


    # 调用类
    stu1_dic = Student()
    stu2_dic = Student()

    print(stu1_dic.__dict__)
    print(stu2_dic.__dict__)

    print(stu1_dic.school) # stu1_dic.__dict__["school"]
    print(stu2_dic.school) # stu2_dic.__dict__["school"]


    # if "school" in stu1_dic.__dict__:
    # print(stu1_dic.__dict__["school"])
    # else:
    # print(Student.__dict__['school'])

    2.3 初始化构造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    # 版本1---------------------------------------------
    class Student:
    school = "SH"

    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))

    # 调用类
    stu1_dic = Student()
    stu2_dic = Student()

    stu1_dic.name = "egon" # stu1_dic.__dict__["name"] = "egon"
    stu1_dic.age = 18 # stu1_dic.__dict__["age"] = 18
    stu1_dic.gender = "male" # stu1_dic.__dict__["gender"] = "male"
    stu1_dic.courses = [] # stu1_dic.__dict__["Course"] = []


    stu2_dic.name = "tom"
    stu2_dic.age = 38
    stu2_dic.gender = "female"
    stu2_dic.courses = []


    print(stu1_dic.__dict__)
    print(stu2_dic.__dict__)
    print(Student.__dict__)

    print(stu1_dic.name)


    # 版本2:---------------------------------------------------------------
    class Student:
    school = "SH"

    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))

    # 类定义阶段发生的三件事
    # 1、会执行类体的代码
    # 2、会产生一个类的名称空间,用来将类体代码运行过程中产生的名字都丢进去
    # 3、将名称空间的内存地址绑定给类名


    # 调用类
    stu1_dic = Student()
    stu2_dic = Student()

    def init(stu_dic, name, age, gender, courses=None):
    if courses is None:
    courses = []
    stu_dic.name = name
    stu_dic.age = age
    stu_dic.gender = gender
    stu_dic.courses = courses

    init(stu1_dic,"egon",18,"male")
    init(stu2_dic,"tom",38,"female")

    print(stu1_dic.__dict__)
    print(stu2_dic.__dict__)
    print(Student.__dict__)


    # 版本3:-----------------------------------------------------------------
    class Student:
    school = "SH"
    # 空对象,,"egon",18,"male")
    def __init__(stu_dic, name, age, gender, courses=None):
    if courses is None:
    courses = []
    stu_dic.name = name
    stu_dic.age = age
    stu_dic.gender = gender
    stu_dic.courses = courses
    # return None

    def choose_course(stu_dic, course):
    stu_dic["Course"].append(course)
    print('学生 %s 选课成功 %s' % (stu_dic["name"], stu_dic["Course"]))


    stu1_dic = Student("egon",18,"male")
    stu2_dic = Student("tom",38,"female")


    print(stu1_dic.__dict__)
    print(stu2_dic.__dict__)
    print(Student.__dict__)

    2.4 属性查找

  • 类有两种属性*
  • 1、数据属性
  • 2、函数属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    class Student:
    school = "SH"

    def __init__(self, name, age, gender, courses=None):
    if courses is None:
    courses = []
    self.name = name
    self.age = age
    self.gender = gender
    self.courses = courses
    # return None

    def choose_course(self, course):
    self.courses.append(course)
    print('学生 %s 选课成功 %s' % (self.name,self.courses))

    def func(self):
    pass

    stu1_obj = Student("egon",18,"male")
    stu2_obj = Student("tom",38,"female")

    # 类中有两种属性--------------------------------------
    # 1、数据属性
    print(Student.school)

    # 2、函数属性
    print(Student.choose_course)
    Student.choose_course(stu1_obj,"python+go开发")

    # 但是类中的属性其实是为对象准备----------------------------------
    # 1、类的属性是直接共享给所有对象用的
    print(Student.school,id(Student.school))
    print(stu1_obj.school,id(stu1_obj.school))
    print(stu2_obj.school,id(stu2_obj.school))

    Student.school = "XXX"
    print(stu1_obj.school)
    print(stu2_obj.school)

    stu1_obj.school = "XXX"
    print(stu1_obj.school)
    print(stu2_obj.school)
    print(Student.school)

    # 2、类的函数属性是绑定给对象用的
    print(Student.choose_course)
    print(stu1_obj.choose_course)
    print(stu2_obj.choose_course)

    stu1_obj.choose_course("python+go开发") # Student.choose_course(stu1_obj,"python+go开发")
    stu2_obj.choose_course("linux运维") # Student.choose_course(stu2_obj,"linux运维")

    Student.func()
    stu1_obj.func()


    # stu1_obj.choose_course("python+go开发")


    # python3统一了类与类型的概念
    l1 = [11,22,33] # l1 = list([11,22,33])

    # l1.append(44) # list.append(l1,44)

    print(type(l1))
    print(type(stu1_obj))

    3 绑定方法与非绑定方法

    3.1 类中定义的函数分为两大类

  • 绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将自己当做第一个参数自动传入
    • 绑定给类的方法
    • 绑定给对象的方法
  • 非绑定方法:不与任何人绑定,意味着谁都可以来调用,但是无论谁来调用就是一个普通函数,没有自动传参的效果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    import settings

    class Mysql:
    def __init__(self, ip, port):
    self.id = self.create_id()
    self.ip = ip
    self.port = port

    def tell_info(self):
    print("<%s:%s>" % (self.ip, self.port))

    @classmethod # 将方法与类绑定,专门给类用,自动传入的参数cls任然是类
    def from_conf(cls):
    return cls(settings.IP, settings.PORT)

    @staticmethod # 非绑定方法,又称作非绑定方法
    def create_id():
    import uuid
    return uuid.uuid4()


    #obj = Mysql('1.1.1.1',3306)
    #obj.tell_info()


    # obj = Mysql.from_conf()
    # print(obj.__dict__)

    # print(Mysql.from_conf)

    # print(Mysql.create_id)
    # print(obj.create_id)

    print(obj.__dict__)

3.2 总结绑定方法与非绑定方法的使用

若类中需要一个功能,该功能的实现代码中需要引用对象则将其定义成对象放方法、需要引用类将其定义成类方法、无需引用类或对象则将其定义成静态方法

4 隐藏属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 隐藏属性的真正目的就是为了不让使用者在类外部的直接操作属性
class People:
__country = "CHINA"

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


def __tell(self):
print('====>tell')


obj = People('egon',18)


print(People.__country)
print(People.__tell)
# 着两种方式访问是会报错的
# print(obj.__name)
# print(obj.age)

# print(People.__dict__)
# print(obj.__dict__)
# print(obj._People__name)
# print(obj._People__country)
# print(obj._People__tell)
class People:
def __init__(self,name,age):
self.__name = name # self._People__name = name
self.age = age

def tell(self):
print(self.__name) # self._People__name

obj = People('egon',18)
obj.__gender = "male"
print(obj.__gender)
print(obj.__dict__)
obj.tell()

# 1、隐藏数据属性的目的: 为了严格控制某一属性的操作
class People:
def __init__(self,name,age):
self.__name = name
self.age = age

def get_name(self):
print("名字:%s" %self.__name)

def set_name(self,v):
if type(v) is not str:
print("名字必须是str类型小垃圾")
return
self.__name = v


obj = People('egon',18)

# obj.get_name()
# obj.set_name(123)
# obj.get_name()

# 2、隐藏函数属性的目的:隔离复杂度

总结:
__开头的属性隐藏有四个特点:

  • 1、并不是真正的隐藏,只是一种语法意义上的变形
  • 2、该变形操作只在类定义阶段扫描语法时发生一次,类定义之后__开头的属性不会变形
  • 3、该隐藏对外不对内
    • 隐藏属性的目的**
  • 1、隐藏数据属性的目的:为了严格控制某一属性的操作
  • 2、隐藏函数属性的母的:隔离复杂度

5 property

5.1 property的用途

可以将类中的函数“伪装成”对象的数据属性,对象在访问该特殊属性时会触发功能的执行,然后将返回值作为本次访问的结果,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class People:
def __init__(self,name,height,weight):
self.name = name
self.height = height
self.weight = weight

@property
def bmi(self):
return self.weight / (self.height ** 2)


obj = People('egon',1.84,80)
# print(obj.bmi)
# obj.bmi = 111
# del obj.bmi


# 案例1:
"""
class People:
def __init__(self,name,age):
self.__name = name
self.age = age

def get_name(self):
return "名字:%s" %self.__name

def set_name(self,v):
if type(v) is not str:
print("名字必须是str类型小垃圾")
return
self.__name = v

def del_name(self):
del self.__name
print('删除成功')

name = property(get_name,set_name,del_name)

obj = People('egon',18)

# print(obj.name)
# obj.name = 123
del obj.name
"""

另外property还提供设置和删除属性的功能,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 案例2:
class People:
def __init__(self,name,age):
self.__name = name
self.age = age

@property
def name(self):
return "名字:%s" %self.__name

@name.setter
def name(self,v):
if type(v) is not str:
print("名字必须是str类型小垃圾")
return
self.__name = v

@name.deleter
def name(self):
del self.__name
print('删除成功')


obj = People('egon',18)

# print(obj.name)
# obj.name = 123
del obj.name

6 继承

6.1 致命三问

  • 1、什么是继承
    继承是一种新建子类的方式,新建的类称之为子类/派生类,被继承的称之为父类/基类
    子类会遗传父类的属性
  • 2、为何要用继承
    类是解决对象之间冗余问题的
    继承可以解决类与类之间的冗余问题
  • 3、如何继承
    在python中支持多继承
    在python3中如果一个类没有继承任何父类,那么默认继承object类
    但凡是继承了object类的子类,以及该子类的子子孙孙类都能用到object内的功能,称之为新式类
    没有继承了object类的子类,以及该子类的子子孙孙类都能用不到object内的功能,称之为经典类

ps:只有在python2中才区分经典类与新式类,python3中都是新式类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ParentClass1: #定义父类
pass

class ParentClass2: #定义父类
pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
pass

print(SubClass2.__bases__)
print(SubClass1.__bases__)
print(ParentClass2.__bases__)

6.2 继承解决冗余问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class People:
school = "SH"
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender


class Student(People):
# def __init__(self, name, age, gender, Course=None):
# if Course is None:
# Course = []
# self.name = name
# self.age = age
# self.gender = gender
# self.Course = Course

def choose_course(self, course):
self.courses.append(course)
print("学生%s 选课成功%s" % (self.name, self.courses))


class Teacher(People):
# def __init__(self, name, age, gender, level):
# self.name = name
# self.age = age
# self.gender = gender
# self.level = level

def score(self, stu_obj, num):
stu_obj.num = num
print('老师%s 为学生%s 打分%s' % (self.name, stu_obj.name, num))


# stu1 = Student('tom',18,'male')
tea1 = Teacher('lili',28,'female',10)

# print(stu1.__dict__)
# print(tea1.__dict__)
# print(stu1.school)

6.3 在子类派生的新方法中如何重用父类

6.3.1 在子类派生的新方法中如何重用父类的功能

  • 方式一:指名道姓地访问父类的函数,与继承无关
  • 方式二:super(),严格依赖继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class People:
school = "SH"

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


class Student(People):
def __init__(self, name, age, gender, courses=None):
People.__init__(self, name, age, gender,) # 方法一
if courses is None:
courses = []
self.courses = courses

def choose_course(self, course):
self.courses.append(course)
print("学生%s 选课成功%s" % (self.name, self.courses))


class Teacher(People):
def __init__(self, name, age, gender, level):
super().__init__(self, name, age, gender,) # super()在python2中必须传入类参数: super(Teacher,self) 而在python3中可以不传
self.level = level

def score(self, stu_obj, num):
stu_obj.num = num
print('老师%s 为学生%s 打分%s' % (self.name, stu_obj.name, num))


stu1 = Student('tom',18,'male')
tea1 = Teacher('lili', 28, 'female', 10)

print(stu1.__dict__)
print(tea1.__dict__)
# print(stu1.school)

6.4 在单继承背景下得属性查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Foo:
def f1(self):
print('Foo.f1')

def f2(self):
print("Foo.f2")
self.f1()


class Bar(Foo):
def f1(self):
print("Bar.f1")
obj = Bar()
obj.f2()

#执行结果为:
Foo.f2
Bar.f1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# class Foo:
x = 222
def __f1(self): # _Foo__f1
print('Foo.f1')

def f2(self):
print("Foo.f2")
self.__f1() # self._Foo__f1


class Bar(Foo):
# x = 111
def __f1(self): # _Bar__f1
print("Bar.f1")


obj = Bar()
obj.f2()
# 执行结果为:
Foo.f2
Foo.f1

6.5 在多继承背景下得属性查找

6.5.1 非菱形继承关系下的属性查找

在非菱形继承关系下,新式类与经典的属性查找属性都一样,都是一个分支一个分支地找下取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class D:
# def test(self):
# print('D')
pass


class F:
# def test(self):
# print('F')
pass


class C(F):
# def test(self):
# print('C')
pass


class E:
# def test(self):
# print('E')
pass

class B(E):
# def test(self):
# print('B')
pass


class A(B,C,D):
# def test(self):
# print('A')

pass

obj = A()
obj.test()

6.5.2 菱形继承关系下得属性查找

菱形继承/死亡钻石:一个类继承的多条分支最终汇聚到一个非object类上

  • 新式类:广度优先
  • 经典类:深度优先
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class G:
# def test(self):
# print('G')
pass

class D(G):
# def test(self):
# print('D')
pass


class F(G):
# def test(self):
# print('F')
pass


class C(F):
def test(self):
print('C')
pass


class E(G):
# def test(self):
# print('E')
pass

class B(E):
# def test(self):
# print('B')
pass


class A(B,C,D):
# def test(self):
# print('A')

pass

obj = A()
obj.test()
print(A.mro())


# c3会计算出一个mro列表

总结:属性的查找顺序最终还是按照mro列表的顺序来查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# super()得到一个特殊的对象,该对象访问的属性直接从父类中查找
class A:
def test(self):
print('A下的test')
super().test()
class B:
def test(self):
print('from B')
class C(A,B):
pass

obj = C()
obj.test()

#结果是:
A下得test
from B
# print(C.mro())

# print(A.mro())
obj = A()
obj.test()
#结果会报错,因为A的父类为object,object没有test方法

强调:属性查找参考的是属性查找发起者的mro列表

7 mixins机制

1
2
3
4
5
6
7
# 继承表达的关系:is-a  
# 在下面这个例子中,继承表达的关系就是:(学生-人:学生是人)
class People:
pass

class Student(People,Foo,Bar):
pass

一个子类可以同时继承多个父类,这样的设计常被人诟病,一来它有可能导致可恶的菱形问题,二来在人的世界观里继承应该是个”is-a”关系。 比如轿车类之所以可以继承交通工具类,是因为基于人的世界观,我们可以说:轿车是一个(“is-a”)交通工具,而在人的世界观里,一个物品不可能是多种不同的东西,因此多重继承在人的世界观里是说不通的,它仅仅只是代码层面的逻辑。不过有没有这种情况,一个类的确是需要继承多个类呢? 答案是有,我们还是拿交通工具来举例子: 民航飞机、直升飞机、轿车都是一个(is-a)交通工具,前两者都有一个功能是飞行fly,但是轿车没有,所以如下所示我们把飞行功能放到交通工具这个父类中是不合理的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Vehicle:  # 交通工具
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")


class CivilAircraft(Vehicle): # 民航飞机
pass


class Helicopter(Vehicle): # 直升飞机
pass


class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
pass

但是如果民航飞机和直升机都各自写自己的飞行fly方法,又违背了代码尽可能重用的原则(如果以后飞行工具越来越多,那会重复代码将会越来越多)。 怎么办???为了尽可能地重用代码,那就只好在定义出一个飞行器的类,然后让民航飞机和直升飞机同时继承交通工具以及飞行器两个父类,这样就出现了多重继承。这时又违背了继承必须是”is-a”关系。这个难题该怎么解决?

Python提供了Mixins机制,简单来说Mixins机制指的是子类混合(mixin)不同类的功能,而这些类采用统一的命名规范(例如Mixin后缀),以此标识这些类只是用来混合功能的,并不是用来标识子类的从属”is-a”关系的,所以Mixins机制本质仍是多继承,但同样遵守”is-a”关系,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Vehicle:  # 交通工具
pass


class FlyableMixin:
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")


class CivilAircraft(FlyableMixin, Vehicle): # 民航飞机
pass


class Helicopter(FlyableMixin, Vehicle): # 直升飞机
pass


class Car(Vehicle): # 汽车
pass

可以看到,上面的CivilAircraft、Helicopter类实现了多继承,不过它继承的第一个类我们起名为FlyableMixin,而不是Flyable,这个并不影响功能,但是会告诉后来读代码的人,这个类是一个Mixin类,表示混入(mix-in),这种命名方式就是用来明确地告诉别人(python语言惯用的手法),这个类是作为功能添加到子类中,而不是作为父类,它的作用同Java中的接口。所以从含义上理解,CivilAircraft、Helicopter类都只是一个Vehicle,而不是一个飞行器。
使用Mixin类实现多重继承要非常小心

  • 首先它必须表示某一种功能,而不是某个物品,python 对于mixin类的命名方式一般以 Mixin, able, ible 为后缀
  • 其次它必须责任单一,如果有多个功能,那就写多个Mixin类,一个类可以继承多个Mixin,为了保证遵循继承的“is-a”原则,只能继承一个标识其归属含义的父类
  • 然后,它不依赖于子类的实现
  • 最后,子类即便没有继承这个Mixin类,也照样可以工作,就是缺少了某个功能。(比如飞机照样可以载客,就是不能飞了)

8 组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# 继承表达是is-a关系
# 组合表达是has-a关系: 一个对象拥有一个属性,该属性值指向另外一个对象

class Foo:
def __init__(self,x,y):
self.x = x
self.y =y

def func1(self):
print('=======>Foo.func1')


class Bar:
def __init__(self, m, n):
self.m = m
self.n = n

def func2(self):
print('=======>Bar.func2')

obj1 = Foo(100,200)

obj2 = Bar(111,222)
obj2.xxx = obj1 # 这里就是 has-a的关系


# obj2.func2()
# obj2.xxx.func1()


# has-a在选课系统中的应用
class People:
school = "SH"

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

class Course:
def __init__(self,name,period,price):
self.name = name
self.period = period
self.price = price

def tell_info(self):
print('%s:%s:%s' %(self.name,self.period,self.price))

class Student(People):
def __init__(self, name, age, gender, courses=None):
super().__init__(name, age, gender,)
if courses is None:
courses = []
self.courses = courses



def choose_course(self, course):
self.courses.append(course)
print("学生%s 选课成功%s" % (self.name, self.courses))


class Teacher(People):
def __init__(self, name, age, gender, level):
super().__init__(name, age, gender,)
self.level = level

def score(self, stu_obj, num):
stu_obj.num = num
print('老师%s 为学生%s 打分%s' % (self.name, stu_obj.name, num))



python = Course("python全栈开发","6mons",30000)


stu1 = Student('tom',18,'male')
tea1 = Teacher('lili', 28, 'female', 10)

# stu1.courses.append(python)
# stu1.courses[0].tell_info()

tea1.course = python
tea1.course.tell_info()

9 多态

9.1 多态性

我们可以在不知道对象具体类型的前提嘻,而可以直接使用对象

1
2
3
4
5
6
7
# 例如:__len__() 就是多态的典型应用之一
"abc".__len__()
[1,2,3].__len__()
{'k1':111}.__len__()

def len(obj):
return obj.__len__()

9.2 abstractmethod

@abc.abstractmethod # 表示继承它的子类必须自己实现这相应的方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import abc

class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 表示继承它的子类必须自己实现speak这个方法。
def speak(self): # 在这里它是作为一种规范来约束继承它的子类
pass

@abc.abstractmethod #
def run(self):
pass
# Animal() 使用了abstractmethod Animal类就不能使用了

class Pig(Animal):
def speak(self):
pass
def run(self):
pass

class People(Animal):
def speak(self):
pass

def run(self):
pass


class Dog(Animal):
def speak(self):
pass

def run(self):
pass

Pig()
People()
Dog()

9.3 鸭子类型

简单来说,就是:只能叫得像鸭子,跑的像鸭子,你就是鸭子,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# python崇尚鸭子类型
# 以下的三个类,看起来很像,都有speak run 方法,这种类型就是鸭子类型
class Pig:
def speak(self):
pass
def run(self):
pass

class People:
def speak(self):
pass

def run(self):
pass


class Dog:
def speak(self):
pass

def run(self):
pass

10 内置方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 在满足某种条件下会自动触发执行

# 1、__str__方法
# 打印对象时自动触发执行,将返回值当作打印结果,返回值必须是str类型
class People:
def __init__(self,name,age):
self.name = name
self.age = age

def __str__(self):
return "<%s:%s>" %(self.name,self.age)
# return 123 # 必须返回str类型的值

obj = People("egon",18)

print(obj) # print(obj.__str__())

# 2、__del__方法
# 删除对象时执行
class People:
def __init__(self,name,age):
self.name = name
self.age = age
self.f = open('a.txt',mode='wb')

def __del__(self): # 删除对象时执行
# 回收系统资源
print('-------')
self.f.close()


obj = People("egon",18)
print('end=============>')

# 以上代码的执行结果是:
end=============>
-------

# 为什么会打印出来 ------ ? 因为在执行完以上代码后,程序就结束了,这时候会回收系统资源,执行 __del__中的方法

del obj的时候 同样会执行__del__中的方法



x = "aa"
print(type(x) is str)
print(isinstance(x,str))
# 判断一个对象的类型的时候提倡第二种用法,因为这更加符合 面向对象的思想


print(issubclass(People,object))
# 判断一个类是否是某个类的子类

其他内置方法(理解,暂时不需要掌握):https://www.cnblogs.com/linhaifeng/articles/6204014.html

11 反射

11.1 hasattr、setattr、getattr、delattr

反射:通过字符串来操作属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# -hasattr------------------------------
# 判断一个对象是否有某种属性
class People:
x = 111
def __init__(self,name,age):
self.name = name
self.age = age

def func(self):
print("=======>",self.name,self.age)


obj = People("egon",18)
print(hasattr(obj,"name")) # 这里我们判断obj对象是否有“name”属性,这里就得用字符串“name”,而不是 hasattr(obj,name)
# hassttr 返回的值是True 或者 False

# -getattr-------------------------------
# 获取对象的属性
print(getattr(obj,"name")) # obj.name
f = getattr(obj,"func") # obj.func
f()

# -setattr------------------------------
# 设置对象的属性
setattr(obj,"gender","male") # obj.gender="male"
print(obj.gender)

# -delattr-------------------------------
# 删除对象的属性
delattr(obj,"name") # del obj.name
print(obj.__dict__)


# setattr(People,"country","china") # People.country = "china"
# print(People.__dict__)
# print(People.country)
# print(obj.country)

delattr(obj,"x") # del obj.x # 不让删,因为x这个属性是继承父类的,而不是它独有
print(obj.__dict__) # 打印发现obj的__dict__属性中并没有 x
>>>{'name': 'egon', 'age': 18}