小李飞刀:python请与我一起愉快的玩耍吧!

又是一段叨叨

如果总是担心能不能做到的话,那就干脆不要做了。
想做的事情还是一往无前的好,什么都不想,就努力冲冲冲吧!

装饰器

因为函数是一个对象。
所以。

  • __name__属性,可以get函数的名字.
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

本质上,decorator就是一个返回函数的高阶函数。

比如,定义一个能打印日志的decorator。

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

log接受函数作为参数,要放置在函数定义处。

@log
def now():
    print('2015-3-25')

小李飞刀:python请与我一起愉快的玩耍吧!

这样调用now()的时候,就会先打印出程序名称。
@log放到now()函数的定义处,相当于执行了语句

now = log(now)

由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。【此时的now已经不是原来的now了,只是个同名的朋友】

当需要再进一步的时候,比如decorator本身需要传入参数,就需要再改造下函数。
小李飞刀:python请与我一起愉快的玩耍吧!

这个三层嵌套的效果如下:

now = log('execute')(now)

但是也存在问题,因为函数的名字now.__name__此时已经变成了wrapper,所以需要把原始函数的名称等属性复制到wrapper()中,否则一些依赖于函数签名的代码执行就会出错。

Python内置的functools.wraps就可以用来处理这个问题。
一个完整的decorator的代码如下:

import functools

def log(func):
    @functools.wraps(func)  #记录的是传入的func的值,位置在嵌套内
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

或者带参数的如下。

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func) #记录的是传入的func的值,位置在嵌套内
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@functools.wraps(func)加上这句很重要

相关推荐