【python】装饰器

来源:廖雪峰

 

看了好多次装饰器,发现还是廖老师讲得好,能让我看懂.....

 

下面是一段装饰器代码

@log
def now():
    print "20161107"

它的含义等价于

def now():
    print "20161107"

now = log(now)

即,log是一个函数,接收一个函数做参数,now变成了log(now)的返回值

 

下面,加上一个简单的log函数,只嵌套一层。

def log(func):
    print 'call %s():' % func.__name__
    return func

@log
def now():
    print "20161107"
print "-----"
now()

结果

call now():
-----
20161107

在log函数中打印了被调用函数的名称,但是一共只会运行一次,在定义的时候。之后每次运行now函数结果和不加装饰器相同。

 

两层嵌套

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

@log
def now():
    print "20161107"

print "-----"
now()
now()
print now.__name__

结果

-----
call now():
20161107
call now():
20161107
wrapper

可以看到,在两层嵌套中,可以实现每次运行now函数时都打印函数名。

在用log装饰后,now=log(now) 也就是wrapper函数,wrapper函数中封存了原本的now函数,采用可变参数,保证wrapper可以接收now函数的变量。wrapper中会先打印函数名,然后返回原本now函数的结果。

问题是,在打印名称时,now.__name__已经变成了wrapper。需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下

import functools

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

 

 

三层嵌套,实现装饰器参数

装饰器也是可以有参数的,比如@log("123")这样

下面是例子

def log(*args0, **kw0):
    def decorator(func):
        def wrapper(*args1, **kw1):
            if len(args0) == 0:
                print "args0 = None"
            else:
                print args0
            if len(kw0) == 0:
                print "kw0 = None"
            else:
                print kw0
            return func(*args1, **kw1)
        return wrapper
    return decorator


@log("huhuhuhu")
def a(x, y):
    print "A"
    return x + y


@log()
def b():
    print "B"

@log("c", t=1, e=2, s=3)
def c():
    print "C"

A = a(3,2)
print A
print "------------"
b()
print "------------"
c()

结果

('huhuhuhu',)
kw0 = None
A
5
------------
args0 = None
kw0 = None
B
------------
('c',)
{'s': 3, 'e': 2, 't': 1}
C

@log("huhuhuhu")的含义为:

now = log("huhuhuhu")(now)

       = decorator(now)

       = wrapper

可以看到,通过增加一层嵌套实现了装饰器参数

posted @ 2016-11-08 21:25  匡子语  阅读(260)  评论(0编辑  收藏  举报