使用python类中的方法作为装饰器用于注册回调函数

以前在学习python装饰器时,多数教学资料对装饰器的介绍案例大多是对一个己经存在的函数,在不修改其代码的前提下对这个函数进行功能增强. 也就是说对一函数进行"包装".

示例如下:

import time

def deco(my_func):
    def wrapper(*args, **kwargs):
        print(f'the starting time is {time.ctime()}')
        return my_func(*args, **kwargs)
    return wrapper

@deco
def test_func(arg1, arg2):
    ...

今天在摆弄itchat这个基于微信网页版的python接口模块时发现作者给出的示例代码中使用装饰器注册回调函数感觉很有意思.

itchat作者的示例代码如下:

import itchat

@itchat.msg_register(itchat.content.TEXT)    #通装饰器将下面的'text_reply'注册为处理微信消息的回调函数.
def text_reply(msg):      #模块中负责接收消息的方法一但收到微信文本消息,就会调用这个回调函数,并把收到的消息作为参数传给这个回调函数.
    return msg.text

itchat.auto_login()
itchat.run()

因为我是初学python, 所以不太理解,于是查看了源码进行学习, 并加入了关键注释:

#这个就是用于注册回调函数的装饰器, 该装饰器是带参数的装饰器. 所以需要一个wrapper传递被装饰的回调函数
def msg_register(self, msgType, isFriendChat=False, isGroupChat=False, isMpChat=False): 
    
    ''' a decorator constructor
        return a specific decorator based on information given '''
    if not (isinstance(msgType, list) or isinstance(msgType, tuple)):
        msgType = [msgType]
    #下面的函数就是用于传递被装饰的回调函数的wrapper.
    def _msg_register(fn):
        #遍历装饰器的参数--msgType(这是一个列表), 以确定将回调函数赋值给哪一个类中的函数(这些函数被放置到了类中的字典里)
        for _msgType in msgType:
            if isFriendChat:
                self.functionDict['FriendChat'][_msgType] = fn
            if isGroupChat:
                self.functionDict['GroupChat'][_msgType] = fn
            if isMpChat:
                self.functionDict['MpChat'][_msgType] = fn
            if not any((isFriendChat, isGroupChat, isMpChat)):
                self.functionDict['FriendChat'][_msgType] = fn
        #wrapper中返回回调函数的引用
        return fn
    #装饰器返回wrapper的引用
    return _msg_register

#最终self.functionDict中的函数是在self.run中被执行的. 所以一但我们注册了回调函数以后, 只需运行self.run这个类方法就可以, 无需我们自己去额外运行这个被装饰的回调函数(不然怎么叫回调函数呢^_^)

由于itchat中的装饰器使用了额外的参数, 所以需要在装饰器中加入一个wrapper函数用于传递回调函数, 如果我们的装饰器不需要参数, 那么装饰器的参数就是回调函数, 直接在装饰器的函数体中把回调函数的引用赋值给类中的函数即可, 不需要wrapper函数. 因为装饰器中并没有执行回调函数的代码.

一句话概括就是: 这个注册回调函数的装饰器的作用就是把我们自定义的回调函数的引用赋值给在self.run中即将被执行的类函数 当然我们自定义的回调函数的参数个数和类型取决于self.run中给这个函数传递的参数个数和类型.

以上是我结合itchat模块的源码学习到的一点东西, 接下来让我们写一个简单明了demo梳理一下思路:

class TestClass(object):
    def __init__(self):
        self.func_dict = {}

    def deco(self, func_type):
        def wrapper(func):
            if func_type == 'A':
                self.func_dict[func_type] = func
            if func_type == 'B':
                self.func_dict[func_type] = func
            else:
                self.func_dict['other'] = func
            return func

        return wrapper

    def run(self):
        print('hello')
        self.func_dict['A']('Hi mark this is Func-A ')
        self.func_dict['B']('Hi mark this is Func-B ')

test = TestClass()

@test.deco('A')
def my_func(msg):
    print(f'this is the message from run: {msg}' )

@test.deco('B')
def my_func2(msg):
    print(f'this is the message from run: {msg}')

test.run()

由于我是一个初学者,所以写的有点磨叽, 如果有错误,还请指正^_^

你可能感兴趣的:(使用python类中的方法作为装饰器用于注册回调函数)