装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直接看示意图,其中 a 为与装饰器 @a 对应的函数, b 为装饰器修饰的函数,装饰器@a的作用是:
简而言之:@a 就是将 b 传递给 a(),并返回新的 b = a(b)
在python中常看到在定义函数是使用@func. 这就是装饰器, 装饰器是把一个函数作为参数的函数,常常用于扩展已有函数,即不改变当前函数状态下增加功能.
def run(): print "I'm run."
我有这么一个函数, 我想知道这个函数什么时候开始什么时候结束. 我应该这么写:
def run(): print time.ctime() print "I'm run." print time.ctime()
但是如果不允许修改函数的话就需要装饰器了
def count(func): def wrapper(): print time.ctime() ret = func() print time.ctime() return ret return wrapper @count def run(): print "I'm run."
再看一个例子:
def now(): print '2016-9-10' f = now f()
函数有一个__name__ 对象 可通过 dir(func) func为定义的函数名
now.__name__ # print 'now' f.__name__ # print 'now' print f # print '<function now at 0x000000000213A908>' print now # print '<function now at 0x000000000213A908>'
我们通过装饰器打印log日志
def log(func): def wrapper(*args, **kwargs): print "call %s()" % func.__name__ return func(*args, **kwargs) return wrapper @log def now(): print '2015-4-10' now() # print 'call now()'
其实装饰器修饰函数相当于, now = log(now) 也就是装饰器函数把被修饰的函数当参数后赋给同名的变量
思考的一个例子:用python 装饰器打log(不明白往后看)
#! /usr/bin/env python # coding=utf-8 from time import time def logged(when): def log(f,*args,**kargs): print("called: function:%s,args:%r,kargs:%r"%(f,args,kargs)) def pre_logged(f): def wrapper(*args,**kargs): log(f,*args,**kargs) return f(*args,**kargs) def post_logged(f): def wrapped(*args,**kargs): now=time() try: return f(*args,**kargs) finally: log(f,*args,**kargs) print("time delta:%s"%(time()-now)) return wrapped try: #从这里开始调用 return{"pre":pre_logged,"post":post_logged}[when] except Exception as e: print(e) @logged("post") def hello(name): print("hello",name) @logged("post") def test(a,b=1): print(a+b) hello("world") test(1,2)
functools.wraps 函数:
当我们使用了装饰器后now的__name__值发生了改变
# 没有使用前 now.__name__ # print 'now' # 使用后 now.__name__ # print 'wrapper'
当我们使用装饰器前,now.__name__使用的是当前now函数,但使用后 now这个函数其实是 log(now) 也就是log函数的返回值也就是被包裹的wrapper. 解决方法是functools.wraps函数.
装饰闭包, 使用前得调用 import functools
def log(func): @functools.wraps(func) def wrapper(*args, **kwargs): ...
带参数的装饰器
如果decorator需要传入参数, 那就需要在写一个返回decorator的高阶函数. 写出来更复杂.
def login(level): def _deco(func): def wrapper(*args, **kwargs): if level >= 5: print '用户 VIP 等级 %d' % int(level-5) else: print '用户 屌丝 等级 %d' % abs(level-5) return func(*args, **kwargs) return wrapper return _deco @login(5) def user(username): print 'welcome, %s' % username # 用户vip 等级0 # welcome, mink user('mink')
带参数的decorator等于func = 装饰器函数(装饰器参数)(func)
------------------------------------------------------------------
# -*- coding:gbk -*-
'''''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
def deco(func):
print("before myfunc() called.")
func()
print(" after myfunc() called.")
return func
@deco
def myfunc():
print(" myfunc() called.")
myfunc()
------------------------------------------------------------------------------
# -*- coding:gbk -*-
'''''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco():
print("before myfunc() called.")
func()
print(" after myfunc() called.")
# 不需要返回func,实际上应返回原函数的返回值
return _deco
@deco
def myfunc():
print(" myfunc() called.")
return 'ok'
myfunc()
------------------------------------------------------------------
# -*- coding:gbk -*-
'''''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print(" after myfunc() called. result: %s" % ret)
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
--------------------------------------------------------------------------
def deco(func):
def _deco(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print(" after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a+b
@deco
def myfunc2(a, b, c):
print(" myfunc2(%s,%s,%s) called." % (a, b, c))
return a+b+c
myfunc(1, 2)
myfunc(3, 4)
myfunc2(1, 2, 3)
-----------------------------------------------------------------
# -*- coding:gbk -*-
'''''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc() called.")
@deco("module2")
def myfunc2():
print(" myfunc2() called.")
myfunc()
myfunc2()
------------------------------------------------------------
# -*- coding:gbk -*-
'''''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
def deco(arg):
def _deco(func):
def __deco():
print("before %s called [%s]." % (func.__name__, arg))
func()
print(" after %s called [%s]." % (func.__name__, arg))
return __deco
return _deco
@deco("mymodule")
def myfunc():
print(" myfunc() called.")
@deco("module2")
def myfunc2():
print(" myfunc2() called.")
myfunc()
myfunc2()
------------------------------------------------------
装饰器类
通过类的__call__可以想使用函数一样使用类
class A(object): def __init__(self, func): self.func = func def __call__(self): return self.func() ** 2 @A def foo(): return 10 print foo() # print 100
- 本文固定链接: http://ttfde.top/index.php/post/313.html
- 转载请注明: admin 于 TTF的家园 发表
《本文》有 0 条评论