在 Python 中实现钩子或回调的首选方法是什么? [英] What's the preferred way to implement a hook or callback in 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-callbacks 或 PyDispatcher 对我来说似乎有点矫枉过正,尤其是后者,但如果使用我的模块的程序员有令人信服的好处,我愿意接受.>
结合 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屋!