1"""
2Author: wangy325
3Date: 2024-07-24 10:20:20
4Description: 介绍python内建的装饰器和更多内容
5"""
6
7
8# 以下是python标准装饰器
9# ########################################## #
10# @property 可以将方法转换为属性访问 #
11# @staticmethod 定义静态方法 #
12# @classmethod 定义类方法 #
13# @abstractmethod 定义抽象方法 #
14# @dataclass 定义数据类 #
15# ########################################## #
16
17
18class MyClass:
19 """
20 'name': str,
21 'age': int
22 """
23
24 def __init__(self, name: str, age: int):
25 self._name = name
26 self.age = age
27
28 @property
29 def name(self):
30 return self._name
31
32 @staticmethod
33 def add(x, y):
34 """
35 静态方法不包含隐式参数cls
36
37 所以可以处理任何业务
38
39 一般用来处理和类相关, 但是又不需要访问类的变量和方法的操作
40 :param x: int
41 :param y: int
42 :return: val of x plus y
43 """
44 return x + y
45
46 @classmethod
47 def get_instance(cls):
48 """
49 类方法和普通方法不一样, 普通方法默认包含隐含参数self, 代表实例
50
51 类方法默认包含隐含参数cls, 代表类
52 :return: 一个实例
53 """
54 return cls('anna', 18)
55
56 @classmethod
57 def from_dict(cls, data: dict):
58 return cls(data['name'], data['age'])
59
60
61# 直接访问类方法
62instance = MyClass.get_instance()
63from_dict = MyClass.from_dict({'name': 'alice', 'age': 18, 'gender': 'male'})
64# name()方法被当作属性访问了
65print(instance.name)
66# 看看静态方法
67print(f"call static method: {MyClass.add(1, 2)}")
68
69
70# 9_decoration.py 中的装饰器都是基于方法实现, 并且在方法上使用
71# 但是, 装饰器也可以基于类实现, 以及在类上使用
72# 例如下面的使用缓存的栗子
73#
74# Tips: __call__() 方法是一个特殊方法,它使你的类实例可以像函数
75# 一样被调用。换句话说,当你使用 () 运算符调用一个类的实例时,实际
76# 上会调用这个实例的 __call__() 方法
77
78
79class MyCache:
80 """
81 装饰器类
82
83 需要实现 __init__和__call__方法
84
85 其中__init__用来初始化以及传递装饰器参数
86
87 __call__方法是装饰器的具体方法
88 """
89 caches = {}
90
91 def __init__(self, key):
92 self.key = key
93
94 def __call__(self, func):
95 def wrapper(*args, **kwargs):
96 cache_key = self.key(*args, **kwargs)
97 if cache_key not in self.caches:
98 self.caches[cache_key] = func(*args, **kwargs)
99 return self.caches[cache_key]
100
101 return wrapper
102
103 @staticmethod
104 def get_caches():
105 return MyCache.caches
106
107
108@MyCache(key=lambda x, y: (x, y))
109def s_val(x, y):
110 return x + y
111
112
113s_val(1, 2)
114s_val(2, 3)
115
116# 等价于以下函数调用式
117# MyCache(key=lambda x, y: (x, y))(s_val)(3, 4)
118print(MyCache.get_caches())
119
120
121# 使用装饰器装饰类
122# 一般不建议这么做
123# 抽象和继承是更好的办法
124# 下面是一个简单的栗子
125def rel_cache(cls):
126 print(f"class {cls} has been decorated.")
127 # seems not work
128 cls.x = 100
129 return cls
130
131
132@rel_cache
133class rel:
134 x, y = 1, 2
135
136 def __init__(self, x, y):
137 self.x = x
138 self.y = y
139
140 def add(self):
141 return self.x + self.y
142
143
144rel1 = rel(3, 4)
145# 等价于
146# rel2 = rel_cache(rel(3, 4))
147
148print(rel1.add())
149
150
151# 一个奇怪的功能, 装饰器可以给类添加新的功能
152# 估计应该, 也许, 可能没什么用
153# 用继承实现, 更直观
154
155def class_dec(func):
156 def wrapper(cls):
157 cls.new_func = func
158 return cls
159
160 return wrapper
161
162
163def new_func(*args):
164 print("new func here.")
165
166
167@class_dec(new_func)
168class c:
169 pass
170
171
172c1 = c()
173c1.new_func()
174
175# 综上, 类可以用作装饰器
176# 但是不要在类上使用装饰器, 这样不如继承来得自然
177# 而且装饰器的粒度应该是方法级别的,
178# ?? 极少数的可能需要类级别 ?? 标准库中是否存在用于类的装饰器??
179# 例如功能性的装饰器, 如记录已经加载的类, 如标记一个类是特殊类型等等