观察者模式
观察者模式是软件设计模式的一种。在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统
观察者模式观察者模式结构如下:
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!")
网友评论