📄 sect12.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:TIPython.rtf
Application Directory:c:\tools\rtf2html\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:12/31/2001
Translation Time:08:24:13
Translation Platform:Win32
Number of Output files:18
This File:Sect12.htm
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>10: Callbacks</TITLE>
<script language="JavaScript">
</script>
</head>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
<!-- [ <a href="README.txt">Viewing Hints</a> ]
[ <a href="RevisionHistory.htm">Revision History</a> ] -->
[ <a href="http://www.mindview.net/Books/TIPython/">Book Home Page</a> ]
[ <a href="http://www.mindview.net/Etc/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/Seminars">Seminars</a> ]
[ <a href="http://www.mindview.net/CDs">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/Services">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
Thinking in Python<br>
<small>Revision 0.1.2 (12/31/01) -- Incomplete and Unfinished</small></FONT></H2>
<H3><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
by Bruce Eckel ©2002 MindView, Inc.</FONT></H3>
<FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
[ <a href="Sect11.htm">Previous Chapter</a> ]
[ <a href="javascript:window.location.href = 'Index.htm';">Table of Contents</a> ]
[ <a href="DocIdx.htm">Index</a> ]
[ <a href="Sect13.htm">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="_Toc476705913"></A><A NAME="_Toc534420123"></A><A NAME="Heading74"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H1 ALIGN="LEFT">
10: Callbacks</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Decoupling code behavior</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><I>Observer</I>, and a category of
callbacks called “multiple dispatching (not in <I>Design
Patterns</I>)” including the <I>Visitor</I> from <I>Design Patterns</I>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_292">Add Comment</A></FONT><A NAME="_Toc462393595"></A><A NAME="_Toc476705914"></A><A NAME="_Toc534420124"></A><BR></P></DIV>
<A NAME="Heading75"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Observer</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Like the other forms of callback, this
contains a hook point where you can change code. The difference is in the
observer’s completely dynamic nature. It is often used for the specific
case of changes based on other object’s change of state, but is also the
basis of event management. Anytime you want to decouple the source of the call
from the called code in a completely dynamic way.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_293">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<A NAME="Index18"></A><A NAME="Index19"></A>observer pattern solves a fairly
common problem: What if a group of objects needs to update themselves when some
object changes state? This can be seen in the “model-view” aspect of
Smalltalk’s MVC (model-view-controller), or the almost-equivalent
“Document-View Architecture.” Suppose that you have some data (the
“document”) and more than one view, say a plot and a textual view.
When you change the data, the two views must know to update themselves, and
that’s what the observer facilitates. It’s a common enough problem
that its solution has been made a part of the standard <B>java.util</B> library.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_294">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are two types of objects used to
implement the observer pattern in Python. The
<A NAME="Index20"></A><B>Observable</B> class keeps track of everybody who wants
to be informed when a change happens, whether the “state” has
changed or not. When someone says “OK, everybody should check and
potentially update themselves,” the <B>Observable</B> class performs this
task by calling the <A NAME="Index21"></A><B>notifyObservers( )</B> method
for each one on the list. The <B>notifyObservers( )</B> method is part of
the base class <B>Observable</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_295">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are actually two “things that
change” in the observer pattern: the quantity of observing objects and the
way an update occurs. That is, the observer pattern allows you to modify both of
these without affecting the surrounding code.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_296">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">-------------</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>Observer</B> is an
“interface” class that only has one member function,
<B>update( )</B>. This function is called by the object that’s being
observed, when that object decides its time to update all its observers. The
arguments are optional; you could have an <B>update( )</B> with no
arguments and that would still fit the observer pattern; however this is more
general—it allows the observed object to pass the object that caused the
update (since an <B>Observer </B>may be registered with more than one observed
object) and any extra information if that’s helpful, rather than forcing
the <B>Observer</B> object to hunt around to see who is updating and to fetch
any other information it needs.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_297">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The “observed object” that
decides when and how to do the updating will be called the <B>Observable</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_298">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>Observable</B> has a flag to indicate
whether it’s been changed. In a simpler design, there would be no flag; if
something happened, everyone would be notified. The flag allows you to wait, and
only notify the <B>Observer</B>s when you decide the time is right. Notice,
however, that the control of the flag’s state is <B>protected</B>, so that
only an inheritor can decide what constitutes a change, and not the end user of
the resulting derived <B>Observer</B> class.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_299">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Most of the work is done in
<B>notifyObservers( )</B>. If the <B>changed</B> flag has not been set,
this does nothing. Otherwise, it first clears the <B>changed</B> flag so
repeated calls to <B>notifyObservers( )</B> won’t waste time. This is
done before notifying the observers in case the calls to <B>update( )</B>
do anything that causes a change back to this <B>Observable</B> object. Then it
moves through the <B>set</B> and calls back to the <B>update( )</B> member
function of each <B>Observer</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_300">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">At first it may appear that you can use
an ordinary <B>Observable</B> object to manage the updates. But this
doesn’t work; to get an effect, you <I>must</I> inherit from
<B>Observable</B> and somewhere in your derived-class code call
<A NAME="Index22"></A><B>setChanged( )</B>. This is the member function
that sets the “changed” flag, which means that when you call
<A NAME="Index23"></A><B>notifyObservers( )</B> all of the observers will,
in fact, get notified. <I>Where</I> you call <B>setChanged( )</B> depends
on the logic of your program.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_301">Add Comment</A></FONT><A NAME="_Toc534420125"></A><BR></P></DIV>
<A NAME="Heading76"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Observing flowers</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Since Python doesn’t have standard
library components to support the observer pattern (like Java does), we must
first create one. The simplest thing to do is translate the Java standard
library <B>Observer</B> and <B>Observable</B> classes. This also provides easier
translation from Java code that uses these libraries.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_302">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In trying to do this, we encounter a
minor snag, which is the fact that Java has a <B>synchronized</B> keyword that
provides built-in support for thread synchronization. We could certainly
accomplish the same thing by hand, using code like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_303">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>import</font> threading
<font color=#0000ff>class</font> ToSynch:
<font color=#0000ff>def</font> __init__(self):
self.mutex = threading.RLock()
self.val = 1
<font color=#0000ff>def</font> aSynchronizedMethod(self):
self.mutex.acquire()
<font color=#0000ff>try</font>:
self.val += 1
<font color=#0000ff>return</font> self.val
<font color=#0000ff>finally</font>:
self.mutex.release()</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But this
rapidly becomes tedious to write and to read. Peter Norvig provided me with a
much nicer solution:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_304">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#: util:Synchronization.py
'''Simple emulation of Java's 'synchronized'
keyword, <font color=#0000ff>from</font> Peter Norvig.'''
<font color=#0000ff>import</font> threading
<font color=#0000ff>def</font> synchronized(method):
<font color=#0000ff>def</font> f(*args):
self = args[0]
self.mutex.acquire();
# <font color=#0000ff>print</font> method.__name__, 'acquired'
<font color=#0000ff>try</font>:
<font color=#0000ff>return</font> apply(method, args)
<font color=#0000ff>finally</font>:
self.mutex.release();
# <font color=#0000ff>print</font> method.__name__, 'released'
<font color=#0000ff>return</font> f
<font color=#0000ff>def</font> synchronize(klass, names=None):
<font color=#004488>""</font>"Synchronize methods <font color=#0000ff>in</font> the given <font color=#0000ff>class</font>.
Only synchronize the methods whose names are
given, <font color=#0000ff>or</font> all methods <font color=#0000ff>if</font> names=None.<font color=#004488>""</font>"
<font color=#0000ff>if</font> type(names)==type(''): names = names.split()
<font color=#0000ff>for</font> (name, val) <font color=#0000ff>in</font> klass.__dict__.items():
<font color=#0000ff>if</font> callable(val) <font color=#0000ff>and</font> name != '__init__' <font color=#0000ff>and</font> \
(names == None <font color=#0000ff>or</font> name <font color=#0000ff>in</font> names):
# <font color=#0000ff>print</font> <font color=#004488>"synchronizing"</font>, name
klass.__dict__[name] = synchronized(val)
# You can create your own self.mutex, <font color=#0000ff>or</font> inherit
# <font color=#0000ff>from</font> this <font color=#0000ff>class</font>:
<font color=#0000ff>class</font> Synchronization:
<font color=#0000ff>def</font> __init__(self):
self.mutex = threading.RLock()
#:~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>synchronized( )</B>
function takes a method and wraps it in a function that adds the mutex
functionality. The method is called inside this function:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_305">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>return</font> apply(method, args)</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">and
as the <B>return</B> statement passes through the <B>finally</B> clause, the
mutex is released.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_306">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is in some ways the <I>Decorator</I>
design pattern, but much simpler to create and use. All you have to say
is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>myMethod = synchronized(myMethod)</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To
surround your method with a mutex.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_307">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>synchronize( )</B> is a
convenience function that applies <B>synchronized( )</B> to an entire
class, either all the methods in the class (the default) or selected methods
which are named in a string as the second argument.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_308">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Finally, for <B>synchronized( )</B> to
work there must be a <B>self.mutex</B> created in every class that uses
<B>synchronized( )</B>. This can be created by hand by the class author, but
it’s more consistent to use inheritance, so the base class
<B>Synchronization</B> is provided.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_309">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s a simple test of the
<B>Synchronization</B> module.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#: util:TestSynchronization.py
<font color=#0000ff>from</font> Synchronization <font color=#0000ff>import</font> *
# To use <font color=#0000ff>for</font> a method:
<font color=#0000ff>class</font> C(Synchronization):
<font color=#0000ff>def</font> __init__(self):
Synchronization.__init__(self)
self.data = 1
<font color=#0000ff>def</font> m(self):
self.data += 1
<font color=#0000ff>return</font> self.data
m = synchronized(m)
<font color=#0000ff>def</font> f(self): <font color=#0000ff>return</font> 47
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -