2010-11-30 62 views
18

我想为我的某个模块的用户提供能力,通过提供一个调用用户功能的接口来扩展其功能。例如,我想让用户能够在创建类的实例时得到通知,并且有机会在实例使用之前修改实例。在Python中实现钩子或回调的首选方式是什么?

我实现它要声明一个模块级的工厂函数,它实例化的方式:

# 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-callbacksPyDispatcher这样的模块在我看来似乎有点矫枉过正,尤其是后者,但是如果对于使用我的模块的程序员有很大的好处,我会向他们开放。

+1

使用简单的子类来扩展类有什么问题?为什么所有这些混淆回拨业务? – 2010-11-30 03:13:27

回答

7

结合使用装饰和一流的伊格纳西奥的想法,保持列表的阿龙的想法附加回调,加上从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?

6

我可能会使用装饰器,以便用户可以只写。

你的模块中
@new_factory 
def myFactory(cls, *args, **kwargs): 
    instance = myFactory.chain(cls, *args, **kwargs) 
    # do something with the instance here if desired 
    return instance 

然后,

import sys 

def new_factory(f): 
    mod = sys.modules[__name__] 
    f.chain = mod.factory 
    mod.factory = f 
    return f 
+0

这是一个有趣的方法。我可以添加一个包装器来执行`chain`调用,以便用户不必编写该部分。它也很好地支持用户想要添加回调函数的用例,而不是定义函数的时候。 – kindall 2010-11-30 02:55:46

11

aaronsterling's idea远一点:

class C(object): 
    _oncreate = [] 

    def __new__(cls): 
    return reduce(lambda x, y: y(x), cls._oncreate, super(C, cls).__new__(cls)) 

    @classmethod 
    def oncreate(cls, func): 
    cls._oncreate.append(func) 

c = C() 
print hasattr(c, 'spew') 

@C.oncreate 
def spew(obj): 
    obj.spew = 42 
    return obj 

c = C() 
print c.spew 
+0

对于实例化类的特定用例,这是一个非常优雅的方法。 – kindall 2010-11-30 04:18:36

+0

对于普通方法也可以完成同样的事情。 – 2010-11-30 04:19:55

相关问题