Python语言与信息数据获取和机器学习

python:signal/slot机制

2016-05-10  本文已影响1331人  核桃啊

观察者模式

观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统

观察者模式观察者模式结构如下:

observer

分析

根据其定义可以得出几条重要的结论:

Observer是观察者的接口,提供notify接口,由Subject来调用,由具体的concreteObserver实现。
Subject是订阅的主题,提供3个接口,通知、增加订阅者和删除订阅者。
贴出维基上Python的实现:

class AbstractSubject(object):
    def register(self, listener):
        raise NotImplementedError("Must subclass me")
 
    def deregister(self, listener):
        raise NotImplementedError("Must subclass me")
 
    def notify_listeners(self, event):
        raise NotImplementedError("Must subclass me")
 
class Listener(object):
    def __init__(self, name, subject):
        self.name = name
        subject.register(self)
 
    def notify(self, event):
        print self.name, "received event", event
 
class Subject(AbstractSubject):
    def __init__(self):
        self.listeners = []
        self.data = None

    def getUserAction(self):
        self.data = raw_input('Enter something to do:')
        return self.data

    # Implement abstract Class AbstractSubject

    def register(self, listener):
        self.listeners.append(listener)
 
    def deregister(self, listener):
        self.listeners.remove(listener)
 
    def notify_listeners(self, event):
        for listener in self.listeners:
            listener.notify(event)

 
if __name__=="__main__":
    # make a subject object to spy on
    subject = Subject()
 
    # register two listeners to monitor it.
    listenerA = Listener("<listener A>", subject)
    listenerB = Listener("<listener B>", subject)
 
    # simulated event
    subject.notify_listeners ("<event 1>")
    # outputs:
    #     <listener A> received event <event 1>
    #     <listener B> received event <event 1>
 
    action = subject.getUserAction()
    subject.notify_listeners(action)
    #Enter something to do:hello
    # outputs:
    #     <listener A> received event hello
    #     <listener B> received event hello

signal/slot 信号槽

信号槽似乎来源于QT,相关介绍qt信号槽,其结构是所有的控件都继承自signalApp,signalApp实现了3个通用接口,产生信号、产生槽和连接信号和槽

信号和槽的关系

当信号发送出去时,会调用信号相关联的槽函数。比如:

button = Button()
lable = Label()
signalAPP.connect(button, SIGNAL(clicked), lable, SLOT(setValue))

将button的点击信号和label的槽函数进行连接,当信号发送时,会调用label的槽函数,这种机制将信号与槽分离,可以信号与信号进行连接,可以一个信号对应多个槽,可以一个槽对应多个信号。

一个简单的python信号槽实现

# -*- coding: utf-8 -*-

class CSignal():
    def __init__(self):
        self.slot = []
    def emit(self, *arg, **kw):
        for pFunc in self.slot:
            pFunc(*arg, **kw)
    def connect(self, cbfunc):
        self.slot.append(cbfunc)

class memberFuc():
    def __init__(self):
        pass
    def test(self, *arg, **kw):
        print 'i am memberFuc!'

def test(*arg, **kw):
    print "i am test", arg, kw

if __name__ == "__main__":
    testSignal = CSignal()
    testSignal.connect(test)

    testOb = memberFuc()
    testSignal.connect(testOb.test)

    testSignal.emit()
    # output:
    #   i am test
    #   i am memberfunc
    del testOb
    testSignal.emit()
    # output:
    #   i am test
    #   i am memberfunc

上面代码和QT的信号槽有几点不同:

注意:当connect一个method时,会增加对象的引用计数,导致del 对象时,对象引用计数不为0,仍然存在,解决的方法时利用弱引用。下面是网上的实现:


"""
File:    signals.py
Author:  Patrick Chasco
Created: July 26, 2005

Purpose: A signals implementation
"""


#========================================================
# Implementation
#========================================================
from weakref import *
import inspect

class Signal:
    """
    class Signal

    A simple implementation of the Signal/Slot pattern. To use, simply 
    create a Signal instance. The instance may be a member of a class, 
    a global, or a local; it makes no difference what scope it resides 
    within. Connect slots to the signal using the "connect()" method. 
    The slot may be a member of a class or a simple function. If the 
    slot is a member of a class, Signal will automatically detect when
    the method's class instance has been deleted and remove it from 
    its list of connected slots.
    """
    def __init__(self):
        self.slots = []

        # for keeping references to _WeakMethod_FuncHost objects.
        # If we didn't, then the weak references would die for
        # non-method slots that we've created.
        self.funchost = []

    def __call__(self, *args, **kwargs):
        for i in range(len(self.slots)):
            slot = self.slots[i]
            if slot != None:
                slot(*args, **kwargs)
            else:
                del self.slots[i]
                
    def call(self, *args, **kwargs):
        self.__call__(*args, **kwargs)

    def connect(self, slot):
        self.disconnect(slot)
        if inspect.ismethod(slot):
            self.slots.append(WeakMethod(slot))
        else:
            o = _WeakMethod_FuncHost(slot)
            self.slots.append(WeakMethod(o.func))
            # we stick a copy in here just to keep the instance alive
            self.funchost.append(o)

    def disconnect(self, slot):
        try:
            for i in range(len(self.slots)):
                wm = self.slots[i]
                if inspect.ismethod(slot):
                    if wm.f == slot.im_func and wm.c() == slot.im_self:
                        del self.slots[i]
                        return
                else:
                    if wm.c().hostedFunction == slot:
                        del self.slots[i]
                        return
        except:
            pass

    def disconnectAll(self):
        del self.slots
        del self.funchost
        self.slots = []
        self.funchost = []

class _WeakMethod_FuncHost:
    def __init__(self, func):
        self.hostedFunction = func
    def func(self, *args, **kwargs):
        self.hostedFunction(*args, **kwargs)

# this class was generously donated by a poster on ASPN (aspn.activestate.com)
class WeakMethod:
    def __init__(self, f):
            self.f = f.im_func
            self.c = ref(f.im_self)
    def __call__(self, *args, **kwargs):
            if self.c() == None : return
            self.f(self.c(), *args, **kwargs)


#========================================================
# Example usage
#========================================================
if __name__ == "__main__":
    class Button:
        def __init__(self):
            # Creating a signal as a member of a class
            self.sigClick = Signal()

    class Listener:
        # a sample method that will be connected to the signal
        def onClick(self):
            print "onClick ", repr(self)
    
    # a sample function to connect to the signal
    def listenFunction():
        print "listenFunction"
   
    # a function that accepts arguments
    def listenWithArgs(text):
        print "listenWithArgs: ", text

    b = Button()
    l = Listener()
    
    # Demonstrating connecting and calling signals
    print
    print "should see one message"
    b.sigClick.connect(l.onClick)
    b.sigClick()

    # Disconnecting all signals
    print
    print "should see no messages"
    b.sigClick.disconnectAll()
    b.sigClick()

    # connecting multiple functions to a signal
    print
    print "should see two messages"
    l2 = Listener()
    b.sigClick.connect(l.onClick)
    b.sigClick.connect(l2.onClick)
    b.sigClick()
    
    # disconnecting individual functions
    print
    print "should see two messages"
    b.sigClick.disconnect(l.onClick)
    b.sigClick.connect(listenFunction)
    b.sigClick()
    
    # signals disconnecting automatically
    print
    print "should see one message"
    b.sigClick.disconnectAll()
    b.sigClick.connect(l.onClick)
    b.sigClick.connect(l2.onClick)
    del l2    
    b.sigClick()
    
    # example with arguments and a local signal
    print
    print "should see one message"
    sig = Signal()
    sig.connect(listenWithArgs)
    sig("Hello, World!")

参考

python signal/slot

上一篇 下一篇

猜你喜欢

热点阅读