在 Python 中实现钩子或回调的首选方法是什么? [英] What's the preferred way to implement a hook or callback in Python?

查看:25
本文介绍了在 Python 中实现钩子或回调的首选方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

# in mymodule.pydef 工厂(cls, *args, **kwargs):返回 cls(*args, **kwargs)

然后当我需要在 mymodule 中创建一个类的实例时,我会使用 factory(cls, arg1, arg2) 而不是 cls(arg1, arg2).>

为了扩展它,程序员会在另一个模块中编写这样的函数:

def myFactory(cls, *args, **kwargs):实例 = myFactory.chain(cls, *args, **kwargs)# 如果需要,对这里的实例做一些事情返回实例

上面回调的安装看起来像这样:

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

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

我希望它尽可能简单;例如,我认为大多数应用程序实际上不需要链接多个用户回调(尽管使用上述模式免费"提供无限链接).我怀疑他们是否需要删除回调或指定优先级或顺序.python-callbacksPyDispatcher 对我来说似乎有点矫枉过正,尤其是后者,但如果使用我的模块的程序员有令人信服的好处,我愿意接受.>

解决方案

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

类委托(对象):def __init__(self, func):self.callbacks = []self.basefunc = funcdef __iadd__(self, func):如果可调用(函数):self.__isub__(func)self.callbacks.append(func)回归自我定义回调(自我,函数):如果可调用(函数):self.__isub__(func)self.callbacks.append(func)返回函数def __isub__(self, func):尝试:self.callbacks.remove(func)除了值错误:经过回归自我def __call__(self, *args, **kwargs):结果 = self.basefunc(*args, **kwargs)对于 self.callbacks 中的 func:新结果 = 函数(结果)result = result if newresult is None else newresult返回结果

使用 @delegate 装饰一个函数允许其他函数附加"到它.

@delegate定义工厂(数量):返回整数(数量)

可以使用 += 将函数添加到委托中(并使用 -= 删除).也可以用funcname.callback装饰,添加回调函数.

@intfactory.callback定义通知(数量):打印通知:",数量定义增量(数量):返回数字+1工厂 += 增量intfactory += lambda num: num * 2打印 intfactory(3) # 输出 8

这感觉像 Pythonic 吗?

I'd like to provide the capability for users of one of my modules to extend its capabilities by providing an interface to call a user's function. For example, I want to give users the capability to be notified when an instance of a class is created and given the opportunity to modify the instance before it is used.

The way I've implemented it is to declare a module-level factory function that does the instantiation:

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

Then when I need an instance of a class in mymodule, I do factory(cls, arg1, arg2) rather than cls(arg1, arg2).

To extend it, a programmer would write in another module a function like this:

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

Installation of the above callback looks like this:

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

This seems straightforward enough to me, but I was wondering if you, as a Python programmer, would expect a function to register a callback rather than doing it with an assignment, or if there were other methods you would expect. Does my solution seem workable, idiomatic, and clear to you?

I am looking to keep it as simple as possible; I don't think most applications will actually need to chain more than one user callback, for example (though unlimited chaining comes "for free" with the above pattern). I doubt they will need to remove callbacks or specify priorities or order. Modules like python-callbacks or PyDispatcher seem to me like overkill, especially the latter, but if there are compelling benefits to a programmer working with my module, I'm open to them.

解决方案

Combining Aaron's idea of using a decorator and Ignacio's idea of a class that maintains a list of attached callbacks, plus a concept borrowed from C#, I came up with this:

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

Decorating a function with @delegate allows other functions to be "attached" to it.

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

Functions can be added to the delegate with += (and removed with -=). You can also decorate with funcname.callback to add a callback function.

@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

Does this feel Pythonic?

这篇关于在 Python 中实现钩子或回调的首选方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆