1,函数装饰器:
是个函数,在不改变原函数的基础上,给函数增加功能"
把一个函数当作参数,返回一个替代版的函数
本质上:返回函数的函数
装饰器的应用场景:
- 计时器
- 记录日志
- 用户登录验证
- 函数参数验证
代码示例1:
# def outer(func): 传入参数为函数
# def inner(): 函数嵌套
# print '*********'
# func() 闭包(函数作用域)
# print '&&&&&&&&&&&&'
# return inner 返回函数名
被装饰的函数
# def func1():
# print 'have a nice day!'
装饰完成后的函数
# func1 = outer(func1)
# func1()
代码示例2:
def outer(func):
def inner(age):
if age < 0:
age = 0
func(age)
return inner
被装饰的函数
def say(age):
print 'man is %d years old' % age
化妆过的函数
say = outer(say) 运行say函数,返回say函数的内存地址
其实是调用的新的函数
say(-10)
现在既没有改变say(age)函数,还在原来的基础上增加新的功能,实现了装饰
2,语法糖
语法糖是一种快捷的调用装饰器的方法
用@符号来调用装饰器
def outer(func):
def inner(age):
if age < 0:
age = 0
func(age)
return inner
@outer
def say(age):
print 'man is %d years old' % age
say(3) 执行函数要进行判断必须输入确切的数值
说明:@say(age)等价于say=out(age)
3,函数返回值的问题
不解决这个问题的后果是无法执行被装饰函数,只能执行装饰器
图
解决办法:用一个函数接收效果如图
顺便补充下不支持中文在代码最前端加上# _*_coding:utf-8 _*_
下面是两图对比看实现返回值的效果
4,含参数的函数装饰器
对于被装饰器,他可以装饰很多的函数,为了适应被装饰函数输入各种各样的参数
装饰器解决的方法是应用了元组和字典的方式来存放任意数量的函数位置参数和
关键字参数,这样我们就很好的解决修饰器中的参数问题了
def run_time(fun):
def wrapper(*args, **kwargs):
starttime = time.time()
res=fun (*args, **kwargs)
endtime = time.time()
print (endtime - starttime)
return res
return wrapper
@run_time
def test2(name,age):
time.sleep(2)
print (name,age)
return 'hi!'
print test2('miao',20)
5,带有多个装饰器函数
需求:用户登陆验证的装饰器is_login
1). 如果用户登陆成功, 则执行被装饰的函数;
2). 如果用户登陆不成功, 则执行登陆函数
需求:判断登陆用户是否未管理员is_admin(此处管理员只有一个为:admin用户)
1).如果用户为管理员, 则执行被装饰的函数;
2).如果用户不是管理员, 则报错;
mport 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='admin'))

6,(1)如何保留被装饰函数的函数名和帮助文档信息
倒入functools包就可以打印出相关信息
import time
import functools
import random
import string
li = [random.choice(string.ascii_letters) for i in range(100)]
def timeit(fun):
@functools.wraps(fun)
def wrapper(*arges, **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
@timeit
def con_add():
s=''
for i in li:
s+=(i+',')
print s
con_add()
@timeit
def join_add():
print ','.join(li)
join_add()
print join_add:__name__
print join_adD:__doc__
同时可以看出,内置的连接字母的方法比自己写的函数要快
(2)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)
add(1,2)
(3)map函数的应用
map():python内置函数,接收两个参数,一个是函数,一个是序列
map依次将序列传递给函数,并把结果作为新的列表返回
匿名函数:
def f(x):
return x*x
print map(lambda x:x*x,[1,2,3,4,5])
以上面的装饰器为例:
@timeit
def fun_map(n):
"""这是fun_map函数"""
print list(map(lambda x:x*2,range(n)))
fun_map(5)
"""
return list(map(lambda x:x*2,range(n)))
print fun_map(50)
"""
7,升级版(有参数的装饰器)
编写装饰器required_types, 条件如下:
1). 当装饰器为@required_types(int,float)确保函数接收到的每一个参数都是int或者float类型;
2). 当装饰器为@required_types(list)确保函数接收到的每一个参数都是list类型;
3). 当装饰器为@required_types(str,int)确保函数接收到的每一个参数都是str或者int类型;
4). 如果参数不满足条件, 打印 TypeError:参数必须为xxxx类型
import functools
def required_types(*kides):
def required(fun):
@functools.wraps(fun)
def wrapper(*args, **kwargs):
for i in args:
if isinstance(i, kides):
pass
else:
print '函数所有的次参数并非', kides
break
else:
res = fun(*args, **kwargs)
return res
return wrapper
return required
@required_types(str,str)
def add(a, b):
return a + b
print add('hello','python')