装饰器(Decorator)本质是函数,功能是为其余函数添加附加功能,定义一个装饰器须要知足如下两个原则:python
装饰器 = 高阶函数 + 函数嵌套 + 闭包数据库
高阶函数定义:闭包
知足以上任意一个条件就是高阶函数。app
def calc(): print('calc 运行完毕~') f = calc f()
因为函数也是对象,所以函数也能够赋值给一个变量,调用这个变量就是调用函数。函数对象有一个 __name__
属性,用于查看函数的名字:框架
calc.__name__ f.__name__ 'calc' 'calc'
需求:函数
在不修改 calc
源代码的前提下,为 calc
函数添加一个代码计时功能(计算 calc
运行时间)。网站
使用高阶函数模拟装饰器实现calc
计时功能。设计
import time def timmer(func): start_time = time.time() func() stop_time = time.time() print('函数 %s 运行时间为:%s' % (func.__name__, stop_time - start_time)) def calc(): time.sleep(2) print('calc 运行完毕~') timmer(calc)
calc 运行完毕~ calc 运行时间为:2.0011146068573
虽然为 calc
增长了计时功能,但 calc
原来的执行方式是 calc()
,而如今是调用高阶函数timmer(calc)
,改变了调用方式。日志
import time def timmer(func): # func: calc start_time = time.time() func() # func(): calc() stop_time = time.time() print('函数 %s 运行时间为:%s' % (func.__name__, stop_time - start_time)) return func # func: calc (<function calc at 0x00000000052D6D90>) 内存地址 def calc(): time.sleep(2) print('calc 运行完毕~') calc = timmer(calc) # calc = timmer(calc): calc = <function calc at 0x00000000052D6D90> calc()
calc 运行完毕~ calc 的运行时间是:2.0001144409179688 calc 运行完毕~
没有改变 calc
的调用方式,但也没为其添加新功能。code
使用高阶函数实现装饰器功能 :
函数中嵌套另外一个函数
def father(name): print('I am %s' % name) def son(): print('My father is %s' % name) def grandson(): print('My grandfather is %s' % name) grandson() son() father('tom')
I am tom My father is tom My grandfather is tom
闭包也是函数嵌套,若是在一个内部函数里调用外部做用域(不是全局做用域)的变量,那么这个内部函数就是闭包(closure)
def father(name): print('I am %s' % name) def son(): name = 'john' def grandson(): print('My father is %s' % name) grandson() son() father('tom')
I am tom My father is john
内部函数 grandson 调用了它的外部函数 son 的变量 name='john'
,那么 grandson就是一个闭包。
无参数装饰器 = 高阶函数 + 函数嵌套
# 实现一个装饰器的基本框架 def timmer(func): def wrapper(): func() return wrapper
def timmer(func): def wrapper(*args, **kwargs): func(*args, **kwargs) return wrapper
import time def timmer(func): def wrapper(*args, **kwargs): """计时功能""" start_time = time.time() func(*args, **kwargs) stop_time = time.time() print('函数 %s 运行时间:%s' % (func, stop_time - start_time)) return wrapper
import time def timmer(func): def wrapper(*args, **kargs): start_time = time.time() res = func(*args, **kwargs) stop_time = time.time() print('函数 %s 运行时间:%s' % (func, stop_time - start_time)) return res return wrapper
def calc(): time.sleep(2) print('calc 运行完毕~') return 'calc 返回值' calc = timmer(calc) calc()
@timmer # 至关于 calc = timmer(calc) def calc(): time.sleep(2) print('calc 运行完毕~') calc()
使用无参数装饰器,为 calc
添加计时功能(统计 calc
代码运行时间)
import timmer def timmer(func): # func: calc """装饰器函数""" def wrapper(*args, **kwargs): # args:<class 'tuple'>:('rose', 18, 'female') start_time = time.time() # kwargs={} res = func(*args, **kwargs) # res: 'calc 返回值' stop_time = time.time() print('函数 %s 运行时间:%s' % (func.__name__, stop_time - start_time)) return res # func: calc (<function calc at 0x00000000052D6D90>) 内存地址 return wrapper @timmer # @timmer: calc=timmer(calc) ——>calc = <function wrapper at 0x00000000052D6D90> def calc(name, age, gender): """被修饰函数""" time.sleep(2) print('calc 运行完毕~') print('名字:%s,年龄:%d,性别:%s' % (name, age, gender)) return 'calc 返回值' s = calc('rose', 18, 'female') # 至关于执行 s = wrapper('rose', 18, 'female') print(s)
calc 运行完毕 名字:rose,年龄:18,性别:female 函数 calc 运行时间:2.0001144409179688 calc 返回值
因为 timmer()
是一个装饰器函数,返回一个函数 wrapper
。因此 calc()
函数仍然存在,只是如今同名的 calc
变量指向了新的函数,因而调用 calc()
执行的是wrapper()
函数。
有参数装饰器 = 高阶函数 + 函数嵌套 + 闭包
若是装饰器本山须要传入参数,就须要再编写一个 decorator
的高阶函数。好比给 calc
函数添加一个日志功能,可以打印日志,其基本框架以下:
def log(text): def timmer(func): def wrapper(*args, **kwargs): func(*args, **kwargs) return wrapper return timmer @log('文本内容') def calc(): pass
示例:
import timmer def log(text): # text: '文本内容' def timmer(func): # func: calc """装饰器函数""" def wrapper(*args, **kwargs): # args:<class 'tuple'>:('rose', 18, 'female') start_time = time.time() # kwargs={} res = func(*args, **kwargs) # res: 'calc 返回值' stop_time = time.time() print('函数 %s 运行时间:%s' % (func.__name__, stop_time - start_time)) return res # func: calc (<function calc at 0x00000000052D6D90>) 内存地址 return wrapper return timmer @log('文本内容') # 至关于 calc = log('自定义文本')(calc) ——> timmer(calc) ——> calc=wrapper def calc(name, age, gender): """被修饰函数""" time.sleep(2) print('calc 运行完毕~') print('名字:%s,年龄:%d,性别:%s' % (name, age, gender)) return 'calc 返回值' s = calc('rose', 18, 'female') # 至关于执行 s = wrapper('rose', 18, 'female') print(s)
与两层嵌套效果的decorator
相比,三层效果是这样的:
calc = log('自定义文本')(calc) # 即 calc = wrapper
首先执行 log('自定义文本')
,返回timmer
函数,再调用返回参数(timmer(calc)
),参数是 calc
,返回值是wrapper
函数,最后再调用wrapper()
。
函数也是对象,也有__name__
属性(返回函数名)。但通过装饰的calc
函数,它的__name__
从原来的calc
变成了wrapper
。
>>> calc.__name__ 'wrapper'
有些须要依赖函数签名的代码由于__name__
改变,而出现某些错误,因此须要将原calc
的__name__
属性复制到wrapper()
函数中。
import functools .... @functools.wraps(func) def wrapper(*args, **kwargs) ...
实例 1
请设计一个decorator,它可做用于任何函数上,并打印该函数的执行时间:
import time import functools def metric(fn): @functools.wraps(fn) def wrapper(*args, **kwargs): start_time = time.time() res = fn(*args, **kwargs) stop_time =time.time() print('%s executed in %s ms' % (fn.__name__, stop_time - start_time)) return res return wrapper @metric # fast=metric(calc) def fast(x, y): time.sleep(0.0012) return x + y @metric def slow(x, y, z): time.sleep(0.1234) return x * y * z f = fast(11, 22) s = slow(11, 22, 33) print(f, s)
fast executed in 0.0019998550415039062 ms slow executed in 0.1240072250366211 ms 33 7986
实例 2
实现一个购物网站基本功能,其功能以下:
import time import functools # 模拟存储用户名、密码数据库 user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic = {'username': None, 'login': False} # 用于保存登陆记录,None,False 为没有登陆 def auth_func(func): """装饰器函数""" @functools.wraps(func) def wrapper(*args, **kwargs): # 仅供 home、shopping 调用(由于不须要再输入用户名和密码) if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username = input('用户名:').strip() passwd = input('密码:').strip() # 遍历循环用户名、密码数据库,比对用户输入的用户名和密码 for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: # 用户保持会话,即保持用户登陆状态,赋值 current_dic['username'] = username current_dic['login'] = True res = func(*args, **kwargs) return res else: print('用户名或密码错误') return wrapper @auth_func def index(): """主页(须要登陆)""" print('欢迎来到xxx主页!') @auth_func def home(name): """用户登陆成功后的界面""" print('欢迎回家 %s' % name) @auth_func def shopping_car(name): """购物车""" print('%s购物车里有:[%s,%s,%s]' % (name, '游艇', '车子', '飞机')) print('before-->',current_dic) index() print('after-->',current_dic) home('rose') shopping_car('rose')
before--> {'username': None, 'login': False} 用户名:alex 密码:123 欢迎来到京东主页! after--> {'username': 'alex', 'login': True} 欢迎回家 rose rose购物车里有:[游艇,车子,飞机]
带参数装饰器(须要认证类型):
import time import functools user_list=[ {'name':'alex','passwd':'123'}, {'name':'linhaifeng','passwd':'123'}, {'name':'wupeiqi','passwd':'123'}, {'name':'yuanhao','passwd':'123'}, ] current_dic={'username':None,'login':False} def auth(auth_type='filedb'): def auth_func(func): @funtools.wraps(func) def wrapper(*args,**kwargs): print('认证类型是',auth_type) if auth_type == 'filedb': if current_dic['username'] and current_dic['login']: res = func(*args, **kwargs) return res username=input('用户名:').strip() passwd=input('密码:').strip() for user_dic in user_list: if username == user_dic['name'] and passwd == user_dic['passwd']: current_dic['username']=username current_dic['login']=True res = func(*args, **kwargs) return res else: print('用户名或者密码错误') elif auth_type == 'ldap': print('ldap 认证类型') res = func(*args, **kwargs) return res else: print('不知道什么认证方式') res = func(*args, **kwargs) return res return wrapper return auth_func # 至关于 auth_func = auth(auth_type='filedb')(auth_func) @auth(auth_type='filedb') def index(): print('欢迎来到xxx主页') @auth(auth_type='ldap') def home(name): print('欢迎回家%s' %name) # @auth(auth_type='sssssss') def shopping_car(name): print('%s的购物车里有[%s,%s,%s]' %(name,'奶茶','妹妹','娃娃')) # print('before-->',current_dic) # index() # print('after--->',current_dic) # home('产品经理') shopping_car('产品经理')