Wetts's blog

Stay Hungry, Stay Foolish.

0%

Python中的装饰器

转自:https://blog.csdn.net/weixin_41179709/article/details/81879601

装饰器介绍

装饰器就是对被装饰的对象(函数、类)进行重构的,其可以在不改变原来对象的情况下调用对象时执行重构后的行为

  1. 解决问题:在函数执行之前和执行之后添加功能,调用函数的方式改变了
  2. 不改变原有函数的调用方法:函数里面嵌套函数,并且返回嵌套的函数

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
##引题:当登陆某系统时常常会有欢迎语,但修改时将在登陆函数中修改,可能会出现问题,所以避免直接侵入原函数修改。
def login():
print("中秋快乐")
print("login....")
print("欢迎您下次光临....")
login()

##为了避免在大的函数块中操作,将欢迎语独立出来,另建函数。
def desc(fun):
def add_info():
print("中秋快乐")
fun()
print("欢迎您下次光临")
return add_info
def login():
print("login....")
login = desc(login)
login()

装饰器语法糖

在Python中,可以使用”@”语法糖来精简装饰器的代码,把 decorator 置于函数的定义处,免去给函数重新赋值(即function = decorator(funtion))

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import functools           #functools 标准库
import time #time 时间函数

def add_log(fun):
@functools.wraps(fun) #不用此模块add(x,y)函数的__name__,__doc__都会丢失
def wrapper(*args,**kwargs): #*args为元组 **kwargs为字典
start_time = time.time()
res = fun(*args,**kwargs)
end_time = time.time()
print("[%s] 函数名:%s, 运行时间 :%.5f ,运行返回值:% d"
%(time.ctime(),fun.__name__,end_time- start_time,res))
return res #返回值为所要装饰的函数
return wrapper #返回值为所要修饰函数所添加的模块
@add_log ##调用语法糖
def add(x,y):
time.sleep(0.1)
return x+y
print(add(1,2))

装饰的函数有返回值

解决办法:给返回值赋值

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def timeit(fun):
def wrapper(*args,**kwargs): #接收可变参数和关键字参数
#args:元组 kwargs:字典 args=(5,)
#原函数执行之前
start_time = time.time()
#执行函数
res = fun(*args,**kwargs) #args解包
#执行函数
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper

@timeit
def list_create(n):
return(i*2 for i in range(n)])

@timeit
def map_create(n):
return(list(map(lambda x:x*2,range(n))))

list_create(5)
map_create(5)
print(list_create(100)) ##wrapper(100)

保留被装饰的函数的函数名和帮助文档信息

解决办法:导入functools标准库,使用functools.wraps函数,保留被装饰的函数的函数名和帮助文档信息。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import  functools
def timeit(fun):
"""这是一个装饰器timeit"""
@functools.wraps(fun) ##内置装饰器
可以保留被装饰的函数的函数名和帮助文档信息
def wrapper(*args,**kwargs): #接收可变参数和关键字参数
"""这是一个wrapper函数"""
#args:元组 kwargs:字典 args=(5,)
#原函数执行之前
start_time = time.time()
#执行函数
res = fun(*args,**kwargs) #args解包
#执行函数
end_time = time.time()
print("运行时间为:%.6f" % (end_time - start_time))
return res
return wrapper
@timeit
def fun():
print("hello")
print(fun_list.__name__)
print(fun_list.__doc__)

装饰器中不同条件下执行不同的函数

用实例说明:需求: 用户来CSDN登陆验证的装饰器is_login

  1. 如果用户登陆成功, 则执行被装饰的函数;
  2. 如果用户登陆不成功, 则执行登陆函数
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
import functools
login_users = ['admin', 'root']

def is_login(fun): # fun: writeBlog
@functools.wraps(fun)
def wrapper(*args, **kwargs): # name="admin" # kwargs={"name":"admin"}
# 判断写博客的这个用户是否登陆成功;
if kwargs.get("name") in login_users:
res = fun(*args, **kwargs)
return res
else:
res=login()
return res

return wrapper

# 必须登陆成功
@is_login # writeBlog = is_login(writeBlog)
def writeBlog(name):
return "编写博客"

def login():
return "登陆。。。。"

# 是否登陆成功都可以执行代码
def news():
print("新闻......")

print(writeBlog(name="admin"))

多个装饰器

若有两个装饰器,从上到下调用装饰器,wrapper内容也是为由上向下执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def makebold(fun):
print("bold1")
def wrapper1(*args, **kwargs):
print("bold2")
return fun(*args, **kwargs) # wrapper
return wrapper1
def makei(fun): # fun=login
print("i1")
def wrapper(*args, **kwargs):
print("i2")
return fun(*args, **kwargs)
return wrapper
# 当有多个装饰器时,从上到下调用装饰器
# 真实wrapper内容执行是从上到下执行
@makebold #login = makebold(login) #login为wrapper1
@makei #login = makei(login) #login为wrapper
def login():
return "登陆"
print(login())

带有参数的装饰器

创建装饰器, 要求如下:

  1. 创建add_log装饰器, 被装饰的函数打印日志信息;
  2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx
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
# 创建装饰器, 要求如下:
# 1. 创建add_log装饰器, 被装饰的函数打印日志信息;
# 2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx


import functools
import time

# format
def log(kind): # kind="debug"
def add_log(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
start_time = time.time()
res = fun(*args, **kwargs)
end_time = time.time()
print("<%s> [%s] 函数名: %s, 运行时间:%.5f, 运行返回值结果:%d"
%(kind, time.ctime(), fun.__name__, end_time-start_time, res )
)
return res
return wrapper
return add_log
@log("debug")
# log("debug")==> 返回值是add_log
# add=add_log(add)
def add(x,y):
time.sleep(0.1)
return x+y
print(add(14214124124,1241231231313))
# wrapper(14214124124,1241231231313)