首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Python中实现钩子或回调的首选方法是什么?

在Python中实现钩子或回调的首选方法是什么?
EN

Stack Overflow用户
提问于 2010-11-30 08:53:52
回答 3查看 19.1K关注 0票数 25

我希望通过提供调用用户函数的接口,为我的一个模块的用户提供扩展其功能的功能。例如,我希望让用户能够在创建类的实例时得到通知,并在使用实例之前提供修改实例的机会。

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

代码语言:javascript
复制
# in mymodule.py
def factory(cls, *args, **kwargs):
    return cls(*args, **kwargs)

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

为了扩展它,程序员可以在另一个模块中编写如下函数:

代码语言:javascript
复制
def myFactory(cls, *args, **kwargs):
    instance = myFactory.chain(cls, *args, **kwargs)
    # do something with the instance here if desired
    return instance

上述回调的安装如下所示:

代码语言:javascript
复制
myFactory.chain, mymodule.factory = mymodule.factory, myFactory

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

我希望让它尽可能简单;例如,我认为大多数应用程序实际上不需要链接多个用户回调(尽管上面的模式“免费”提供了无限链接)。我怀疑他们是否需要删除回调或指定优先级或顺序。在我看来,像python-callbacksPyDispatcher这样的模块有点杀伤力,特别是后者,但如果使用我的模块的程序员有令人信服的好处,我对它们持开放态度。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-12-01 13:35:52

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

代码语言:javascript
复制
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修饰函数允许将其他函数“附加”到该函数。

代码语言:javascript
复制
@delegate
def intfactory(num):
    return int(num)

可以使用+=将函数添加到委托中(使用-=将其删除)。您还可以使用funcname.callback进行装饰,以添加回调函数。

代码语言:javascript
复制
@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式的吗?

票数 11
EN

Stack Overflow用户

发布于 2010-11-30 09:11:29

aaronsterling's idea更进一步:

代码语言:javascript
复制
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
票数 14
EN

Stack Overflow用户

发布于 2010-11-30 08:59:20

我可能会使用装饰器,这样用户就可以编写代码了。

代码语言:javascript
复制
@new_factory
def myFactory(cls, *args, **kwargs):
    instance = myFactory.chain(cls, *args, **kwargs)
    # do something with the instance here if desired
    return instance

然后在你的模块中,

代码语言:javascript
复制
import sys

def new_factory(f):
    mod = sys.modules[__name__]
    f.chain = mod.factory
    mod.factory = f
    return f
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4309607

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档