Python-装饰器

装饰器

装饰器是经常用到的功能,很多面试都会问到,使用也是功能很强大。
本篇介绍了无参数和不定长参数的函数使用装饰器,带参数的装饰器,多重装饰器以及类装饰器。

装饰器常用场景

  1. 引入日志
  2. 函数执行时间
  3. 执行函数的预处理
  4. 执行函数后的清理
  5. 权限校验
  6. 缓存

例1:入门装饰器(无参数装饰器)

上代码:

def w1(func):
    def inner(): 
        print('this is one')
        print('------------------')
        return func()
    return inner  # 返回inner函数

@w1
def f1():
    print('this is two')

# 调用函数f1
f1()

执行结果:

执行步骤:

  1. python解释器会先def w1(func) ,w1函数加载到内存中
  2. @w1

在函数调用之前其内部的代码不会被执行。但是@w1语句,包含很多的内容。@函数名是python的一种语法糖。

@w1会执行以下操作:

  • 执行w1函数,并且将@符号下面的函数作为w1函数的参数,即@w1=w1(f1)。
    此时就会去调用并执行w1(func)函数,解释器首先加载的是inner函数,而不是执行(因为没有调用)
  • 当执行到return inner时候,才会调用函数inner()函数,在函数inner中依次执行,执行到return func(),此时func() = f1()。即调用函数f1(),则是调用函数inner(),
  • 调用inner函数是,因为func() = f1(),所以才会执行函数f1()。

例2:被装饰的函数带不定长参数

上代码:

from time import ctime, sleep

def timefun(func):
    def inner(*args, **kwagrs):     # 使用 *args, **kwagrs接受参数
        print('%s called at %s' %(func.__name__, ctime()))
        return func(*args, **kwagrs) # func函数也要加上*args, **kwagrs
    return inner

@timefun
def foo(a, b, c):
    print(a+b+c)

foo(1, 2, 3)
sleep(3)
foo(9, 8, 7)

获取结果:

在inner函数出使用*args, kwagrs接受不定长以及关键字参数。 注意**:

  • inner函数中使用的func.name输出的是函数foo的函数名,如果只是使用func,则是输出是函数的内存地址。
  • return func() return必须加上,为了让装饰器更通用。以防在执行函数foo()处没有print而是renturn的时候,就不会输出结果。
  • ctime()方法返回的是当前的时间

例3:装饰器带参数

在原有装饰器的基础上,设置外部变量。
上代码:
from time import ctime, sleep

def timefun_arg(pre='hello'):  # 装饰器参数在这里接受,里面嵌套的函数可以直接使用
    def timefunc(func):           # 以下内容和装饰器不带参数的形式形同
        def inner(*args, **kwagrs):
            print('%s called at %s %s' % (func.__name__, ctime(), pre))
            return func(*args, **kwagrs)
        return inner        # 返回inner函数
    return timefunc            # 返回timefunc函数

@timefun_arg('Python')   # 装饰器参数在这里传递
def foo(a, b, c):
    print(a+b+c)

foo(1, 3, 4)
sleep(2)
foo(7, 8, 9)

输出结果:

例4:多重装饰器

@A
@B
def c():
    pass

装饰的时候先是用B装饰,再用A装饰。
在执行时候,先执行A函数,再执行B函数。
当执行A是,需要return func即c函数,此时的c函数放在了B中的return func()中了,因为先装饰最近的函数,所以返回给A的内容中,是已经加上B中的内容以后的。
上代码:

def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makeBold
@makeItalic
def test3():
    return "hello world-3"

运行结果:

<b><i>hello world-3</i></b>

例6:类装饰器

参考链接:
http://howdoit.cn/2017/03/16/09-python-%E7%B1%BB%E8%A3%85%E9%A5%B0%E5%99%A8/
待补充,敬请期待