装饰器进阶

前言

如果你还不知道什么是装饰器,请看这里,

请读者不要感到文章的冗长无聊,我会尽量用生动的表达,但我保证这是你见过最详细的教程(之一),如果你看完不能理解,欢迎在评论区批评我

给装饰器传参数-1

正如之前所说,装饰器就是函数,那么是函数就肯定可以传参数.

其实装饰器在调用时本身就把它所装饰的函数作为参数传给了它,只是没有很明白的写出来而已,而下面我要介绍的是"看得见的参数传递过程".

使用场景模拟

现在假设老板需要一个功能,要把一个函数结果(可能是爬虫获取的数据)发送给某个客户.你很自然的想到用装饰器来实现,但是有个问题,老板的客户很多,他随时可能更换发送的客户,这也就意味着你不能把客户给"写定",那么把客户作为装饰器的参数就显得十分必要了.

(我把与装饰器无关的功能用文字来代替了)

你想象中代码样子和功能

@sendMsgTo("老王")def spider(): return "爬到的数据"spider()

out:

已把 爬到的数据 发送给老王@sendMsgTo("老胡")def spider(): return "爬到的数据"spider()

out:

已把 爬到的数据 发送给老王给装饰器传参数-2

之前介绍过,当函数名后面跟上括号后代表执行该函数的意思,而普通装饰器的语法是不带括号的(可以理解为:就是把这个函数拿来装饰,而不是这个函数的执行结果)

既然我们知道了@符后面跟的是函数(名),而不是函数的执行结果,那么我们该怎么实现上面例子的效果呢?

很简单:只要使上面例子中的sendMsgTo(XX)返回一个符合装饰器语法的函数就可以啦!

符合装饰器语法的函数长这样:

def decorator(func): def wrapper(*args, **kwargs): func(*args, **kwargs) """ 一些其他操作 """ return wrapper

如果要返回上面的这个函数,那就必须这么写:

def returnDecorator(): def decorator(func): def wrapper(*args, **kwargs): func(*args, **kwargs) """ 一些其他操作 """ return wrapper return decorator

就是加个一头一尾的事情,当然,最外层函数的参数,变量等等内层的函数都是可以使用的

给装饰器传参数-3

有了上面的理解,那么我们来完善最开始我们需要的装饰器吧!

def sendMsgTo(name): # 定义外层函数,可以接收额外参数 def decorator(func): # 定义内存函数实际上就是装饰器 def wrapper(*args, **kwargs): data = func(*args, **kwargs) print("以把 {} 发送给{}".format(data, name)) # 调用外层函数传进来的参数 return wrapper return decorator # 返回这个装饰器@sendMsgTo("老王")def spider(): return "爬到的数据"if __name__ == "__main__": spider()

out:

以把 爬到的数据 发送给老王

分析:

@sendMsgTo("老王")这句话执行了sendMsgTo这个函数,并把老王作为参数.

sendMsgTo的作用实际上就是定义了一个装饰器函decorator并把该装饰器返回.

这样做的目的是使其内部装饰器函数能够接收外面传进来的参数(这个例子中是name)

从而达到给装饰器传参的目的.

但我们都清楚sendMsgTo并不是真正的装饰器,他只是负责接收外界的参数来构造真正的装饰器并返回.因为真正的装饰器函数是只能有一个参数,那就是被装饰的函数

用类来写一个装饰器-1

在这之前,我们都是通过函数来实现装饰器的功能,从现在起教大家使用类来实现装饰器的功能,这样一来就可以添加更多的功能(虽然绝大多数情况下使用函数装饰器也可以做,但是代码的可读性不高,因为所有功能都集中在一个函数中,使用类装饰器就可以把功能单独作为类的方法,使用时调用方法即可)

class AAA(object): def __call__(self, func): def wrapper(): print(func()) print("这是类装饰器给你的附加功能") return wrapper@AAA()def spider(): return "爬到的数据"if __name__ == "__main__": spider()

out:

爬到的数据这是类装饰器给你的附加功能

注意:

使用类来实现装饰器必须实现__call__方法(其实也不是强制)

调用类装饰器时要加(),这不是调用函数的意思,这是实例化对象的意思

实例化对象后Python会自动调用__call__方法作为装饰器(本质还是函数),如果你不实现__call__方法那么就需要你自己手动调用了,举个例子:

class AAA(object): def myDec(self, func): def wrapper(): print(func()) print("这是类装饰器给你的附加功能") return wrapperaaa = AAA()@aaa.myDecdef spider(): return "爬到的数据"if __name__ == "__main__": spider()

及其不推荐这种写法,一个字繁

用类来写一个装饰器-2

通过上面的介绍,我相信你们都理解了怎么使用类来写一个装饰器(其实本质还是函数),那么我们就在最开始的例子上加点要求.

附加要求:现在老板觉得直接把数据发给客户不太稳重,他想先检查一下数据是否存在敏感信息,将其去除后再发送

使用类装饰器来完成:

class sendMsgTo(object): # 函数参数 def __init__(self, name): self.name = name def __call__(self, func): def wrapper(*args, **kwargs): data = func(*args, **kwargs) data = self.washData(data) print("以把 {} 发送给{}".format(data, self.name)) return wrapper def washData(self, data): """ 负责清洗数据,去除敏感信息 这里把 `数据` 两个字去除换成 `东西` """ return data.replace("数据", "东西")@sendMsgTo("老王")def spider(): return "爬到的数据"if __name__ == "__main__": spider()

out:

以把 爬到的东西 发送给老王

为了加深读者对类装饰器的理解,我希望读者可以结合前面说过的内容自己来理一遍上面代码的执行顺序和逻辑,这并不难

品略图书馆 http://www.pinlue.com/