One of the four important features of class definition is
polymorphism. Polymorphism exists when you define a number of subclasses
which have commonly named methods. A function can use objects of any of
the polymorphic classes without being aware that the classes are
In some languages, it is essential that the polymorphic classes
have the same interface (or be subinterfaces of a common parent
interface), or be subclasses of a common superclass. This is sometimes
called "strong, hierarchical typing", since the type rules are very
rigid and follow the subclass/subinterface hierarchy.
Python implements something that is less rigid, often called "duck
typing". The phrase follows from a quote attributed to James Whitcomb
Riley: "When I see a bird that walks like a duck and swims like a duck
and quacks like a duck, I call that bird a duck." In short, two objects
are effectively of the class
Duck if they have a
common collection of methods (walk, swim and quack, for example.)
When we look at the examples for
Ace in the section called “Inheritance”, we see that all three classes
have the same method names, but have different implementations for some
of these methods. These three classes are polymorphic. A client class
Hand can contain individual objects of any
of the subclasses of
Card. A function can
evaluate these polymorphic methods without knowing which specific
subclass is being invoked.
In our example, both
Ace were subclasses of
Card. This subclass relationship isn't necesary
for polymorphism to work correctly in Python. However, the subclass
relationship is often an essential ingredient in an overall design that
relies on polymorphic classes.
What's the Benefit? If we treat all of the various subclasses of
Card in a uniform way, we effectively delegate
any special-case processing into the relevant subclass. We concentrate
the implementation of a special case in exactly one place.
The alternative is to include
over our program to enforce special-case processing rules. This
diffusing of special-case processing means that many components wind up
with an implicit relationship. For example, all portions of a program
that deal with
Cards would need multiple
-statements to separate the number card points,
face card points and ace points.
By making our design polymorphic, all of our subclasses of
Card have ranks and suits, as well as hard and
soft point values. We we can design the
Shoe classes to deal cards in a uniform way. We
can also design a
Hand class to total points
without knowing which specific class to which a
Card object belongs.
Similarly, we made our design for
Shoe classes polymorphic. This allows us to model
one-deck blackjack or multi-deck blackjack with no other changes to our
The Hand of Cards. In order to completely model Blackjack, we'll need a class for
keeping the player and dealer's hands. There are some differences
between the two hands: the dealer, for example, only reveals their
first card, and the dealer can't split. There are, however, some
important similarities. Every kind of
determine the hard and soft point totals of the cards.
The hard point total for a hand is simply the hard total of all
the cards. The soft total, on the other hand, is not simply the soft
total of all cards. Only the
Ace cards have
different soft totals, and only one Ace can meaningfully contribute it's
soft total of 11. Generally, all cards provide the same hard and soft
point contributions. Of the cards where the hard and soft values differ,
only one such card needs to be considered.
Note that we are using the values of the
methods. Since this test applies to all classes of cards, we preserve
polymorphism by checking this property of every card. We'll preserving
just one of the cards with a soft value that is different from the hard
value. At no time do use investigate the class of a
Card to determine if the card is of the class
Ace. Examining the class of each object
needlessly constrains our algorithm. Using the polymorphic methods means
that we can make changes to the class structure without breaking the
processing of the
Pretty Poor Polymorphism
The most common indicator of poor use polymorphism is using the
issubclass functions to determine the class of an
object. These should used rarely, if at all. All processing should be
focused on what is different about the objects, not the class to which
an object belongs.
We have a number of ways to represent the presence of a
Card with a distinct hard and soft value.
An attribute with the point difference (usually 10).
A collection of all
Cards except for
Card with a point difference, and a
single attribute for the extra card.
We'll choose the first implementation. We can use use a sequence
to hold the cards. When cards are added to the hand, the first card that
returns distinct values for the hard value and soft value will be used
to set a variable has keeps the hard vs. soft point difference.
Example 22.2. hand.py
class Hand( object ):
"""Model a player's hand."""
def __init__( self ):
self.cards = [ ]
def addCard( self, aCard ):
self.cards.append( aCard )
if aCard.getHardValue() != aCard.getSoftValue():
if self.softDiff == 0:
def points( self ):
"""Compute the total points of cards held."""
for c in self.cards:
p += c.getHardValue()
if p + self.softDiff <= 21:
return p + self.softDiff
__init__ special function creates
the instance variable,
self.cards, which we
will use to accumulate the
that comprise the hand. This also sets
self.softDiff which is the difference in
points between hard and soft hands. Until we have an Ace, the
difference is zero. When we get an Ace, the difference will be
We provide an
addCard method that
places an additional card into the hand. At this time, we
Card to see if the soft value
is different from the hard value. If so, and we have not set the
self.softDiff yet, we save this
points method evaluates the hand.
It initializes the point count,
p, to zero.
We start a
-loop to assign each card
c. We could, as an alternative, use
reduce function to perform this
reduce( lambda a,b:a+b.getSoftValue(),
self.cards, 0 ).
If the total with the
21 or under, we have a soft hand, and these are the total
points. If the total with the
is over 21, we have a hard hand. The hard hand may total more
than 21, in which case, the hand is bust.