Software Design Spring 2007 For today you should have: 1) worked on homework 7 2) prepared for a quiz (postponed!) For Wednesday: 1) finish Homework 7 class attributes ---------------- In the Card class, rankList and suitList are class attributes 1) defined inside the class definition, but outside any function 2) there is only one copy of the class attributes as opposed to instance attributes (one per instance) In a sense, all the Cards share the same rankList and suitList. class Card: rankList = ... suitList = ... # class attributes can be accessed as if they # were instance attributes: def __str__(self): return '%s of %s' % (self.rankList[self.rank], self.suitList[self.suit]) # or by specifying the class name print Card.rankList Decks ----- How do we represent a Deck? 1) a list or tuple of cards 2) a set of cards (represented with a dictionary) 3) a new object type that contains a list of cards as an attribute. Any of these might be a reasonable choice, but in an object-oriented program, we would tend to favor (3). class Deck: def __init__(self): self.cards = [] for suit in range(4): for rank in range(1, 14): self.cards.append(Card(suit, rank)) Draw a picture of what a Deck object looks like. One reason to prefer this choice is that we have a natural place to keep methods that pertain to Decks: def shuffle(self): num = len(self.cards) for i in range(num): j = random.randrange(i, num) self.cards[i], self.cards[j] = self.cards[j], self.cards[i] Abstractly, it is useful to think "A deck is a list of cards" but mechanically we have to remember that a Deck object has an attribute named cards that refers to a list of Card objects. Inheritance ----------- Probably the characteristic feature of object-oriented programming. A new class extends an existing class: class Hand(Deck): def __init__(self, name=''): self.cards = [] self.name = name The new class has all the methods of the old class. The new class can add additional methods, or override existing ones. For example, Hand.__init__ replaces Deck.__init__ class Tagger(Turtle): def __init__(self, world, speed=1, clumsiness=60): Turtle.__init__(self, world) self.delay = 0 self.speed = speed self.clumsiness = clumsiness self.it = 0 self.sulking = 0 self.goal = None def step(self): ... A Tagger is a kind of Turtle, so it has all the methods in the Turtle class. Tagger.__init__ overrides Turtle.__init__, but it also invokes Turtle.__init__ Tagger inherits from Turtle, Turtle inherits from Animal, Animal is a base class -- it inherits from the Ur-object. These chains form a CLASS HIERARCHY. Child classes inherit from parent classes. (or sometimes) Subclasses extend superclasses. UML Class Diagrams ------------------ A UML Class Diagram shows relationships between classes. It is less detailed (more abstract) than an object diagram. Inheritance denotes the IS-A relationship between classes: 1) a Tagger is a kind of Turtle 2) a Turtle is a kind of Animal Embedded objects represent a HAS-A relationship between objects: 1) a Turtle has a reference to a World 2) World has a list of references to Animals 3) a Deck has a list of references to Cards These relationships are shown graphically in a UML Class Diagram. Example: python PokerHand.py Homework 6 Solutions -------------------- sd_hw06_soln.py is available from the class web page. If you create a new class that inherits from exception: class Missed(Exception): """this the exception raised when a turtle tries to tag someone too far away""" You can use the class object in a raise statement (no need to instantiate a Missed object): def apply_tag(self, other): """try to tag the other turtle. if it is too far away, let the other turtle flee and raise an exception""" if self.distance_from(other) < 10: self.not_it() other.youre_it() else: other.flee(self) raise Missed Optionally, you can provide additional information: raise Missed, 'nah, nah!' Either way, you can catch the exception: if self.it: target = self.closest(world.animals) try: self.apply_tag(target) except Missed: self.chase(target) closest demonstrates a variation on the DSU pattern: def closest(self, others): """return the animal in the list that is closest to self (not including self!)""" t = [(self.distance_from(animal), animal) for animal in others if animal is not self] (distance, animal) = min(t) return animal And a new feature called a "list comprehension".