装饰器2

装饰器2


  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# 例如功能性的装饰器, 如记录已经加载的类, 如标记一个类是特殊类型等等