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

查看:399
本文介绍了在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屋!

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