装饰器:python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能
def say(age):
print 'man is %d years old' % age
def outer(func):
def inner(age): #注意:当原函数有参数,装饰器的作用是为了操作原函数中的参数,所以给inner设置参数
if age < 0:
age = 0
func(age)
return inner
# "化妆过的函数"
say = outer(say)
# 其实是调用的新的函数
say(-10)
语法糖:使用@标识符将装饰器直接应用与函数
格式:@装饰器的名字(外部函数的函数名)
注意:使用@之后,装饰器必须先被定义,然后再使用原函数,此时原函数也相当于被增加新功能
@desc
这一句则是python中的一个语法糖,它告诉我们这里使用了装饰器,用desc
函数来装饰login(),在不改变源代码的情况下,增加了两条print语句
def desc(fun):
def add_info():
print '欢迎光临'
fun()
print '欢迎下次光临'
return add_info
#语法糖
@desc
def login():
print 'login....'
login()
带有不定长参数的装饰器(应用更广泛)
上面的例子是最简单的装饰器,但是有一个问题,如果被装饰的函数需要传入参数,那么这个装饰器就坏了。因为返回的函数并不能接受参数。Python提供了可变参数*args
和关键字参数**kwargs
,有了这两个参数,装饰器就可以用于任意目标函数了。
装饰器的需求:获取每个函数的执行时间
1).函数执行之前计算时间
2).函数执行之后计算时间
通过此例子,我们也可发现python内置函数的运行效率远大于使用一些循环来的快
import time
import string
import random
li= [random.choice(string.ascii_letters) for i in range(100)]
def timeit(fun):
def wrapper(*arges,**kwargs): # 接收可变参数和关键字参数
start_time=time.time()
fun(*arges,**kwargs)
end_time=time.time()
print '运行时间为:%.6f' % (end_time - start_time)
return wrapper
@timeit
def con_add():
s=''
for i in li:
s+=(i+',')
print s
@timeit
def join_add():
print ','.join(li)
@timeit
def fun_list(n):
print [2*i for i in range(n)]
@timeit
def fun_map(n):
print list(map(lambda x:x*2,range(n)))
con_add()
join_add()
fun_list(50)
fun_map(50)
通过上面的例子会出现以下问题1:被装饰的函数有返回值的时候怎么办
问题2:如何保留被装饰函数的函数名和帮助文档信息
问题1解决方法:
def timeit(fun):
"""这是一个装饰器材timeit"""
#@functools.wraps(fun)
def wrapper(*arges, **kwargs): # 接收可变参数和关键字参数
"""这是一个wrpper函数"""
# args:元组 kwargs:字典
# 函数执行之前
start_time = time.time()
# 执行函数
res = fun(*arges, **kwargs) #fun_list(50000)
# 函数执行之后
end_time = time.time()
print '运行时间为:%.6f' % (end_time - start_time)
return res
return wrapper
问题2解决方法:import functools
import time
import functools
def timeit(fun):
"""这是一个装饰器材timeit"""
@functools.wraps(fun)
def wrapper(*arges, **kwargs): # 接收可变参数和关键字参数
"""这是一个wrpper函数"""
start_time = time.time()
res = fun(*arges, **kwargs) #fun_list(50000)
end_time = time.time()
print '运行时间为:%.6f' % (end_time - start_time)
return res
return wrapper
@timeit
def fun_map(n):
"""这是fun_map函数"""
return list(map(lambda x:x*2,range(n)))
print fun_map.__name__
print fun_map.__doc__
不加时,不会显示fun_map函数的名字和文档描述,默认显示的是wrapper函数的名字和文档描述
带有参数的装饰器:
import functools
import time
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') 返回值:add_log
# add = add_log(add)
@log('debug')
def add(x,y):
time.sleep(1)
return x+y
print add(1,2)
带有多个装饰器的函数
需求: 用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数
需求: 判断登陆用户是否未管理员is_admin(此处管理员只有一个为:admin用户)
1).如果用户为管理员, 则执行被装饰的函数;
2).如果用户不是管理员, 则报错;
import functools
login_users = ['admin', 'root']
def is_admin(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
if kwargs.get('name') == 'admin':
res = fun(*args,**kwargs)
return res
else:
return 'Error:您没有权限访问该网站'
return wrapper
def is_login(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
# 判断要写博客的这个用户是否登陆成功
if kwargs.get('name') in login_users:
res = fun(*args, **kwargs)
return res
else:
res = login()
return res
return wrapper
@is_login
@is_admin
def writeBlog(name):
return '编写博客'
def login():
return '登陆。。。'
print(writeBlog(name='root'))
装饰器练习
1.创建装饰器,要求如下:
1).创建add_log装饰器,被装饰的函数打印日志信息
2).日志的格式为:[字符串时间] 函数名:xxx,运行时间:xxx,返回值结果:xxx
def add_log(fun):
@functools.wraps(fun)
def inner(*args,**kwargs):
start_time1 = time.time()
res = fun(*args, **kwargs)
end_time1= time.time()
print '[%s] 函数名:%s,运行时间:%d,返回值结果:%d'%(time.ctime(),fun.__name__,end_time1-start_time1,res)
return res
return inner
@add_log
def add(x,y):
time.sleep(1)
return x+y
print add(2,3)
def required_ints(fun):
@functools.wraps(fun)
def inner(*args,**kwargs):
for i in args:
if isinstance(i,int):
pass
else:
print '函数的所有参数并非整形'
else:
res=fun(*args,**kwargs)
return res
return inner
@required_ints
def add(a,b):
return a+b
print add(2,3)
print add(2,3.5)
def required_types(*kind):
def require(fun):
@functools.wraps(fun)
def wrapper(*args,**kwargs):
for i in args:
if isinstance(i,kind):
pass
else:
print '函数所有的参数并非',kind
break
else:
res=fun(*args,**kwargs)
return res
return wrapper
return require
@required_types(int,float)
def add(a,b):
return a+b
print add('hello','world')
print add(2,3)
@required_types(list)
def add(a,b):
return a+b
print add([1,2,3],[5,7,6])
print add(2,3)
print isinstance(2.3,float) #判断是否为float型
print isinstance(123,int) #判断是否为int型
print isinstance('abc',str) #判断是否为string型
print isinstance(1.2,(int,str)) #判断是否为int或str型