在Python中实现一个钩子或回调的首选方法是什么? [英] What's the preferred way to implement a hook or callback in Python?
问题描述
我想为我的一个模块的用户提供一个功能,通过提供一个接口来调用用户的功能来扩展它的功能。例如,我想给用户提供在创建类的实例时获得通知的能力,并给予用户在使用之前修改实例的机会。
方法我已经实现它是声明一个模块级工厂函数实例化:
#in mymodule.py
def factory(cls,* args,** kwargs):
return cls(* args,** kwargs)
然后,当我需要mymodule中的一个类的实例时,我会 factory(cls,arg1,arg2)
,而不是<$ c $程序员要在另一个模块中写入一个这样的函数:
def myFactory(cls,* args,** kwargs):
instance = myFactory.chain(cls,* args,* * kwargs)
#如果需要,在这里做一些事情
return instance
安装上面的回调看起来像这样:
myFactory.chain,mymodule.factory = mymodule.factory,myFactory
对我来说这看起来很简单,但我想知道,作为一个Python程序员,注册一个回调,而不是用一个赋值,或者如果有其他方法,你会期望。我的解决方案对你来说是否可行,惯用和清楚?
我希望尽可能简单;我不认为大多数应用程序实际上需要链接多个用户回调,例如(虽然无限链接免费与上述模式)。我怀疑他们将需要删除回调或指定优先级或顺序。 python-callbacks 或 PyDispatcher 对我来说似乎是过度的,特别是后者,但如果有一个有吸引力的好处,一个程序员使用我的模块,我对他们开放。 p>
结合Aaron使用装饰器的想法和Ignacio对维护附加回调列表的类的想法,以及从C# ,我想出了这个:
类委托(对象):
def __init __(self, func):
self.callbacks = []
self.basefunc = func
def __iadd __(self,func):
如果可调用$ b self .__ isub __(func)
self.callbacks.append(func)
return self
def callback(self,func):
如果可调用):
self .__ isub __(func)
self.callbacks.append(func)
return func
def __isub __(self,func):
尝试:
self.callbacks.remove(func)
除了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
def intfactory(num):
return int(num)
函数可以添加到代理与 + =
(并用 - =
)删除。您还可以使用 funcname.callback
修饰以添加回调函数。
@ intfactory.callback
def notify(num):
printnotify:,num
def increment(num):
return num + 1
intfactory + = increment
intfactory + = lambda num:num * 2
print intfactory(3)#outputs 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屋!