Jump to content

Archive:Think Python/Inheritance

From IdeaWazaWiki
Revision as of 22:44, 15 September 2008 by wikademia>Whiteknight (Think Python: Automatically uploading HTML source of this book from http://www.greenteapress.com/thinkpython/html/. Will convert to wikitext in a separate step)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"

           "http://www.w3.org/TR/REC-html40/loose.dtd">

<HTML> <HEAD>

<META http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <META name="GENERATOR" content="hevea 1.10"> <LINK rel="stylesheet" type="text/css" href="book.css"> <TITLE>Inheritance</TITLE> </HEAD> <BODY > <A HREF="book018.html"><IMG SRC="previous_motif.gif" ALT="Previous"></A> <A HREF="index.html"><IMG SRC="contents_motif.gif" ALT="Up"></A> <A HREF="book020.html"><IMG SRC="next_motif.gif" ALT="Next"></A>


<A NAME="htoc212">Chapter 18</A>  Inheritance

In this chapter we will develop classes to represent playing cards,

decks of cards, and poker hands. If you don’t play poker, you can read about it at wikipedia.org/wiki/Poker, but you don’t have

to; I’ll tell you what you need to know for the exercises.

<A NAME="@default1564"></A>

<A NAME="@default1565"></A>

<A NAME="@default1566"></A>

If you are not familiar with Anglo-American playing cards, you can read about them at wikipedia.org/wiki/Playing_cards.

<A NAME="toc193"></A><A NAME="htoc213">18.1</A>  Card objects

There are fifty-two cards in a deck, each of which belongs to one of

four suits and one of thirteen ranks. The suits are Spades, Hearts, Diamonds, and Clubs (in descending order in bridge). The ranks are Ace, 2, 3, 4, 5, 6, 7, 8, 9, 10, Jack, Queen, and King. Depending on the game that you are playing, an Ace may be higher than King

or lower than 2.

<A NAME="@default1567"></A> <A NAME="@default1568"></A>

If we want to define a new object to represent a playing card, it is

obvious what the attributes should be: rank and suit. It is not as obvious what type the attributes should be. One possibility is to use strings containing words like 'Spade' for suits and 'Queen' for ranks. One problem with this implementation is that it would not be easy to compare cards to

see which had a higher rank or suit.

<A NAME="@default1569"></A>

<A NAME="@default1570"></A> <A NAME="@default1571"></A>

<A NAME="@default1572"></A>

An alternative is to use integers to encode the ranks and suits.

In this context, “encode” means that we are going to define a mapping between numbers and suits, or between numbers and ranks. This kind of encoding is not meant to be a secret (that

would be “encryption”).

For example, this table shows the suits and the corresponding integer codes:

Spades↦3
Hearts↦2
Diamonds↦1
Clubs↦0

This code makes it easy to compare cards; because higher suits map to higher numbers, we can compare suits by comparing their codes.

The mapping for ranks is fairly obvious; each of the numerical ranks maps to the corresponding integer, and for face cards:

Jack↦11
Queen↦12
King↦13

I am using the ↦ symbol to make is clear that these mappings

are not part of the Python program. They are part of the program

design, but they don’t appear explicitly in the code.

<A NAME="@default1573"></A> <A NAME="@default1574"></A>

The class definition for Card looks like this:

<FONT COLOR=blue><FONT SIZE=4>class Card:
    """represents a standard playing card."""

    def __init__(self, suit=0, rank=2):
        self.suit = suit
        self.rank = rank
</FONT></FONT>

As usual, the init method takes an optional

parameter for each attribute. The default card is

the 2 of Clubs.

<A NAME="@default1575"></A> <A NAME="@default1576"></A>

To create a Card, you call Card with the suit and rank of the card you want.

<FONT COLOR=blue><FONT SIZE=4>queen_of_diamonds = Card(1, 12)
</FONT></FONT>

<A NAME="toc194"></A><A NAME="htoc214">18.2</A>  Class attributes

<A NAME="@default1577"></A> <A NAME="@default1578"></A>

In order to print Card objects in a way that people can easily

read, we need a mapping from the integer codes to the corresponding ranks and suits. A natural way to do that is with lists of strings. We assign these lists to class

attributes:

<FONT COLOR=blue><FONT SIZE=4># inside class Card:

    suit_names = ['Clubs', 'Diamonds', 'Hearts', 'Spades']
    rank_names = [None, 'Ace', '2', '3', '4', '5', '6', '7', 
              '8', '9', '10', 'Jack', 'Queen', 'King']

    def __str__(self):
        return '%s of %s' % (Card.rank_names[self.rank],
                             Card.suit_names[self.suit])
</FONT></FONT>

Variables like suit_names and rank_names, which are

defined inside a class but outside of any method, are called class attributes because they are associated with the class object

Card.

<A NAME="@default1579"></A> <A NAME="@default1580"></A>

This term distinguished them from variables like suit and rank, which are called instance attributes because they are associated with a particular instance.

<A NAME="@default1581"></A>

Both kinds of attribute are accessed using dot notation. For

example, in __str__, self is a Card object, and self.rank is its rank. Similarly, Card is a class object, and Card.rank_names is a

list of strings associated with the class.

Every card has its own suit and rank, but there is only one copy of suit_names and rank_names.

Putting it all together, the expression

Card.rank_names[self.rank] means “use the attribute rank from the object self as an index into the list rank_names

from the class Card, and select the appropriate string.”

The first element of rank_names is None because there

is no card with rank zero. By including None as a place-keeper, we get a mapping with the nice property that the index 2 maps to the string '2', and so on. To avoid this tweak, we could have

used a dictionary instead of a list.

With the methods we have so far, we can create and print cards:

<FONT COLOR=blue><FONT SIZE=4>>>> card1 = Card(2, 11)
>>> print card1
Jack of Hearts
</FONT></FONT>

Here is a diagram that shows the Card class object and one Card instance:

<A NAME="@default1582"></A>

<A NAME="@default1583"></A> <A NAME="@default1584"></A>

<A NAME="@default1585"></A>

<IMG SRC="book026.png">

Card is a class object, so it has type type. card1 has type Card. (To save space, I didn’t draw the contents of suit_names and rank_names).

<A NAME="toc195"></A><A NAME="htoc215">18.3</A>  Comparing cards

<A NAME="comparecard"></A>

<A NAME="@default1586"></A> <A NAME="@default1587"></A>

For built-in types, there are conditional operators

(<, >, ==, etc.) that compare values and determine when one is greater than, less than, or equal to another. For user-defined types, we can override the behavior of the built-in operators by providing a method named

__cmp__.

__cmp__ takes two parameters, self and other,

and returns a positive number if the first object is greater, a negative number if the second object is greater, and 0 if they are

equal to each other.

<A NAME="@default1588"></A> <A NAME="@default1589"></A>

The correct ordering for cards is not obvious.

For example, which is better, the 3 of Clubs or the 2 of Diamonds? One has a higher rank, but the other has a higher suit. In order to compare

cards, you have to decide whether rank or suit is more important.

The answer might depend on what game you are playing, but to keep

things simple, we’ll make the arbitrary choice that suit is more important, so all of the Spades outrank all of the Diamonds,

and so on.

<A NAME="@default1590"></A> <A NAME="@default1591"></A>

With that decided, we can write __cmp__:

<FONT COLOR=blue><FONT SIZE=4># inside class Card:

    def __cmp__(self, other):
        # check the suits
        if self.suit > other.suit: return 1
        if self.suit < other.suit: return -1

        # suits are the same... check ranks
        if self.rank > other.rank: return 1
        if self.rank < other.rank: return -1

        # ranks are the same... it's a tie
        return 0    
</FONT></FONT>

You can write this more concisely using tuple comparison:

<A NAME="@default1592"></A> <A NAME="@default1593"></A>

<FONT COLOR=blue><FONT SIZE=4># inside class Card:

    def __cmp__(self, other):
        t1 = self.suit, self.rank
        t2 = other.suit, other.rank
        return cmp(t1, t2)
</FONT></FONT>

The built-in function cmp has the same interface as

the method __cmp__: it takes two values and returns a positive number if the first is larger, a negative number

of the second is larger, and 0 if they are equal.

<A NAME="@default1594"></A> <A NAME="@default1595"></A>

Exercise 1  

Write a __cmp__ method for Time objects. Hint: you can use tuple comparison, but you also might consider using

integer subtraction.

<A NAME="toc196"></A><A NAME="htoc216">18.4</A>  Decks

<A NAME="@default1596"></A>

<A NAME="@default1597"></A>

Now that we have Cards, the next step is to define Decks. Since a

deck is made up of cards, it is natural for each Deck to contain a

list of cards as an attribute.

<A NAME="@default1598"></A> <A NAME="@default1599"></A>

The following is a class definition for Deck. The

init method creates the attribute cards and generates

the standard set of fifty-two cards:

<A NAME="@default1600"></A> <A NAME="@default1601"></A>

<A NAME="@default1602"></A> <A NAME="@default1603"></A>

<FONT COLOR=blue><FONT SIZE=4>class Deck:

    def __init__(self):
        self.cards = []
        for suit in range(4):
            for rank in range(1, 14):
                card = Card(suit, rank)
                self.cards.append(card)
</FONT></FONT>

The easiest way to populate the deck is with a nested loop. The outer

loop enumerates the suits from 0 to 3. The inner loop enumerates the ranks from 1 to 13. Each iteration creates a new Card with the current suit and rank,

and appends it to self.cards.

<A NAME="@default1604"></A> <A NAME="@default1605"></A>

<A NAME="toc197"></A><A NAME="htoc217">18.5</A>  Printing the deck

<A NAME="printdeck"></A>

<A NAME="@default1606"></A> <A NAME="@default1607"></A>

Here is a __str__ method for Deck:

<FONT COLOR=blue><FONT SIZE=4>#inside class Deck:

    def __str__(self):
        res = []
        for card in self.cards:
            res.append(str(card))
        return '\n'.join(res)
</FONT></FONT>

This method demonstrates an efficient way to accumulate a large

string: building a list of strings and then using join. The built-in function str invokes the __str__

method on each card and returns the string representation.

<A NAME="@default1608"></A>

<A NAME="@default1609"></A> <A NAME="@default1610"></A> <A NAME="@default1611"></A>

<A NAME="@default1612"></A>

Since we invoke join on a newline character, the cards are separated by newlines. Here’s what the result looks like:

<FONT COLOR=blue><FONT SIZE=4>>>> deck = Deck()
>>> print deck
Ace of Clubs
2 of Clubs
3 of Clubs
...
10 of Spades
Jack of Spades
Queen of Spades
King of Spades
</FONT></FONT>

Even though the result appears on 52 lines, it is one long string that contains newlines.

<A NAME="toc198"></A><A NAME="htoc218">18.6</A>  Add, remove, shuffle and sort

To deal cards, we would like a method that

removes a card from the deck and returns it.

The list method pop provides a convenient way to do that:

<A NAME="@default1613"></A> <A NAME="@default1614"></A>

<FONT COLOR=blue><FONT SIZE=4>#inside class Deck:

    def pop_card(self):
        return self.cards.pop()
</FONT></FONT>

Since pop removes the last card in the list, we are

dealing from the bottom of the deck. In real life bottom dealing is frowned upon<A NAME="text30" HREF="#note30">1</A>,

but in this context it’s ok.

<A NAME="@default1615"></A> <A NAME="@default1616"></A>

To add a card, we can use the list method append:

<FONT COLOR=blue><FONT SIZE=4>#inside class Deck:

    def add_card(self, card):
        self.cards.append(card)
</FONT></FONT>

A method like this that uses another function without doing

much real work is sometimes called a veneer. The metaphor comes from woodworking, where it is common to glue a thin layer of good quality wood to the surface of a cheaper piece of

wood.

<A NAME="@default1617"></A>

In this case we are defining a “thin” method that expresses a list operation in terms that are appropriate for decks.

As another example, we can write a Deck method named shuffle using the function shuffle from the random module:

<A NAME="@default1618"></A>

<A NAME="@default1619"></A> <A NAME="@default1620"></A>

<A NAME="@default1621"></A>

<FONT COLOR=blue><FONT SIZE=4># inside class Deck:
            
    def shuffle(self):
        random.shuffle(self.cards)
</FONT></FONT>

Don’t forget to import random.

Exercise 2  

<A NAME="@default1622"></A>

<A NAME="@default1623"></A>

Write a Deck method named sort that uses the list method sort to sort the cards in a Deck. sort uses the __cmp__ method we defined to determine sort order.

<A NAME="toc199"></A><A NAME="htoc219">18.7</A>  Inheritance

<A NAME="@default1624"></A> <A NAME="@default1625"></A>

The language feature most often associated with object-oriented

programming is inheritance. Inheritance is the ability to define a new class that is a modified version of an existing

class.

<A NAME="@default1626"></A>

<A NAME="@default1627"></A> <A NAME="@default1628"></A>

<A NAME="@default1629"></A>

It is called “inheritance” because the new class inherits the

methods of the existing class. Extending this metaphor, the existing class is called the parent and the new class is

called the child.

As an example, let’s say we want a class to represent a “hand,”

that is, the set of cards held by one player. A hand is similar to a deck: both are made up of a set of cards, and both require operations

like adding and removing cards.

A hand is also different from a deck; there are operations we want for

hands that don’t make sense for a deck. For example, in poker we might compare two hands to see which one wins. In bridge, we might

compute a score for a hand in order to make a bid.

This relationship between classes—similar, but different—lends itself to inheritance.

The definition of a child class is like other class definitions, but the name of the parent class appears in parentheses:

<A NAME="@default1630"></A>

<A NAME="@default1631"></A> <A NAME="@default1632"></A> <A NAME="@default1633"></A>

<A NAME="@default1634"></A>

<FONT COLOR=blue><FONT SIZE=4>class Hand(Deck):
    """represents a hand of playing cards"""
</FONT></FONT>

This definition indicates that Hand inherits from Deck;

that means we can use methods like pop_card and add_card

for Hands as well as Decks.

Hand also inherits __init__ from Deck, but

it doesn’t really do what we want: instead of populating the hand with 52 new cards, the init method for Hands should initialize

cards with an empty list.

<A NAME="@default1635"></A>

<A NAME="@default1636"></A>

<A NAME="@default1637"></A>

If we provide an init method in the Hand class, it overrides the one in the Deck class:

<FONT COLOR=blue><FONT SIZE=4># inside class Hand:

    def __init__(self, label=''):
        self.cards = []
        self.label = label
</FONT></FONT>

So when you create a Hand, Python invokes this init method:

<FONT COLOR=blue><FONT SIZE=4>>>> hand = Hand('new hand')

>>> print hand.cards [] >>> print hand.label new hand

</FONT></FONT>

But the other methods are inherited from Deck, so we can use pop_card and add_card to deal a card:

<FONT COLOR=blue><FONT SIZE=4>>>> deck = Deck()
>>> card = deck.pop_card()
>>> hand.add_card(card)
>>> print hand
King of Spades
</FONT></FONT>

A natural next step is to encapsulate this code in a method called move_cards:

<A NAME="@default1638"></A>

<FONT COLOR=blue><FONT SIZE=4>#inside class Deck:

    def move_cards(self, hand, num):
        for i in range(num):
            hand.add_card(self.pop_card())
</FONT></FONT>

move_cards takes two arguments, a Hand object and the number of

cards to deal. It modifies both self and hand, and

returns None.

In some games, cards are moved from one hand to another,

or from a hand back to the deck. You can use move_cards for any of these operations: self can be either a Deck

or a Hand, and hand, despite the name, can also be a Deck.

Exercise 3  

Write a Deck method called deal_hands that takes two parameters, the number of hands and the number of cards per hand, and that creates new Hand objects, deals the appropriate number of cards per hand, and returns a list of Hand objects.

Inheritance is a useful feature. Some programs that would be

repetitive without inheritance can be written more elegantly with it. Inheritance can facilitate code reuse, since you can customize the behavior of parent classes without having to modify them. In some cases, the inheritance structure reflects the natural structure of the problem, which makes the program easier to

understand.

On the other hand, inheritance can make programs difficult to read.

When a method is invoked, it is sometimes not clear where to find its definition. The relevant code may be scattered among several modules. Also, many of the things that can be done using inheritance can be

done as well or better without it.

<A NAME="toc200"></A><A NAME="htoc220">18.8</A>  Class diagrams

So far we have seen stack diagrams, which show the state of

a program, and object diagrams, which show the attributes of an object and their values. These diagrams represent a snapshot in the execution of a program, so they change as the program

runs.

They are also highly detailed; for some purposes, too

detailed. A class diagrams is a more abstract representation of the structure of a program. Instead of showing individual

objects, it shows classes and the relationships between them.

There are several kinds of relationship between classes:

  • Objects in one class might contain references to objects

    in another class. For example, each Rectangle contains a reference to a Point, and each Deck contains references to many Cards. This kind of relationship is called HAS-A, as in, “a Rectangle

    has a Point.”
  • One class might inherit from another. This relationship is called IS-A, as in, “a Hand is a kind of a Deck.”
  • One class might depend on another in the sense that changes in one class would require changes in the other.

<A NAME="@default1639"></A>

<A NAME="@default1640"></A> <A NAME="@default1641"></A> <A NAME="@default1642"></A>

<A NAME="@default1643"></A>

A class diagram is a graphical representation of these

relationships<A NAME="text31" HREF="#note31">2</A>. For example, this diagram shows the

relationships between Card, Deck and Hand.

<IMG SRC="book027.png">

The arrow with a hollow triangle head represents an IS-A

relationship; in this case it indicates that Hand inherits

from Deck.

The standard arrow head represents a HAS-A

relationship; in this case a Deck has references to Card

objects.

<A NAME="@default1644"></A>

The star (*) near the arrow head is a

multiplicity; it indicates how many Cards a Deck has. A multiplicity can be a simple number, like 52, a range, like 5..7 or a star, which indicates that a Deck can

have any number of Cards.

A more detailed diagram might show that a Deck actually

contains a list of Cards, but built-in types

like list and dict are usually not included in class diagrams.

Exercise 4  

Read TurtleWorld.py, World.py and Gui.py and draw a class diagram that shows the relationships among the classes defined there.

<A NAME="toc201"></A><A NAME="htoc221">18.9</A>  Debugging

<A NAME="@default1645"></A>

Inheritance can make debugging a challenge because when you

invoke a method on an object, you might not know which method

will be invoked.

<A NAME="@default1646"></A>

Suppose you are writing a function that works with Hand objects.

You would like it to work with all kinds of Hands, like PokerHands, BridgeHands, etc. If you invoke a method like shuffle, you might get the one defined in Deck, but if any of the subclasses override this method, you’ll

get that version instead.

<A NAME="@default1647"></A>

Any time you are unsure about the flow of execution through your

program, the simplest solution is to add print statements at the beginning of the relevant methods. If Deck.shuffle prints a message that says something like Running Deck.shuffle, then as

the program runs it traces the flow of execution.

As an alternative, you could use this function, which takes an

object and a method name (as a string) and returns the class that

provides the definition of the method:

<FONT COLOR=blue><FONT SIZE=4>def find_defining_class(obj, meth_name):
    for ty in type(obj).mro():
        if meth_name in ty.__dict__:
            return ty
</FONT></FONT>

Here’s an example:

<FONT COLOR=blue><FONT SIZE=4>>>> hand = Hand()

>>> print find_defining_class(hand, 'shuffle') <class 'Card.Deck'>

</FONT></FONT>

So the shuffle method for this Hand is the one in Deck.

<A NAME="@default1648"></A>

<A NAME="@default1649"></A>

<A NAME="@default1650"></A>

find_defining_class uses the mro method to get the list

of class objects (types) that will be searched for methods. “MRO”

stands for “method resolution order.”

<A NAME="@default1651"></A>

<A NAME="@default1652"></A> <A NAME="@default1653"></A>

<A NAME="@default1654"></A>

Here’s a program design suggestion: whenever you override a method,

the interface of the new method should be the same as the old. It should take the same parameters, return the same type, and obey the same preconditions and postconditions. If you obey this rule, you will find that any function designed to work with an instance of a superclass, like a Deck, will also work with instances of subclasses

like a Hand or PokerHand.

If you violate this rule, your code will collapse like (sorry) a house of cards.

<A NAME="toc202"></A><A NAME="htoc222">18.10</A>  Glossary

encode:
To represent one set of values using another

set of values by constructing a mapping between them.

<A NAME="@default1655"></A>
class attribute:
An attribute associated with a class object. Class attributes are defined inside a class definition but outside any method. <A NAME="@default1656"></A> <A NAME="@default1657"></A>
instance attribute:
An attribute associated with an instance of a class. <A NAME="@default1658"></A> <A NAME="@default1659"></A>
veneer:
A method or function that provides a different interface to another function without doing much computation. <A NAME="@default1660"></A>
inheritance:
The ability to define a new class that is a modified version of a previously defined class. <A NAME="@default1661"></A>
parent class:
The class from which a child class inherits. <A NAME="@default1662"></A>
child class:
A new class created by inheriting from an existing class; also called a “subclass.” <A NAME="@default1663"></A>
IS-A relationship:
The relationship between a child class and its parent class. <A NAME="@default1664"></A>
HAS-A relationship:
The relationship between two classes where instances of one class contain references to instances of the other. <A NAME="@default1665"></A>
class diagram:
A diagram that shows the classes in a program and the relationships between them. <A NAME="@default1666"></A> <A NAME="@default1667"></A>
multiplicity:
A notation in a class diagram that shows, for a HAS-A relationship, how many references there are to instances of another class. <A NAME="@default1668"></A>

<A NAME="toc203"></A><A NAME="htoc223">18.11</A>  Exercises

Exercise 5   <A NAME="@default1669"></A>

The following are the possible hands in poker, in increasing order of value (and decreasing order of probability):

pair:
two cards with the same rank
two pair:
two pairs of cards with the same rank
three of a kind:
three cards with the same rank
straight:
five cards with ranks in sequence (aces can be high or low, so Ace-2-3-4-5 is a straight and so is 10-Jack-Queen-King-Ace, but Queen-King-Ace-2-3 is not.)
flush:
five cards with the same suit
full house:
three cards with one rank, two cards with another
four of a kind:
four cards with the same rank
straight flush:
five cards in sequence (as defined above) and with the same suit

The goal of these exercises is to estimate

the probability of drawing these various hands.

  1. Download the following files from thinkpython.com/code:
    Card.py
    : A complete version of the Card, Deck and Hand classes in this chapter.
    PokerHand.py
    : An incomplete implementation of a class that represents a poker hand, and some code that tests it.
  2. If you run PokerHand.py, it deals six 7-card poker hands

    and checks to see if any of them contains a flush. Read this

    code carefully before you go on.
  3. Add methods to PokerHand.py named has_pair, has_twopair, etc. that return True or False according to whether or not the hand meets the relevant criteria. Your code should work correctly for “hands” that contain any number of cards (although 5 and 7 are the most common sizes).
  4. Write a method named classify that figures out the highest-value classification for a hand and sets the label attribute accordingly. For example, a 7-card hand might contain a flush and a pair; it should be labeled “flush”.
  5. When you are convinced that your classification methods are working, the next step is to estimate the probabilities of the various hands. Write a function in PokerHand.py that shuffles a deck of cards, divides it into hands, classifies the hands, and counts the number of times various classifications appear.
  6. Print a table of the classifications and their probabilities. Run your program with larger and larger numbers of hands until the output values converge to a reasonable degree of accuracy. Compare your results to the values at wikipedia.org/wiki/Hand_rankings.
Exercise 6  

<A NAME="@default1670"></A> <A NAME="@default1671"></A>

This exercise uses TurtleWorld from Chapter <A HREF="book005.html#turtlechap">4</A>.

You will write code that makes Turtles play tag. If you are not familiar with the rules of tag, see

wikipedia.org/wiki/Tag_(game).

  1. Download thinkpython.com/code/Wobbler.py and run it. You

    should see a TurtleWorld with three Turtles. If you press the

    Run button, the Turtles wander at random.
  2. Read the code and make sure you understand how it works. The Wobbler class inherits from Turtle, which means that the Turtle methods lt, rt, fd and bk work on Wobblers.

    The step method gets invoked by TurtleWorld. It invokes steer, which turns the Turtle in the desired direction, wobble, which makes a random turn in proportion to the Turtle’s clumsiness, and move, which moves forward a few pixels, depending on the Turtle’s speed.

    <A NAME="@default1672"></A>

  3. Create a file named Tagger.py. Import everything from

    Wobbler, then define a class named Tagger that inherits

    from Wobbler. Call make_world passing the Tagger class object as an argument.
  4. Add a steer method to Tagger to override the one in Wobbler. As a starting place, write a version that always points the Turtle toward the origin. Hint: use the math function atan2 and the Turtle attributes x, y and heading.
  5. Modify steer so that the Turtles stay in bounds. For debugging, you might want to use the Step button, which invokes step once on each Turtle.
  6. Modify steer so that each Turtle points toward its nearest neighbor. Hint: Turtles have an attribute, world, that is a reference to the TurtleWorld they live in, and the TurtleWorld has an attribute, animals, that is a list of all Turtles in the world.
  7. Modify steer so the Turtles play tag. You can add methods to Tagger and you can override steer and __init__, but you may not modify or override step, wobble or move. Also, steer is allowed to change the heading of the Turtle but not the position.

    Adjust the rules and your steer method for good quality play; for example, it should be possible for the slow Turtle to tag the faster Turtles eventually.

You can get my solution from thinkpython.com/code/Tagger.py.


<A NAME="note30" HREF="#text30">1</A>
See wikipedia.org/wiki/Bottom_dealing.
<A NAME="note31" HREF="#text31">2</A>
The diagrams I am using here are similar to UML (see wikipedia.org/wiki/Unified_Modeling_Language), with a few simplifications.

<A HREF="book018.html"><IMG SRC="previous_motif.gif" ALT="Previous"></A> <A HREF="index.html"><IMG SRC="contents_motif.gif" ALT="Up"></A> <A HREF="book020.html"><IMG SRC="next_motif.gif" ALT="Next"></A> </BODY> </HTML>