装饰器

装饰器的定义

前面我们在学习自定义对象的时候,类里面的静态方法是这样写的

class AccordCar:    
    
    @staticmethod
    def pressHorn(): 
        print('嘟嘟~~~~~~')

这个pressHorn 方法的上面有个 @staticmethod 的修饰。

这种 函数定义前面 @开头的修饰,被称为装饰器,英文称之为decorator

以后我们开发Python代码,会经常碰到装饰器。

Python中装饰器通常用来装饰函数、或者类的方法。

通常被装饰后的函数, 会在原有的函数基础上,增加一点功能。

比如 上面 staticmethod 这个装饰器,就给被装饰的函数 pressHorn 增加了一层含义,表示这个方法是个静态方法。

通常装饰器本身是也一个函数。 那么装饰器是怎么装饰另外的函数的呢? 我们看下面的函数

import time
def getCurTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())

这个函数会返回字符串格式的当前时间。

如果我们希望返回的当前时间前面加上一个说明: 当地时间

我们可以不去修改原来的函数, 而是使用装饰器,像这样

import time

def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

@sayLocal
def getCurTime():
    return time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())


print (getCurTime())   

这里 sayLocal函数就是一个装饰器函数。 它被用来装饰其它的函数,比如 getCurTime

当Python解释器执行 完 下面语句的时候,

@sayLocal
def getCurTime():
    ....

就等于执行了这样的一条语句

getCurTime = sayLocal(getCurTime)

就是重新定义了 getCurTime 这个变量 为 后面 sayLocal(getCurTime) 调用的返回值。

sayLocal(getCurTime) 调用的返回值是什么呢?

我们看到 sayLocal(getCurTime) 函数的定义是这样的:

def sayLocal(func):
    def wrapper():
        curTime = func()
        return f'当地时间: {curTime}'
    return wrapper

所以, 它的参数 func 传入的是一个函数对象 ,就是原来的getCurTime 函数。

然后这个函数 内部 又定义了一个函数 wrapper, 这个内部的函数 实现了在原来的getCurTime 函数 调用的结果前面加上 当地时间: 这样的前缀。

这个sayLocal 函数调用的返回值 就是 这个内部函数 wrapper。

那么getCurTime 经过装饰后,其实 已经变成了 内部函数 wrapper

所以后面我执行调用 getCurTime 函数,其实就是调用 wrapper。


可能有的读者会问,干嘛要这么麻烦? 我们直接改这个函数的内容,不是更简单吗?像这样

import time
def getCurTime():
    curTime = time.strftime('%Y_%m_%d %H:%M:%S',time.localtime())
    return f'当地时间: {curTime}'

装饰器经常被用在库和框架中, 给别的开发者使用。 这些库的开发者预料到 使用者 开发的函数可能需要 一些增强的功能。 因为他们没法去改使用者的代码, 就可以把这些增强的部分做在 装饰器函数中。

这样使用者,只需要在他们的函数前面上@xxx 就使用了这些增强的功能了。

被装饰的函数有参数

如果要装饰函数有 未知个数的参数, 怎么办呢?

像这样的2个函数,都要装饰

import time
def getCurTimeFormat1(name):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} '


def getCurTimeFormat2(name,place):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} , 采集地:{place}'

这时,可以使用函数的可变参数,像这样

import time

def sayLocal(func):
    def wrapper(*args,**kargs):
        curTime = func(*args,**kargs)
        return f'当地时间: {curTime}'
    return wrapper

@sayLocal
def getCurTimeFormat1(name):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} '

@sayLocal
def getCurTimeFormat2(name,place):
    curTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
    return f'{curTime} ,数据采集者:{name} , 采集地:{place}'

print (getCurTimeFormat1('张三'))    
print (getCurTimeFormat2('张三',place='北京'))    

其中 *args 可以接受一切 不指定参数名的传参方式 , 比如 '张三'

*kargs 可以接受一切 指定参数名 的传参方式 , 比如 place='北京'