怎么理解python中的装饰器

一个比喻

知乎上有一个比较形象的比喻 https://www.zhihu.com/questio...
人类穿着内裤很大程度上是为了遮羞和对关键部位进行保护,但是却不能提供保暖。因此我们还需要穿着长裤。长裤就是对内裤功能的补充,却不影响内裤本身的功能。

装饰器介绍

python中的装饰器的目的是为一个目标函数添加额外的功能却不修改函数本身。装饰器的本身其实是一个特殊的函数。主要的应用场景有插入日志,性能测试、事务处理等。

下面我们来举一个简单的例子一步一步了解一下。

我们首先写了三个函数,即对两个数做加减乘的操作并打印:

def func_sum(x, y):
    print x+y


def func_minus(x, y):
    print x - y


def func_multiply(x, y):
    print x*y

但是我们现在有了新需求,就是需要在日志中打印所有加、减、乘操作时的时间。

import logging
import time


def func_sum(x, y):
    logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
    print x+y


def func_minus(x, y):
    logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
    print x - y


def func_multiply(x, y):
    logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
    print x*y

显然像上面这样修改每一个函数,在每一个函数中添加重复代码是不合适的。真正理想的是我们可以定义一个函数,专门用来输出日志,输出完日志后,再执行真正的函数。

import logging
import time


def func_sum(x, y):
    print x+y


def func_minus(x, y):
    print x - y


def func_multiply(x, y):
    print x*y


def logging_first(func):
    logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
    return func

logging_first(func_sum)(20,30)

但是上面这中实现方式也存在着种种问题。

  1. log其实是logging_first(func_sum)时就打印了,而不是进行加、减、乘的时候打印的。

  2. 对加减乘函数的调用都需要修改成logging_first(func_sum)(20,30)类似方式。
    那么有啥更好的解决方式呢?

装饰器

import logging
import time


def func_sum(x, y):
    print x+y


def func_minus(x, y):
    print x - y


def func_multiply(x, y):
    print x*y


def logging_first(func):
    def wrapper(x, y):
        logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
        return func(x, y)
    return wrapper

logging_first(func_sum)(20, 30)

代码像上面这么写,可以较好地解决了上面提到的第一个问题。调用logging_first返回的是wrapper函数。执行wrapper函数的时候先打印log,然后马上执行了传入的func函数。

但是调用方式依旧需要修改成logging_first(func_sum)(20, 30)这种方式。
幸好,python给我们提供了优雅的语法糖。
我们可以将上面的代码修改成以下形式。

import logging
import time


def logging_first(func):
    def wrapper(x, y):
        logging.warning(time.strftime("%Y-%m-%d %H:%M:%S %Z", time.localtime(time.time())))
        return func(x, y)

    return wrapper


@logging_first
def func_sum(x, y):
    print x + y


@logging_first
def func_minus(x, y):
    print x - y


@logging_first
def func_multiply(x, y):
    print x * y

func_sum(20, 30)
func_minus(20, 30)
func_multiply(20, 30)

装饰器语法糖放在函数前面,相当于执行了logging_firts(func_sum)等。


Char
506 声望33 粉丝

hello world