1. 装饰器简介
1.1 函数装饰函数
python 中的装饰器也是语法糖的一种。
通常可以把装饰器理解为输入和输出都是函数的函数。
函数调用函数返回一个函数 (或者说 callab)。
装饰器的一大特性是,能把被装饰的函数替换成其他函数;第二个特性是,装饰器在加载模块时立即执行,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import time
def timeit(f): def wrapper(*args, **kwargs): start = time.time() ret = f(*args, **kwargs) print(time.time() - start) return ret
return wrapper
@timeit def myfunc(x): time.sleep(x)
|
带参数的装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def timeit(iteration): def inner(f):
def wrapper(*args, **kwargs): start = time.time() for _ in range(iteration): ret = f(*args, **kwargs) print(time.time() - start) return ret return wrapper
return inner
@timeit(10) def double(x): return x * 2
|
这里 timeit(10)
的返回值也是一个函数,并且这个函数的输入和输出都是函数
1.2 类装饰函数
类调用一个函数返回一个 object,依然是 callable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import time
class Timer: def __init__(self, func): self.func = func
def __call__(self, *args, **kwargs): start = time.time() ret = self.func(*args, **kwargs) print(f"Time: {time.time() - start}") return ret
@Timer def add(a, b): return a + b
if __name__=="__main__": print(add(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
| import time
class Timer: def __init__(self, prefix): self.prefix = prefix
def __call__(self, prefix):
def wrapper(self, *args, **kwargs): start = time.time() ret = self.func(*args, **kwargs) print(f"{self.prefix}{time.time() - start}") return ret return wrapper
@Timer(prefix="cost_time:") def add(a, b): return a + b
if __name__=="__main__": print(add(2, 3))
|
1.3 函数装饰类
装饰器去装饰类
当打印一个自定义类的时候,不会输出任何有价值的内容,只会告诉你这是一个人什么类,除非重载他的
__str__
方法来改变他的 print
结果,但是每次都重载很麻烦,所以可以写一个装饰器来装饰类用于重载其
__str__
方法。下面就是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| def add_str(cls): def __str__(self): return str(self.__dict__) cls.__str__ = __str__ return cls
@add_str class MyObject: def __init__(self, a, b): self.a = a self.b = b
if __name__=="__main__": obj = MyObject(1, 2) print(obj)
|
1.4 类装饰类?
emmm,本质上其实还是函数,是一个输入和输出都是类的函数
1.5 小结
其实只要转化为等价形式就知道装饰器在干什么了,因为装饰器的本质毕竟还是语法糖。
2. 一些常见的装饰器
2.1 @staticmethod
用于定义静态方法,意味着被它装饰的函数不需要创建类的实例(不需要传递
self
参数)就可以直接调用,即变为静态函数。
2.2 @classmethod
用于定义类的方法,被它装饰的函数第一个参数应该为类 cls
,变为类的方法,但不能访问实例的属性,因为传入的是类本身
cls
,而不是实例 self
。
类方法可以访问类的属性和其他类方法,但不能访问实例的属性。
1 2 3 4 5 6 7 8 9 10 11
| class MyClass: @staticmethod def static_method(x, y): return x + y
@classmethod def class_method(cls, x, y): return x * y
print(MyClass.static_method(2, 3)) print(MyClass.class_method(2, 3))
|
2.3 @property
用于将一个方法转化为属性,可以让你在访问属性时调用一个函数,从而封装对属性的访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Circle: def __init__(self, radius): self._radius = radius
@property def radius(self): return self._radius
@radius.setter def radius(self, value): if value < 0: raise ValueError("Radius cannot be negative") self._radius = value
c = Circle(5) print(c.radius) c.radius = 10 print(c.radius)
|