【Python学习之二】装饰器

装饰器

  首先,给出装饰器的框架:

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

@log
def now():
    print('2018-6-14')

  Python装饰器,本质上就是一个高阶函数。作用是给其它函数增加新的功能。借用python的@语法,可以将一个高阶函数定义为装饰器。

@符号的作用

  但是,在学习廖雪峰老师的Python教程时,似懂非懂,尤其是看到@这个符号的时候,感觉一头雾水。现在回想来看,只要百度一下就能知道@符号在Python中的作用。它的作用就是修饰一个函数。位置在被修饰的函数的前一行,@之后是修饰函数的函数。 例如上述的 @log 起到的作用就是相当于执行了语句: now = log(now) ,现在或许难以理解这行代码的作用。接下来,进行一个对比,就能知道装饰器的威力了。

使用装饰器之前

#给下列函数增加新功能:调用函数时,打印函数名。
#要求:(1)不改变函数的定义,不改变函数的调用方式。
def now():
    print('2015-3-25')

#增加一个高级函数 def call_name(func): def wrapper(
*args, **kw): print('call %s()' % func.__name__) return func(*args, **kw) return wrapper
#调用now函数 now
= call_name(now) #调用方式还是改变了(增加了一行) now()

使用装饰器之后

#给下列函数增加新功能:调用函数时,打印函数名。
#要求:(1)不改变函数的定义,不改变函数的调用方式。

#写一个装饰器
def call_name(func):
    def wrapper(*args, **kw):
        print('call %s()' % func.__name__)
        return func(*args, **kw)
    return wrapper


@call_name               #相当于执行语句 call_name(now)
def now():
    print('2015-3-25')


#调用now函数
now()                     #这才是真正没改变调用方式

  通过对比,我对装饰器的作用了解更加深刻了。下面是带参数的装饰器:

带参数的装饰器

  上例,如果装饰器call_name(func)本身还要带参数,那么需要更加复杂的高阶函数。

 1 def call_name(text):                   #text为装饰器的参数
 2     def decorator(func):
 3         def wrapper(*args, **kw):
 4             print('%s call %s()' % (text, func.__name__))
 5             return func(*args, **kw)
 6         return wrapper
 7     return decorator
 8 
 9 
10 @call_name('execute ')                #相当于 now = log('execute')(now)
11 def now():
12     print('2015-3-25')
13 
14 
15 # 调用now函数
16 now()

装饰器练习

  设计一个decorator(装饰器),它可作用于任何函数上,并打印该函数的执行时间:

 1 import time
 2 
 3 import functools
 4 
 5 
 6 def metric(fn):
 7     @functools.wraps(fn)
 8     def wrapper(*args, **kw):
 9         print('%s executed in %s' %
10               (fn.__name__, time.asctime(time.localtime(time.time()))))
11         return fn(*args, **kw)
12     return wrapper
13 
14 # 测试
15 
16 
17 @metric
18 def fast(x, y):
19     time.sleep(0.0012)
20     return x + y
21 
22 
23 @metric
24 def slow(x, y, z):
25     time.sleep(0.1234)
26     return x * y * z
27 
28 
29 f = fast(11, 22)
30 s = slow(11, 22, 33)
31 
32 print(f)
33 print(s)
34 
35 if f != 33:
36     print('测试失败!')
37 elif s != 7986:
38     print('测试失败!')
39 else:
40     print('测试成功!')

  编写一个decorator,能在函数调用的前后打印出'begin call'和'end call'的日志:

 1 def call_name(func):
 2     def wrapper(*args, **kw):
 3         print('begin call:')
 4         print('call %s()' % func.__name__)
 5         call = func(*args, **kw)               #函数在此时调用
 6         print('end call.')
 7         return call
 8     return wrapper
 9 
10 
11 @call_name
12 def now():
13     print('2015-3-25')
14 
15 
16 # 调用now函数
17 now()

  加大难度,写出一个@call_name的decorator,使它既支持:

@log
def f():
    pass

  又支持:

@log('execute')
def f():
    pass

  源码实现:

 1 import functools
 2 
 3 
 4 def call_name(text):
 5     if isinstance(text, str):      #通过参数能够判断两种模式
 6         def decorator(func):
 7             @functools.wraps(func)
 8             def wrapper(*args, **kw):
 9                 print('%s call %s()' % (text, func.__name__))
10                 return func(*args, **kw)
11             return wrapper
12         return decorator
13     else:
14         @functools.wraps(text)
15         def wrapper(*args, **kw):
16             print('call %s()' % text.__name__)
17             return text(*args, **kw)
18         return wrapper
19 
20 
21 @call_name
22 def now():
23     print('2015-3-25')
24 
25 
26 # 调用now函数
27 now()


原文地址:https://www.cnblogs.com/cjvae/p/9182522.html