python 装饰器及标准库functools中的wraps

混江龙づ霸主 提交于 2020-01-07 18:23:35

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

最近在看 flask的视图装饰器 时,忽然想起预(复)习一下python的装饰器.

这里有一篇比较好的讲解装饰器的书写的 Python装饰器学习(九步入门) .

这里不单独记录装饰器的书写格式了,重点是工作流程.

首先常见的 装饰器 格式就是通过@语法糖,简便的写法,让流程有些不太清楚.

 

装饰器不带参数的情况下:

def deco(func):
    def _deco():
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")
    return _deco
 
@deco
def myfunc():
    print(" myfunc() called.")
 
myfunc()

 

运行结果:

before myfunc() called.
myfunc() called.
after myfunc() called.
myfunc() called.

 

这个@语法糖的作用是:

def myfunc():
    print(" myfunc() called.")
myfunc = deco(myfunc)

 

也就是现在的myfunc不再是一开始定义的那个了,而变成了

    def _deco():
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")

 

这一点可以通过

  print myfunc.__name__

 

而复杂一点的,装饰器带参数的,如:

def deco(arg="haha"):
    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()#注意有括号
def myfunc():
    print(" myfunc() called.") 
@deco("haha1")
def myfunc1():
    print(" myfunc() called.")

myfunc()
myfunc1()

 

实际的操作是,先把装饰进行了运算,即函数deco先被调用

等效于:

def _deco(func):
    def __deco():
        print("before %s called [%s]." % (func.__name__, "haha"))# arg ==> "haha"
        func()
        print("  after %s called [%s]." % (func.__name__, "haha"))# arg ==> "haha"
    return __deco
@d_deco#注意没有括号,第一处
def myfunc():
    print(" myfunc() called.") 
@_deco#这也没括号,第二处
def myfunc1():
    print(" myfunc1() called.")

myfunc()
myfunc1()

 

而参数arg 使用的是默认的"haha

更直观的表达方式就是:

def deco(arg="haha"):
    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
def myfunc():
    print(" myfunc() called.") 
def myfunc1():
    print(" myfunc() called.")

myfunc = deco()(myfunc)
myfunc1 = deco("haha1")(myfunc1)

 

这时再来看标准库functools中的wraps的使用,比如官网例子:

from functools import wraps
def my_decorator(f):
    @wraps(f)
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    return wrapper
@my_decorator
def example():
     """Docstring"""
     print 'Called example function'
example()
print example.__name__
print example.__doc__

 

过程就是

def my_decorator(f):
    def wrapper(*args, **kwds):
        print 'Calling decorated function'
        return f(*args, **kwds)
    wrapper.__name__ = f.__name__
    wrapper.__doc__  = f.__doc__
    return wrapper
example = my_decorator(example)

 

这样就保留了原函数名称属性和doc,

标准库中函数wraps,可以这样理解:

def wraps(f):
    def _f(*args,**kwargs):
        f(*args,**kwargs)
    _f.__name__ = f.__name
    _f.__doc__  = f.__doc__
    return _f

 

上面的wraps流程可以看出,如果直接使用wraps简直就是f = f(其实不能直接使用),所以一般都是如实例这样包藏在一个装饰器函数内部.

注:示例代码来自:Python装饰器学习(九步入门)python标准库functools之wraps

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!