Python学习笔记 装饰器理解

装饰器的理解

以下内容整理于如何理解Python装饰器? - 刘志军的回答 - 知乎和廖雪峰老师Python教程。

装饰器

装饰器本质上是一个Python函数,装饰器的返回值也是一个函数对象,它可以在不改变其他函数的代码的前提下,实现动态的增加其他函数的功能。它经常用于有切面需求的场景,比如插入日志、测试等。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

一个例子
def func1():
    print("i am func 1")

我们在做测试时,希望可以记录下该函数的执行日志,于是可以修改代码:

def func1():
    print("i am func1")
    logging.info("func1 is running")

但是函数func2(),函数func3()等函数也有同样的需要,如果我们仍采用这种方法,不仅低效,写了大量的重复且与原函数功能不相关的代码,于是我们可以将日志代码抽离出来,如下:

def logging(func):
    logging.info("%s is running" % func.__name__)
    func()


def func2():
    print("i am func2")


logging(func2)

这样做可以实现我们预期功能,但是缺点很明显,我们破坏了原始代码的逻辑结构。这时候,我们可以使用装饰器来实现同样的功能,而不破坏原始逻辑结构。

简单的装饰器

def logging(func):

    def wrapper(*args, **kwargs):
        logging.info("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper


def func3():
    print("i am func3")


func3 = logging(func3)
func3()

在上面的代码中,logging()函数就是一个装饰器,用以实现我们测试时的需求。func3()函数则是我们实现原始功能的函数。这样我们就将实现日志功能的代码抽离了出来。

进一步完善

“@”符号是装饰器的语法糖,可在定义函数时使用,避免后面再一次的赋值操作。

def logging(func):

    def wrapper(*args, **kwargs):
        logging.info("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper


@logging
def func3():
    print("i am func3")


@logging
def func4():
    print("i am a func4")


func3()
func4()

像上面这样,我们则可以省略“func3 = logging(func3)”这一赋值操作,直接调用func3()函数即可实现同样的功能。

对以上代码的分析理解

logging()函数是一个装饰器,它返回的是一个函数,因此func3变量在执行“@logging”(func3 = logging(func3))后指向了新的函数logging(func3),而且logging()函数中的局部变量func现在指向func3变量在定义时指向的函数。因此在调用func3()函数时将执行新函数logging(func3)。

对比一下

如果装饰器函数这样定义:

def logging(func):

    def wrapper(*args, **kwargs):
        logging.info("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper()                                 # 这里做了变动

那么,在执行“func3 = logging(func3)”后,func3变量指向的就是wrapper()执行后的返回值。因此可以看出,函数名后的“()”是函数运行的标志。

你可能感兴趣的:(Python,学习笔记)