0

我有一个类“Listener”,它将回调连接到 D-Bus 信号。回调和信号名称由另一个类“客户端”提供。如果 Client 提供的回调传递给 connect_to_signal(在 dbus.Interface 上)作为接收信号时使用的回调,则一切都按预期工作,即当接收到连接的信号时调用类 Client 中的回调方法。

但是,如果我想在继续调用客户端回调之前“拦截”信号并评估有效负载,我想我可以使用 lambda 表达式并将其传递给 connect_to_signal 方法,如下例所示:

import dbus
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
from gi.repository import GObject


class Client(object):

    def __init__(self):
        bus = dbus.SystemBus()
        obj = bus.get_object("org.freedesktop.UDisks", "/org/freedesktop/UDisks")
        interface = dbus.Interface(obj, "org.freedesktop.UDisks")
        listener = Listener()
        signals_and_callbacks = {"DeviceAdded": self.device_added, 
            "DeviceChanged": self.device_changed}
        listener.listen_to_signals(interface, signals_and_callbacks)

    def device_added(self, payload):
        print "in device_added ", payload

    def device_changed(self, payload):
        print "in device_changed ", payload


class Listener(object):

    def listen_to_signals(self, interface, signals_and_callbacks):
        for signal, callback in signals_and_callbacks.items():
            cb = lambda x: self.signal_cb(x, callback)
            interface.connect_to_signal(signal, cb)

    def signal_cb(self, opath, subscriber_cb):
        print subscriber_cb
        subscriber_cb(opath)


if __name__ == "__main__":
    client = Client()
    mainloop = GObject.MainLoop()
    mainloop.run()

But this does not work as intended. The signals gets connected, in this case the code reacts to both 'DeviceAdded' and 'DeviceChanged', but only the last callback added gets called. If I only connect one signal the behaviour is as expected, but as soon as I connect more than one signal, passing lambda expressions as callbacks, both signals triggers the call to the last callback added.

Does anyone have any idea what's going on here?

4

1 回答 1

1

The basic problem is Python's scoping rules and how you set up the callback.
This example illustrates your problem (and the solution):

def test1():
    print("test1")

def test2():
    print("test2")

def caller(name, fn):
    print("calling function with name: {}".format(name))
    fn()

class Driver(object):
    def __init__(self):

        self.signals = []

    def connect_to_signal(self, name, what_to_call):
        self.signals.append((name, what_to_call))

    def run(self):
        for name, signal in self.signals:
            signal(1)



def main():
    signals = {'test1':test1, 'test2':test2}

    d = Driver()

    for signal, callback in signals.items():
        cb = lambda x: caller(signal, callback)
        #cb = lambda x,s=signal,c=callback: caller(s, c)  # TRY THIS INSTEAD!
        d.connect_to_signal(signal, cb)

    d.run()

if __name__ == '__main__':
    main()

If you run this function as-is, you get the following:

calling function with name: test2
test2
calling function with name: test2
test2

If you comment out the line starting cb = lambda x, and uncomment the following line, you now get the desired:

calling function with name: test1
test1
calling function with name: test2
test2

The reason is that you need to capture the variables in your lambda expression, or their values will just be what they are at the end of your loop.

于 2012-11-20T15:05:49.513 回答