在Python中实现挂钩或回调的首选方法是什么?

发布于 2021-01-29 15:10:37

我想通过提供一个调用用户函数的接口来为我的一个模块的用户提供扩展其功能的能力。例如,我想让用户能够在创建类的实例时得到通知,并有机会在使用该实例之前对其进行修改。

我实现它的方法是声明一个执行实例化的模块级工厂函数:

# in mymodule.py
def factory(cls, *args, **kwargs):
    return cls(*args, **kwargs)

然后,当我在mymodule中需要一个类的实例时,我会做factory(cls, arg1, arg2)而不是cls(arg1, arg2)

为了扩展它,程序员将在另一个模块中编写如下函数:

def myFactory(cls, *args, **kwargs):
    instance = myFactory.chain(cls, *args, **kwargs)
    # do something with the instance here if desired
    return instance

上面的回调的安装如下所示:

myFactory.chain, mymodule.factory = mymodule.factory, myFactory

这对我来说似乎很简单,但是我想知道作为Python程序员的您是否期望函数注册一个回调而不是通过赋值来完成,或者您是否期望其他方法。我的解决方案对您来说似乎可行,惯用且清晰吗?

我希望将其保持尽可能简单。我认为大多数应用程序实际上并不需要链接多个用户回调,例如(尽管在上述模式下,无限链接“免费”提供)。我怀疑他们是否需要删除回调或指定优先级或顺序。在我看来,像python-
callbacks
PyDispatcher之类的模块似乎过大了,尤其是后者,但是,如果使用该模块的程序员能够获得引人注目的收益,我就可以接受。

关注者
0
被浏览
66
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    结合了Aaron使用装饰器的想法和Ignacio的类维护维护附加回调列表的类的想法,再加上从C#借用的概念,我想到了:

    class delegate(object):
    
        def __init__(self, func):
            self.callbacks = []
            self.basefunc = func
    
        def __iadd__(self, func):
            if callable(func):
                self.__isub__(func)
                self.callbacks.append(func)
            return self
    
        def callback(self, func):
            if callable(func):
                self.__isub__(func)
                self.callbacks.append(func)
            return func
    
        def __isub__(self, func):
            try:
                self.callbacks.remove(func)
            except ValueError:
                pass
            return self
    
        def __call__(self, *args, **kwargs):
            result = self.basefunc(*args, **kwargs)
            for func in self.callbacks:
                newresult = func(result)
                result = result if newresult is None else newresult
            return result
    

    使用修饰功能@delegate可以将其他功能“附加”到该功能。

    @delegate
    def intfactory(num):
        return int(num)
    

    可以使用添加函数+=(使用删除-=)。您还可以装饰funcname.callback以添加回调函数。

    @intfactory.callback
    def notify(num):
        print "notify:", num
    
    def increment(num):
        return num+1
    
    intfactory += increment
    intfactory += lambda num: num * 2
    
    print intfactory(3)   # outputs 8
    

    这听起来像Pythonic吗?



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看