面向对象高阶

内置成员

  • 获取当前类\对象的所属成员,返回一个字典包含类或对象的成员
    • 类名.dict
    • 对象名.dict
  • 获取类的文档信息,就是定义类后对类的一个说明
    • 类名.doc
    • 对象名.doc
  • 获取类名称,结果返回字符串
    • 类名.name
  • 获取类所在的文件名称,如果是当前文件就会返回main
    • 类名.module
  • 获取当前类的父类或者父类列表列表
    • 类名.base
    • 类名.bases
  • 获取类的继承关系
    • 类名.mro
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
class A():
pass


class B():
pass


class Demo(A, B):
"""
这是类的说明文档
"""
name = None
age = None

def func(self):
print('这是一个方法')


res = Demo.__dict__
print(
res) # {'__module__': '__main__', '__doc__': '\n 这是类的说明文档\n ', 'name': None, 'age': None, 'func': <function Demo.func at 0x10076b560>, '__dict__': <attribute '__dict__' of 'Demo' objects>, '__weakref__': <attribute '__weakref__' of 'Demo' objects>}

d = Demo()
res = d.__dict__
print(res) # {}

res = Demo.__doc__
print(res)
#
# 这是类的说明文档
#
res = d.__doc__
print(res)
#
# 这是类的说明文档
#

res = Demo.__name__
print(res) # Demo

res = Demo.__module__
print(res) # __main__

res = Demo.__base__
print(res) # <class '__main__.A'>
res = Demo.__bases__
print(res) # (<class '__main__.A'>, <class '__main__.B'>)

print(Demo.mro()) # [<class '__main__.Demo'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
print(Demo.__mro__) # (<class '__main__.Demo'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

方法的分类

对象方法

在类中定义的方法,含有 self 参数 含有 self 的方法,只能使用对象进行调用 该方法会把调用的对象传递进来

1
2
3
4
5
6
7
8
9
10
# 对象方法
class Demo():
def fun(self):
print(self)
print('this is func')

d = Demo()
d.fun()
# <__main__.Demo object at 0x10cc92150>
# this is object method

类方法

在类中定义的方法,使用了装饰器@classmethod 进行装饰的类 方法中有 cls 这个形参,不需要实例化对象就可以直接用类调用的方法 会把调用这个方法的类传递进来

1
2
3
4
5
6
7
8
9
10
11
# 类方法
class Cdemo():
@classmethod
def fun(cls):
print(cls)
print('this is class method')


Cdemo.fun()
# <class '__main__.Cdemo'>
# this is class method

绑定类方法

绑定类方法就是在类中,不带有形参 self 的一个方法 调用的时候直接使用类调用,无法使用对象调用 不会传递任何参数进来

1
2
3
4
5
6
7
8
# 绑定类方法
class Bdemo():
def func():
print('this is bind class func')


Bdemo.func()
# this is bind class func

静态方法

在类中定义的方法,使用了装饰器@staticmethod 进行装饰的类 类中,不带有形参 self 的一个方法,也没有其他参数 可以使用类或者对象直接调用 不会传递任何参数

静态方法只是定义在类范围内的一个函数而已

1
2
3
4
5
6
7
8
9
10
# 静态方法
class Sdemo():
@staticmethod
def func():
print('this is a static method')


s = Sdemo()
s.func() # this is a static method
Sdemo.func() # this is a static method

常用函数

  • 检测一个类是否为另一个类的子类
    • issubclass(son,fa)
  • 检测一个对象是否是一个类的实例化
    • isinstance(obj,cls)
  • 检测一个类/对象是否具有一个成员属性
    • hasattr(obj/cls,attr)
  • 获取类/对象成员的值
    • getattr(obj/cls,attr)
  • 设置类/对象成员的值
    • setattr(obj/cls,attr,value)
  • 删除类/对象的成员属性
    • delattr(obj/cls,attr)
  • dir() 获取当前对象所可以访问的成员列表
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
class A():
def hello(self):
print('hello')


class B(A):
pass


class C(A):
pass


class D(B, C):
name = None

def get_hello(self):
print('hello')


print(issubclass(D, B)) # True

d = D()
print(isinstance(d, D)) # True
print(isinstance(d, B)) # True

print(hasattr(d, 'hello')) # True
print(hasattr(d, 'get_hello')) # True
print(hasattr(d, 'name')) # True

res = getattr(d, 'name')
print(res) # None

setattr(d, 'name', 'zachary')
print(d.name) # zachary

delattr(d,'name')

print(d.name) # None

res = dir(d)
print(res)

魔术方法

魔术方法就是不需要手动调用就可以自动执行的方法

init 初始化方法

  • 触发机制:当实例化对象之后就会立即触发的方法
  • 作用:为当前创建的对象完成一些初始化的操作,比如:成员属性的赋值,方法的调用,打开或创建一些资源
  • 参数:一个 self,接受当前对象,其他参数根据需求进行定义即可
  • 返回值:无
  • 注意事项:无

new 构造方法

构造一个对象的过程是:

1
2
3
4
person = Person("Jack")
实际上做了两件事情
1. person = object.__new__(Person, "Jack")
2. person.__init__("Jack")
  • 触发机制:实例化对象时自动触发(在init之前触发)
  • 作用:管理控制对象创建的过程
  • 参数:一个 cls 接受当前类,其他参数根据初始化方法的参数进行决定
  • 返回值:必须返回 object.new(cls)进行对象的创建,如果没有返回值,则实例化的对象的结果为 None
  • 注意事项:
    • new方法的参数和init方法的参数要保持一致,除了第一个参数
    • 必须返回 object.new(cls)进行对象的创建,如果没有返回值,则实例化对象的结果为 None
    • 通常情况下,定义了new,就不用再定义init方法了
  • 应用场景:设计模式中的单例设计模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Square(int):
def __new__(cls,value:int):
return super().__new__(cls,value**2)

num = Square(2)
print(num)
print(type(num))
print(isinstance(num, int))


class Student:
def __new__(cls,name,gender):
obj = super().__new__(cls)
obj.name = name
obj.gender = gender
return obj

student = Student("zachary","male")
print(student.name)
print(student.gender)

del 析构方法

  • 触发机制:当该类对象被销毁时,自动触发
  • 作用:关闭或释放对象创建时打开或创建的一些资源
  • 参数:一个 self,接受当前对象
  • 返回值:无
  • 注意事项:无

call

  • 触发机制:把对象当做函数直接调用时自动触发
  • 作用:一般用于归纳类或对象的操作步骤,方便调用
  • 参数:一个 self 接收当前对象,其他参数根据调用需求确定
  • 返回值:可有可无
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
class Person():

# 构造方法
def __new__(cls, *args, **kwargs):
print('触发了构造方法')
print(args)
return object.__new__(cls)
# 必须要有这一句才能返回object对象,才能触发init

# 初始化方法
def __init__(self, name, age, gender):
print('触发了初始化方法')
self.name = name
self.age = age
self.gender = gender

def __call__(self, *args, **kwargs):
print('你把对象当成了函数进行调用')

# 析构方法
def __del__(self):
print('触发了析构方法')


# 实例化对象
duanduan = Person('duandaun', 23, 'female')
duanduan()
# 触发了构造方法
# ('duandaun', 23, 'female')
# 触发了初始化方法
# 你把对象当成了函数进行调用
# 触发了析构方法

len

可以代替对象使用 len 函数,并返回一个指定的整型

  • 触发机制:当使用 len 函数去检测当前对象的时候自动触发
  • 作用:可以使用 len 函数检测当前对象中某个数据的信息
  • 参数:一个 self 接收当前对象
  • 返回值:必须有,并且必须是一个整数
  • 注意事项:len 要获取什么属性的值,就在返回值中返回那个属性的长度即可

str

可以代替对象进行 str 或者 print 的字符串信息返回

  • 触发机制:当使用 str 或者 print 函数对对象进行操作时自动触发
  • 作用:代码对象进行字符串的返回,可以自定义打印的信息;该描述面向用户
  • 参数:一个 self,接受当前对象
  • 返回值:必须有,而且必须是字符串类型的值

repr

  • 触发机制:在使用 repr 方法对当前对象进行转换时自动触发,某种情况 print 也可以触发
  • 作用:可以设置 repr 函数操作对象的结果;该描述的主要目标是机器或者开发者
  • 参数:一个 self 接受当前对象
  • 返回值:必须有,而且必须是字符串类型的值
  • 注意:正常情况下,如果没有str这个魔术方法,repr方法就会代替str魔术方法,可以使用 print 进行触发

bool

  • 触发机制:当使用 bool 函数转换当前对象时,自动触发
  • 作用:可以代替对象进行布尔类型的转换,可以转换任何数据
  • 参数:一个 self 接收对象
  • 返回值:必须是一个布尔类型的返回值,如果没有实现bool方法,会去掉用len的结果求解 bool 值

eq

  • 触发机制:当使用==比较两个对象的内容是否相同的时候
  • 作用:对比两个对象是否相等的逻辑
  • 参数:一个 self 接收对象,other 接收另一个对象
  • 返回值:必须有 Bool 类型

hash

  • 触发机制:使用hash()方法 或者 将该对象加入到 set、dict 等需要调用 hash()方法的时候会调用
  • 作用:根据对象生成 hash 值的逻辑
  • 参数:一个 self 接收对象
  • 返回值:hash 值
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
class Demo():
listurl = []

def __len__(self):
return len(self.listurl)

def __str__(self):
return '这是当前脚本中的一个对象'

def __repr__(self):
return '这是一个对象'


obj = Demo()
res = len(obj)
print(res) # 0

# 使用__str__
print(obj) # 这是当前脚本中的一个对象
res = str(obj)
print(res) # 这是当前脚本中的一个对象

# 注释掉__str__, 只使用__repr__
print(obj) # 这是一个对象

# 不用注释掉__str__, 使用__repr__
res = repr(obj)
print(res) # 这是一个对象


# 使用bool without __bool__
res = bool(obj)
print(res) # False 因为listurl是空的,若listurl有值,则为True

class MyDate():
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day


def __eq__(self,other):
if not isinstance(other,MyDate):
return False

return self.year == other.year and self.month == other.month and self.day == other.day


def __hash__(self):
print("__hash__被调用了")
return hash(self.year + self.month * 41 + self.day * 41)


# eq
mydate1 = MyDate(2023,1,4)
mydate2 = MyDate(2023,1,4)
mydate3 = mydate1
print(mydate1 is mydate2)
print(mydate1 is mydate3)
print(mydate1 == mydate2)
print(mydate1 == mydate3)

# hash
test_set = set()
test_set.add(mydate1)
print(hash(mydate2))

str 与 repr

str 和 repr 函数都能够把其他类型的数据转化为字符串类型 str 函数会把对象 转为 更适合人类阅读的形式 repr 函数会把对象 转为 更适合解释器读取的形式 如果数据对象并没有明显的区别的话,str 和 repr 结果是一样的

成员相关魔术方法

  1. getattribute(self, item)
  • 触发机制:当访问对象成员时,自动触发,无论当前成员是否存在,也无论当前成员是否有值
  • 作用:可以在获取对象成员时,对数据进行一些处理
  • 返回值:可有可无,返回的值就是访问的结果
  • 注意事项:
    • 在当前的魔术方法中,禁止使用 对象.成员 的方式进行成员访问,会触发递归
    • 如果想要在当前魔术方法中访问对象的成员必须使用 object 来进行访问
      • 格式:
      • object.getattribute(self, item)
  1. getattr
  • 触发机制:当访问对象中不存在的成员时,自动触发
  • 作用:防止访问不存在的成员时报错,也可以为不存在的成员进行赋值操作
  • 参数:一个 self 接收当前对象,一个 item 接收当前访问的成员名称
  • 返回值:可有可无
  • 注意事项:
    • 如果有getattribute存在,就会无法使用getattr,会直接调用到getattribute
    • 也要注意,不要在当前的方法中再次去访问这个不存在的成员,会触发递归操作
  1. setattr
  • 触发机制:当给对象的成员进行赋值操作时会自动触发(包括添加、修改)
  • 作用:可以限制或管理对象成员的添加和修改操作
  • 参数:1.self 接收当前对象 2.设置的成员名 3.设置的成员值
  • 返回值:无
  • 注意事项:
    • 在当前的魔术方法中禁止给当前对象的成员直接进行赋值操作,会触发递归操作,
    • 如果想要给当前对象的成员进行赋值,需要借助 object
      • 格式:object.setattr(self,key,value)
    • 该方法中如果没有给对象成员进行赋值,那么对象成员赋值失败
  1. delattr
  • 触发机制:当删除对象成员时自动触发
  • 作用:可以去限制对象成员的删除,还可以删除不存在成员时的报错
  • 参数:1.self 接收当前对象 2.item 删除的成员名称
  • 返回值:无
  • 注意事项:
    • 在当前魔术方法中禁止直接删除对象的成员,会触发递归操作
    • 如果想要删除当前对象的成员,那么需要借助 object
      • 格式:object.delattr(self,item)

访问一个成员的顺序

  1. 调用 getattribute魔术方法
  2. 调用数据描述符
  3. 调用当前对象的成员属性
  4. 调用当前类的成员
  5. 调用非数据描述符
  6. 调用父类的成员
  7. 调用getattr魔术方法
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
class Person():
name = '名字'
age = '年龄'
gender = '性别'

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

def say(self):
print('聊一聊,倾诉一下')

def sing(self):
print('庄先生,高歌一曲')

# 获取成员对象时候 自动触发
def __getattribute__(self, item):
try:
# 在方法中只能使用object获取属性值
res = object.__getattribute__(self, item)
return res[0] + '*' + res[-1]
except:
return False

def __getattr__(self, item):
print(item)
return False

def __setattr__(self, key, value):
object.__setattr__(self, key, value)

def __delattr__(self, item):
object.__delattr__(self, item)


duan = Person('duanduan', 23, 'female')
print(duan.name) # d*n
print(duan.salary) # False

# 把 __getattribute__ 注释掉
print(duan.name) # duanduan
print(duan.salary)
# salary
# False

# 把 __getattribute__ 注释掉
duan.salary = 10000
print(duan.salary) # 10000

# 把 __getattribute__ 注释掉
del duan.salary
print(duan.salary) # salary
del duan.name
print(duan.name)
# False
# 名字

描述符与 property

当一个类中,包含了三个魔术方法(get,set,delete)之一,或者全部,那么这个类就被称为描述符类

基本语法

描述符的作用:

  • 描述符的作用就是对一个类中的某个成员进行一个详细的管理操作(获取、赋值、删除)
  • 描述符就是代理了一个类中的成员的操作,描述符属于类,只能定义为类的属性

一个类中的成员的值是另一个描述符类的对象() 那么当对这个类中的成员进行操作时,可以理解为就是对另一个对象的操作 使用格式:把当前的描述符类赋值给一个需要代理的类中的成员属性

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
class PersonName():
__name = 'duanduan'

def __get__(self, instance, owner):
# print(self,instance,owner)
return self.__name

def __set__(self, instance, value):
# print(self,instance,value)
self.__name = value

def __delete__(self, instance):
del self.__name


class Person():
# 把类中的成员属性交给描述符来实现
name = PersonName()


duan = Person()
print(duan.name) # duanduan
duan.name = 'duanxiaozhu'
print(duan.name) # duanxiaozhu
del duan.name
print(duan.name) # duanduan

实际上真正的用法应该是下面这个例子,还需要额外使用一个

__set_name__(self, owner, name)方法 给属性赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PersonName:
def __set_name__(self, owner, name):
self.__property_name = name

def __set__(self, instance, value):
# 做一些校验
if not isinstance(value, str):
raise Exception(f"{self.__property_name} is not a str type")
if len(value) == 0:
raise Exception(f"{self.__property_name} is empty")
instance.__dict__[self.__property_name] = value

def __get__(self, instance, owner):
if self.__property_name in instance.__dict__:
return instance.__dict__[self.__property_name]
return None

应用案例

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
'''
定义一个学生类,需要记录,学员的id、名字、分数
解决方法:
1. 在__init__方法中检测当前分数范围的合法性
但是这个方法只能在初始化的时候有效,之后修改的话就无效了
2. 定义一个__setattr__的魔术方法进行检测
检测给分数进行赋值的时候的一个合法性

假如 学员的分数不止一个时怎么办,比如 语文分数、数学分数、英语分数 甚至类中的代码比较多的情况
解决方法:
3. 可以使用描述符来代理我们分数这个属性

'''
# 定义一个学生类,需要记录,学员的id、名字、分数
# 解决方法:1. 2.
class Student():
def __init__(self,id,name,score):
self.id = id
self.name = name
if score >= 0 and score <=100:
self.score = score
else:
print('当前分数出错啦')

def returnShe(self):
info = f'''
学号:{self.id}
姓名:{self.name}
分数:{self.score}
'''
print(info)

def __setattr__(self, key, value):
# 检测是否是给score进行赋值操作
if key == 'score':
if value >= 0 and value <= 100:
object.__setattr__(self,key,value)
else:
print('当前分数出错啦')
else:
object.__setattr__(self,key,value)

duan = Student(2022,'duanduan',100)
duan.returnShe()
duan.score = 1000
duan.returnShe()


#解决方法: 3. 使用描述符来代理我们分数这个属性

# 定义描述符类
class Score():
def __set__(self, instance, value):
if value >= 0 and value <= 100:
self.__score = value
else:
print('当前分数出错啦')

def __get__(self, instance, owner):
return self.__score

def __delete__(self, instance):
pass

class Student():
score = Score()
def __init__(self,id,name,score):
self.id = id
self.name = name
self.score = score



def returnShe(self):
info = f'''
学号:{self.id}
姓名:{self.name}
分数:{self.score}
'''
print(info)


duan = Student(2022,'duanduan',100)
duan.returnShe()
duan.score = 1000
duan.returnShe()

三种定义方式

  • 数据描述符(完整)
    • 同时具备三个魔术方法的类就是 数据描述符
  • 非数据描述符 (不完整)
    • 没有同时具备三个魔术方法的类就是 非数据描述符类

第一种

1
2
3
4
5
6
7
8
9
10
11
12
# 格式一 通过定义描述符类来实现

class ScoreManage():
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass

class Student():
score = ScoreManage()

第二种

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
# 格式二 使用property函数来实现
class Studebnt():

def getscore(self):
print('getscore')

def setscore(self, value):
print('setscore', value)

def deletescore(self):
print('deletescore')

# 在property函数中指代对应的三个方法,并且参数要按顺序对应上
# 1.__get__ 2.__set__ 3.__delete__
score = property(getscore, setscore, deletescore)


xs = Studebnt()
print(xs.score)
# getscore
# None 因为上面的getscore没有返回值
xs.score = 100 # setscore 100
print(xs.score)
# getscore
# None 因为上面的getscore没有返回值
del xs.score # deletescore

第三种

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
# 格式三 使用装饰器 @property 语法来实现
class Student():
__score = None

@property
def score(self):
print('get')
return self.__score

@score.setter
def score(self, value):
print('set')
self.__score = value

@score.deleter
def score(self):
print('delete')
del self.__score


xs = Student()
print(xs.score)
# get
# None
xs.score = 100 # set
print(xs.score)
# get
# 100
del xs.score # delete

设计模式

设计模式是前人为了完成某个功能或需求,根据经验和总结,对实现的代码步骤和代码设计进行了总结和归纳。成为了实现某种需求的经典模式 设计模式并不是固定的,而是一种面向对象编程的设计

单例(单态)设计模式

在当前脚本中,同一个类只能创建出一个对象去使用,这种情况就称为单例(单态)

  • 数据库连接对象
  • 共享对象

Python 中的单例模式设计思路,

  • 1、需要有一个方法,可以去控制当前对象的创建
    • 构造方法 new
  • 2、需要有一个标识来存储和表示是否有对象
    • 创建一个属性,进行存储,默认值为 None
  • 3、在创建对象的方法中去检测和判断是否有对象?
    • 如果没有对象,则创建对象,并且把对象存储起来
    • 如果存储的是对象,则直接返回对象,就不需要创建新的对象了

这个代码很重要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Demo():
# 2.定义私有属性存储对象,默认值为None
__obj = None

# 1.定义构造方法
def __new__(cls, *args, **kwargs):
# 3.在创建对象的过程中,判断是否有对象
if not cls.__obj:
cls.__obj = object.__new__(cls)

return cls.__obj


a = Demo()
b = Demo()
print(a) # <__main__.Demo object at 0x10344e1d0>
print(b) # <__main__.Demo object at 0x10344e1d0>

使用装饰器来实现的话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def singleton(cls):
_obj = {}

def inner(*args, **kwargs):
if cls in _obj:
return _obj[cls]

obj = cls(*args, **kwargs)
_obj[cls] = obj
return obj

return inner

@singleton
class Demo:
pass

a = Demo()
b = Demo()
print(a is b)

装饰器模式-2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def singleton(cls):
def inner():
if hasattr(cls,"__obj"):
return getattr(cls,"__obj")

obj = cls()
setattr(cls,"__obj",obj)
return obj
return inner

@singleton
class Demo:
pass

a = Demo()
b = Demo()
print(a is b)

装饰器模式-3,使用元类来设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class SingletonMeta(type):
def __call__(cls, *args, **kwargs):
if hasattr(cls,"__obj"):
return getattr(cls,"__obj")

obj = super().__call__(*args, **kwargs)
setattr(cls,"__obj",obj)
return obj

class Demo(metaclasss=SingletonMeta):
pass

a = Demo()
b = Demo()
print(a is b)

mixin 混合设计模式

继承需要一个必要的前提,继承应该是一个‘is-a’的关系 例如:苹果可以去继承水果,因为苹果是一个水果

  • Minin 表示混合
    • Minin 必须是表示一种功能,而不是一个对象
    • Minin 的功能必须单一,如果有多个功能,那就多定义 Minin 类
    • Python 中的 Minin 是通过多继承实现的
    • Minin 这个类通常不单独使用,而是混合到其他类中,去增加功能
    • Minin 类不依赖子类的实现,即便子类没有继承这个 Minin,子类也能正常运行,就是可能会缺少了一些功能
  • 使用 Minin 混合类的好处
    • 在不对类的内容修改的前提下,扩展了类的功能
    • Minin 混合类为了提高代码的重用性,使得代码结构更加简单清晰
    • 可以根据开发需求任意调整功能(创建新的 Minin 类)
    • 避免设计多层次的复杂继承关系
  • 解决方案使用的还是多继承,但是给飞行器这个类的名称定义成为一个 Minin 混合类
  • 这样的一个 Minin 混合类,功能单一、并且不会单独使用:只有一个飞行器的功能,在继承的时候不会单一继承这一个,作为一个扩展的功能
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
# 解决方案使用的还是多继承,但是给飞行器这个类的名称定义成为一个 Minin 混合类
# 这样的一个Minin混合类,功能单一、并且不会单独使用:只有一个飞行器的功能,在继承的时候不会单一继承这一个,作为一个扩展的功能

# 交通工具类
class vehicle():

def huo(self):
print('运输货物')

def ren(self):
print('搭载乘客')

# 飞行器类
class flyingMixin():
def fly(self):
print('可以起飞啦')


# 定义汽车类
class cart(vehicle):
pass


# 定义飞机类
class airplane(vehicle,flyingMixin):
pass

# 定义直升机
class helicopter(airplane,flyingMixin):
pass

实现一个具体的列子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ItemMixin:
def __getitem__(self, key):
return self.__dict__[key]

def __setitem__(self, key, value):
self.__dict__[key] = value

class Car(ItemMixin):
def __init__(self, origin, auto):
self.origin = origin
self.auto = auto

car = Car("Germany", True)
print(car["origin"])

练习:把一个对象转换成 dict 和 json 格式,使用 mixin 设计模式

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 DictMixin:
def to_dict(self):
return self.__dictConvert(self.__dict__)

def __dictConvert(self, dicAttrs: dict):
resDict = {}
for key, value in dicAttrs.items():
resDict[key] = self.__valueConvert(value)

return resDict

def __valueConvert(self, value):
if isinstance(value, DictMixin):
return value.to_dict()
elif isinstance(value, dict):
return self.__dictConvert(value)
elif isinstance(value, list):
return [self.__valueConvert(i) fot i in list]
elif hasattr(value,'__dict__):
return self.__dictConvert(value)
else:
return value


class JSONMixin:
def to_json(self):
return json.dumps(self.to_dict())

class Car(DictMixin, JSONMixin):
def __init__(self, origin, auto):
self.origin = origin
self.auto = auto


car = Car("Germany",True)
print(car.to_dict())
print(car.to_json())

抽象类

  • 抽象类是一个特殊的类
    • 抽象类不能用,不能直接实例化成为一个对象
    • 抽象类中包含了抽象方法,抽象方法就是没有实现的代码的方法
    • 抽象类需要子类继承,并重写父类的抽象方法,才可以使用
  • 如果要定义一个抽象类,需要继承metaclass=abc.ABCMeta类 或者 ABC
  • 如果要定义一个抽象方法,需要使用装饰器进行装饰@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
37
38
import abc


class AbsCls(metaclass=abc.ABCMeta):

@abc.abstractmethod
def absmethod(self):
pass

def othermethod(self):
print('这是一个实现了的方法')


class sonclass(AbsCls):

def absmethod(self):
print('son实现了抽象类的抽象方法')


class daughterclass(AbsCls):
def absmethod(self):
print('daughter实现了抽象类的抽象方法')


def method_call(abscls: AbsCls):
abscls.absmethod()


obj = sonclass()
obj.absmethod()
obj.othermethod()
# 实现了抽象类的抽象方法
# 这是一个实现了的方法

son = sonclass()
daughter = daughterclass()
method_call(son)
method_call(daughter)

枚举

基本用法

作用:代码中的数值,不便于阅读和查错

1
2
3
class Student:
def __init__(self):
self.gender = 1 # male 1 female 0

定义枚举类:通过继承 enum.Enum实现一个枚举类

  • 可以通过字符串 用[]获取到对应的枚举类型
  • 可通过整数类型转换 用()获取到对应的枚举类型
  • 枚举类本身是可以迭代的(比如 下拉列表,需要获取全部选项)
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
from enum import Enum

class Gender(Enum):
MALE = 1
FEMALE = 2

class Student:
def __init__(self):
self.gender = Gender.MALE


print(Gender.MALE.name)
print(Gender.MALE.value)

# 可以通过字符串 用[]获取到对应的枚举类型
gender_str = "MALE"
student = Student()
student.gender = Gender[gender_str]
print(student.gender)

# 也可通过整数类型转换 用()获取到对应的枚举类型
gender_int = 2
student.gender = Gender(gender_int)
print(student.gender)

# 遍历枚举成员
for gender in Gender:
print(gender)


枚举成员的别名

  • 枚举类中的多个成员具有同一个 value 时,只有一个能成为主要成员,其余是别名
  • 可以通过__members__获取枚举类的所有成员
  • 唯一枚举装饰器 @enum.unique(为了避免 不允许重复 value 的情况下出错,比如有时候复制粘贴会写出两个相同 value 的枚举 然后忘记修改的情况,可以使用唯一枚举装饰器进行处理)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ResStatus(Enum):
SUCCESS = 1
OK = 1
FAIL = 2
NO = 2

for rs in ResStatus:
print(rs.name)


#获取枚举类所有成员
print(ResStatus.__members__)
print(ResStatus.SUCCESS == ResStatus.OK)
print(ResStatus.SUCCESS is ResStatus.OK)

@num.unique
class Status(Enum):
SUCCESS = 1
OK = 1
FAIL = 2
NO = 2

枚举的扩展

  1. __str__
1
2
3
4
5
6
7
8
9
10
class ResStatus(Enum):
SUCCESS = 1
OK = 1
FAIL = 2
NO = 2

def __str__(self):
print(f"{self.name}({self.value})")

print(ResStatus.OK)
  1. __eq__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ResStatus(Enum):
SUCCESS = 1
OK = 1
FAIL = 2
NO = 2

def __eq__(self,other):
if isinstance(other,int):
return self.value == other

if isinstance(other,str):
return self.name == other.upper()

if isinstance(other,ResStatus):
return self is other

return False

print(ResStatus.OK == 1)
  1. __lt__

需要使用@total_ordering

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@total_ordering
class OrderOfProcess(Enum):
START = 1
PROCESSING = 2
TEST = 3
END = 4

def __lt__(self):
if isinstance(other,int):
return self.value < other

if isinstance(other,OrderOfProcess):
return self.value < other.value

return False

print(OrderOfProcess.PROCESSING < 2)
print(OrderOfProcess.END < OrderOfProcess.TEST)
  1. auto()

按顺序给枚举成员赋值

1
2
3
class ResStatus(Enum):
SUCCESS = auto()
FAIL = auto()

更新: 2024-01-11 22:37:47
原文: https://www.yuque.com/zacharyblock/cx2om6/yke47fm8vxrh2w6g