python 装饰器

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):
# 使用 *args, **kwargs 允许函数接受变长的参数
# 就是说你这个函数来了什么参数 我都能接受
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)

# 等价于下面这种写法
# def myfunc(x):
# time.sleep(x)
# myfunc = timeit(myfunc)

带参数的装饰器:

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

# 上面等价于:
# def double(x):
# return x * 2
# double = timeit(10)(double)

这里 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

# 变为 callable 的
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

# 等价于
# def add(a, b):
# return a + b
# add = Timer(add)
# 相当于把一个函数变为了一个类的对象
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

# 变为 callable 的
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

# 等价于
# def add(a, b):
# return a + b
# add = Timer(prefix="cost_time")(add)
# 相当于把一个函数变为了一个类的对象
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

# 等价于:
# class MyObject:
# def __init__(self, a, b):
# self.a = a
# self.b = b
# add_str(MyObject)


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)) # 输出: 5
print(MyClass.class_method(2, 3)) # 输出: 6

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 # 只读属性

# @<property_name>.setter 是一个装饰器,
# 用于为 @property 装饰的属性提供设置器即定义属性的写操作。
# 通过这个装饰器,你可以指定如何修改某个属性的值,从而让这个属性变为可写的。
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value

c = Circle(5)
print(c.radius) # 输出: 5
c.radius = 10 # 设置 radius
print(c.radius) # 输出: 10
-------------本文结束 感谢您的阅读-------------