0%

Python小技巧(9)--元编程

元编程----来源于cookbook第九章

记录第九章中比较有意思的部分


类方法与装饰器

将类方法作为装饰器时需要处理的小问题就是cls与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
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

####### 将普通装饰器用于类普通方法---处理self

def dec1(func):
def warp(self, *args):
print(self.x) # 由于此时需要访问实例内数据,因此需要显示指明self
func(self, *args)
return warp


class A:
def __init__(self):
self.x = 1
self.y = 2

@dec1
def p(self, z):
print(self.y)
print(z)


a = A()
a.p(123) # 1 \n 2 \n 123

####### 将一个类的方法作为装饰器用于这个类的其他方法方法---一个错误示范

class A:
def __init__(self):
self.x = 1
self.y = 2

def a_dec(self, func):
def warp(self, * args):
print(self.x)
func(*args)
return warp

@ a_dec #由于这个方法是类普通方法,所以需要绑定到某个实例上,如@a.a_dec
# 如果a_dec是类方法,由于此时类还没创建出来,所以也不能用
# 因此不要把一个类的某个方法作为装饰器来作用在同一个类的另外一个方法上!
def p(self, z):
print(self.y)
print(z)


a = A()
a.p(123)

####### 将一个类的方法作为装饰器用于其他函数---处理self和cls

class A:
def dec1(self, func):
def warp(*args):
print("dec1")
func(*args)
return warp

@classmethod
def dec2(cls, func):
def warp(*args):
print("dec2")
func(*args)
return warp


a = A()

@a.dec1
def p1():
...

@A.dec2
def p2():
...

p1()
p2()


不要尝试将一个类中的某个方法作为装饰器用于同一个类的其他方法上

可以看更新的文章,给出了这样做的方法

元类

可以使用元类来管理类的创建,具体而言有实例、类、元类

实例 = 类() ===> 元类("类", "基类", "属性字典") 即调用元类的__init__方法

因此需要通过元类对类进行定制化修改往往在元类的__init__函数中进行(对属性字典)

另外,实例() ===> 类.__call__,而类本身是元类的对象,所以类() ===> 元类.__call__,在元类的call函数中会控制调用类的__new__、__init__方法,并返回结果。

所以类的创建顺序:

  • 实例 = 类() ===> 实例 = 元类("类", "基类", "属性字典")
  • 然后回调用元类的__new__方法生成出一个'对象',这个'对象'就是类,并调用元类的__init__来初始化类
  • 调用元类的__call__方法来生成类的对象实例,会依次调用类的__new__、init,并最终返回对象

至于在元类中重新定义__init__还是__new__,取决于如何具体操作。__new__会在类创建之前调用,如果想要修改属性字典的时候就可以重写new;__init__会在类完成创建后调用,不需要控制类的创建的时候就可以重写__init__方法。

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

class Mymeta(type):

def __new__(cls, *args, **kwargs):
print('type 调用了 Mymeta 的 new 方法--生成一个空对象,即 People 类')
"这里调用的是 type 的 new 方法,传入参数需要注意全部传会 type 元类"
return super().__new__(cls, *args, **kwargs)

def __init__(self, class_name, class_bases, class_dic):
print('初始化这个对象--- People 类,给 People 类添加额外的功能')
super(Mymeta, self).__init__(class_name,
class_bases, class_dic) # 重用父类的功能
# 自定义的类的功能
if not class_name.istitle():
raise TypeError('类名%s请修改为首字母大写' % class_name)

if '__doc__' not in class_dic or len(class_dic['__doc__'].strip(' \n')) == 0:
raise TypeError('类中必须有文档注释,并且文档注释不能为空')

# 传入 Mymeta的参数:People, 以及传入People的参数

def __call__(self, *args, **kwargs):
"""
self---<class '__main__.People'>
:param args: (1,)
:param kwargs: {'y': 2}
:return: 返回最终初始化好的代码
"""
print('调用了 Mymeta 的 call 方法')
# 调用 People 类里的 __new__方法,生成空对象
People_obj = self.__new__(self, *args, **kwargs)
# 调用 People 类里的 __init__方法,初始化空对象,注意:第一个传入的参数是生成好的空对象
self.__init__(People_obj, *args, **kwargs)
# 给 People 类生成的对象 obj 添加额外的功能
print("给 People 类生成的对象 obj 添加额外的功能")
People_obj.__dict__["新增一个属性"] = None
# 返回初始化好的对象
return People_obj


class People(metaclass=Mymeta):
"""People 类的注释"""

# 产生 People 类真正的对象
def __new__(cls, *args, **kwargs):
# 在这里就可以定制功能
print('生成 People 类的空对象')
print('传入的位置参数', args)
print('传入的位置参数', kwargs)
# 调用所继承的父类的__new__方法,这里就是 object 类,一定要传入 cls(当前这个类)
"这里要区别于自定义元类的 new 方法,自定义元类调用的是 type 的 new 方法,传入参数是不一样的"
return super().__new__(cls)

def __init__(self, x, y=None):
print("初始化 People 类的对象")
self.x = x
self.y = y
print("初始化 People 类的对象结束")


# 调用People 类生成对象---> People()= Mymeta.__call__()
obj = People(1, y=2)
print('最终的对象字典:', obj.__dict__)

'''

结果输出

type 调用了 Mymeta 的 new 方法--生成一个空对象,即 People 类
初始化这个对象--- People 类,给 People 类添加额外的功能
调用了 Mymeta 的 call 方法
生成 People 类的空对象
传入的位置参数 (1,)
传入的位置参数 {'y': 2}
初始化 People 类的对象
初始化 People 类的对象结束
给 People 类生成的对象 obj 添加额外的功能
最终的对象字典: {'x': 1, 'y': 2, '新增一个属性': None}
'''