如何从装饰器获取对实例方法的引用

时间:2012-08-22 18:03:19

标签: python callback signals decorator

我一直在使用GUI库,它允许您使用连接功能将信号连接到信号处理程序,例如:

widget.connect(signal, callback)

表示只要从窗口小部件触发信号,就会运行函数callback。为了使我的代码更好,并从我的构造函数中删除一系列connect调用,我决定使用一个装饰器,它运行良好:

def callback(widget, signal)
    def decorate(f):
            widget.connect(signal, f)
            return f
    return decorate

...

@callback(widget, signal)
def do_something():
    ...

这非常有效,直到我需要在一个类中执行此操作 - 该函数在之前装饰它绑定到类,这意味着给定的回调函数不会获得类的实例拥有它,使它无用。有没有办法让这个工作?

1 个答案:

答案 0 :(得分:2)

可以采用相对简单的解决方案。我们首先使用装饰器来标记函数。构造实例时,我们搜索这些标记并注册回调。

更具体地说,标记和重新捕获模式通过使用装饰器在绑定之前标记函数,然后在构造函数中的实例的绑定方法中找到它来工作。

首先我们使用装饰器来做标记(我们使用一个集合来允许在一个方法上有多个标记):

def callback(*args):
    def decorate(f):
        try:
            f._marks.add(args)
        except AttributeError:
            f._marks = {args}
        return f
    return decorate

然后我们使用the inspect module来查找已标记函数的绑定版本,并将它们连接起来:

def connect_callbacks(obj):
    for _, f in inspect.getmembers(obj, inspect.ismethod):
        try:
            marks = f.__func__._marks
        except AttributeError:
            continue
        for widget, signal in marks:
            widget.connect(signal, f)

__func__是原始未绑定函数的名称。这允许我们访问我们之前应用的标记,从而促进重新捕获

然后我们可以简单地创建我们的类并装饰我们的函数,记住在构造函数中连接我们的回调:

class Test:
    def __init__(self):
        ...
        connect_callbacks(self)

    @callback(widget, signal)
    def test():
        ...

这允许我们将绑定方法与装饰器连接。

编辑:我在github上发布了一个小型库,可以为你做这个 - 它被称为recap