📄 sect13.htm
字号:
# Multiple dispatching using a table
<font color=#0000ff>from</font> __future__ <font color=#0000ff>import</font> generators
<font color=#0000ff>import</font> random
<font color=#0000ff>class</font> Outcome:
<font color=#0000ff>def</font> __init__(self, value, name):
self.value = value
self.name = name
<font color=#0000ff>def</font> __str__(self): <font color=#0000ff>return</font> self.name
<font color=#0000ff>def</font> __eq__(self, other):
<font color=#0000ff>return</font> self.value == other.value
Outcome.WIN = Outcome(0, <font color=#004488>"win"</font>)
Outcome.LOSE = Outcome(1, <font color=#004488>"lose"</font>)
Outcome.DRAW = Outcome(2, <font color=#004488>"draw"</font>)
<font color=#0000ff>class</font> Item(object):
<font color=#0000ff>def</font> compete(self, item):
# Use a tuple <font color=#0000ff>for</font> table lookup:
<font color=#0000ff>return</font> outcome[self.__class__, item.__class__]
<font color=#0000ff>def</font> __str__(self):
<font color=#0000ff>return</font> self.__class__.__name__
<font color=#0000ff>class</font> Paper(Item): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Scissors(Item): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Rock(Item): <font color=#0000ff>pass</font>
outcome = {
(Paper, Rock): Outcome.WIN,
(Paper, Scissors): Outcome.LOSE,
(Paper, Paper): Outcome.DRAW,
(Scissors, Paper): Outcome.WIN,
(Scissors, Rock): Outcome.LOSE,
(Scissors, Scissors): Outcome.DRAW,
(Rock, Scissors): Outcome.WIN,
(Rock, Paper): Outcome.LOSE,
(Rock, Rock): Outcome.DRAW,
}
<font color=#0000ff>def</font> match(item1, item2):
<font color=#0000ff>print</font> <font color=#004488>"%s <--> %s : %s"</font> % (
item1, item2, item1.compete(item2))
# Generate the items:
<font color=#0000ff>def</font> itemPairGen(n):
# Create a list of instances of all Items:
Items = Item.__subclasses__()
<font color=#0000ff>for</font> i <font color=#0000ff>in</font> range(n):
<font color=#0000ff>yield</font> (random.choice(Items)(),
random.choice(Items)())
<font color=#0000ff>for</font> item1, item2 <font color=#0000ff>in</font> itemPairGen(20):
match(item1, item2)
#:~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s a tribute to the
flexibility of dictionaries that a tuple can be used as a key just as easily as
a single object.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_327">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="_Toc462393597"></A><A NAME="_Toc476705917"></A><A NAME="_Toc534420129"></A><BR></P></DIV>
<A NAME="Heading80"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Visitor, a type of multiple dispatching</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The assumption is that you have a primary
class hierarchy that is fixed; perhaps it’s from another vendor and you
can’t make changes to that hierarchy. However, you’d like to add new
polymorphic methods to that hierarchy, which means that normally you’d
have to add something to the base class interface. So the dilemma is that you
need to add methods to the base class, but you can’t touch the base class.
How do you get around this?
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_328">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The design pattern that solves this kind
of problem is called a “visitor” (the final one in the <I>Design
Patterns</I> book), and it builds on the double<I> </I>dispatching scheme shown
in the last section.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_329">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The
<A NAME="Index31"></A><A NAME="Index32"></A>visitor pattern allows you to extend
the interface of the primary type by creating a separate class hierarchy of type
<B>Visitor </B>to virtualize the operations performed upon the primary type. The
objects of the primary type simply “accept” the visitor, then call
the visitor’s dynamically<B>-</B>bound member function.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_330">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>#: c11:FlowerVisitors.py
# Demonstration of <font color=#004488>"visitor"</font> pattern.
<font color=#0000ff>from</font> __future__ <font color=#0000ff>import</font> generators
<font color=#0000ff>import</font> random
# The Flower hierarchy cannot be changed:
<font color=#0000ff>class</font> Flower(object):
<font color=#0000ff>def</font> accept(self, visitor):
visitor.visit(self)
<font color=#0000ff>def</font> pollinate(self, pollinator):
<font color=#0000ff>print</font> self, <font color=#004488>"pollinated by"</font>, pollinator
<font color=#0000ff>def</font> eat(self, eater):
<font color=#0000ff>print</font> self, <font color=#004488>"eaten by"</font>, eater
<font color=#0000ff>def</font> __str__(self):
<font color=#0000ff>return</font> self.__class__.__name__
<font color=#0000ff>class</font> Gladiolus(Flower): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Runuculus(Flower): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Chrysanthemum(Flower): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Visitor:
<font color=#0000ff>def</font> __str__(self):
<font color=#0000ff>return</font> self.__class__.__name__
<font color=#0000ff>class</font> Bug(Visitor): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Pollinator(Bug): <font color=#0000ff>pass</font>
<font color=#0000ff>class</font> Predator(Bug): <font color=#0000ff>pass</font>
# Add the ability to do <font color=#004488>"Bee"</font> activities:
<font color=#0000ff>class</font> Bee(Pollinator):
<font color=#0000ff>def</font> visit(self, flower):
flower.pollinate(self)
# Add the ability to do <font color=#004488>"Fly"</font> activities:
<font color=#0000ff>class</font> Fly(Pollinator):
<font color=#0000ff>def</font> visit(self, flower):
flower.pollinate(self)
# Add the ability to do <font color=#004488>"Worm"</font> activities:
<font color=#0000ff>class</font> Worm(Predator):
<font color=#0000ff>def</font> visit(self, flower):
flower.eat(self)
<font color=#0000ff>def</font> flowerGen(n):
flwrs = Flower.__subclasses__()
<font color=#0000ff>for</font> i <font color=#0000ff>in</font> range(n):
<font color=#0000ff>yield</font> random.choice(flwrs)()
# It's almost as <font color=#0000ff>if</font> I had a method to Perform
# various <font color=#004488>"Bug"</font> operations on all Flowers:
bee = Bee()
fly = Fly()
worm = Worm()
<font color=#0000ff>for</font> flower <font color=#0000ff>in</font> flowerGen(10):
flower.accept(bee)
flower.accept(fly)
flower.accept(worm)
#:~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_331">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="_Toc534420130"></A><BR></P></DIV>
<A NAME="Heading81"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Exercises</H2></FONT>
<OL>
<LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Create a business-modeling
environment with three types of <B>Inhabitant</B>: <B>Dwarf</B> (for engineers),
<B>Elf</B> (for marketers) and <B>Troll</B> (for managers). Now create a class
called <B>Project</B> that creates the different inhabitants and causes them to
<B>interact( )</B> with each other using multiple dispatching.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_332">Add Comment</A></FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Modify
the above example to make the interactions more detailed. Each <B>Inhabitant</B>
can randomly produce a <B>Weapon</B> using <B>getWeapon( )</B>: a
<B>Dwarf</B> uses <B>Jargon</B> or <B>Play</B>, an <B>Elf</B> uses
<B>InventFeature</B> or <B>SellImaginaryProduct</B>, and a <B>Troll</B> uses
<B>Edict</B> and <B>Schedule</B>. You must decide which weapons
“win” and “lose” in each interaction (as in
<B>PaperScissorsRock.py</B>). Add a <B>battle( )</B> member function to
<B>Project</B> that takes two <B>Inhabitant</B>s and matches them against each
other. Now create a <B>meeting( )</B> member function for <B>Project</B>
that creates groups of <B>Dwarf</B>, <B>Elf</B> and <B>Manager</B> and battles
the groups against each other until only members of one group are left standing.
These are the “winners.”
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_333">Add Comment</A></FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Modify
<B>PaperScissorsRock.py</B> to replace the double dispatching with a table
lookup. The easiest way to do this is to create a <B>Map </B>of <B>Map</B>s,
with the key of each <B>Map </B>the class of each object. Then you can do the
lookup by
saying:</FONT><BR><FONT FACE="Georgia">(<B>(Map)map.get(o1.getClass())).get(o2.getClass())</B></FONT><BR><FONT FACE="Georgia">Notice
how much easier it is to reconfigure the system. When is it more appropriate to
use this approach vs. hard-coding the dynamic dispatches? Can you create a
system that has the syntactic simplicity of use of the dynamic dispatch but uses
a table lookup?
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_334">Add Comment</A></FONT><LI><FONT FACE="Verdana"> </FONT><FONT FACE="Georgia">Modify
Exercise 2 to use the table lookup technique described in Exercise 3.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_335">Add Comment</A></FONT></OL>
<DIV ALIGN="CENTER">
<FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
[ <a href="Sect12.htm">Previous Chapter</a> ]
[ <a href="javascript:window.location.href = 'Index.htm';">Table of Contents</a> ]
[ <a href="DocIdx.htm">Index</a> ]
[ <a href="Sect14.htm">Next Chapter</a> ]
</FONT>
<BR>
Last Update:12/31/2001</P></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -