Python中字典回调的引用

6 投票
5 回答
6312 浏览
提问于 2025-04-17 04:12

我有一个类,这个类里定义了一些回调函数(这里叫做 cb1cb2)。我还保存了一个这些函数的映射表,想在某个事件发生后调用它们。

class Foo:
    cb1 = None
    cb2 = None

    def test(self, input):
        for (name, callback) in map:
            if name == input:
                if callback: callback()
                ...

    map = {'one':cb1, 'two':cb2}

def mycallback():
    print "mycallback()"

f = Foo()
f.cb1 = mycallback  # Register our callback
f.test('one')     # Nothing happens

你能发现问题吗?

问题是,当这个类被初始化的时候,cb1cb2 的值(这两个值都是 None)会被复制到映射表里。所以即使用户“注册”了回调函数(通过给 cb1 赋值),映射表里的值依然是 None,这样就不会调用任何函数。

因为在Python中没有“引用传递”这种说法,我该怎么解决这个问题呢?

5 个回答

1

添加一个注册功能。在 Foo 类中:

def register(self, name, cb): self.map[name] = cb

而不是:

f.cb1 = mycallback

使用:

f.register('one', mycallback)  
1

为什么你需要为自定义回调设置一个不同的变量,而不是直接用执行它的那个变量呢?如果你用同一个变量,这个问题就解决了。

用一些简单的语法糖,它可以看起来像这样:

class CallbackMap(object):
    pass

class Foo(object):
    callbacks = CallbackMap()

    def test(self, input):
        callback = getattr(Foo.callbacks, input)
        if callback: callback()

# setup defaults
Foo.callbacks.one = None
Foo.callbacks.two = some_default_callback

# customize
def mycallback():
    print "mycallback()"

f = Foo()
Foo.callbacks.one = mycallback  # Register our callback
f.test('one') # works
12

为什么不让你的类明确地处理注册呢?

import collections

class Foo(object):
    handlers = None

    def __init__(self):
        self.handlers = collections.defaultdict(set)

    def register(self, event, callback):
        self.handlers[event].add(callback)

    def fire(self, event, **kwargs):
        for handler in self.handlers.get(event, []):
            handler(**kwargs)

foo = Foo()
foo.register('one', mycallback)
foo.fire('one')

撰写回答