- Mastering Objectoriented Python
- Steven F. Lott
- 354字
- 2021-11-12 16:25:11
Attributes and the __init__() method
Most of the time, we create an initial suite of attributes using the __init__()
method of a class. Ideally, we provide default values for all the attributes in __init__()
.
It's not required to provide all attributes in the __init__()
method. Because of this, the presence or absence of an attribute can be used as part of an object's state.
An optional attribute pushes the edge of the envelope for class definition. It makes considerable sense for a class to have a well-defined set of attributes. Attributes can often be added (or removed) more clearly by creating a subclass or superclass.
Consequently, optional attributes imply a kind of informal subclass relationship. Therefore, we bump up against Pretty Poor Polymorphism when we use optional attributes.
Consider a Blackjack game in which only a single split is permitted. If a hand is split, it cannot be resplit. There are several ways that we can model this:
- We can create a subclass for
SplitHand
from theHand.split()
method. We won't show this in detail. - We can create a status attribute on an object named
Hand
, which can be created from theHand.split()
method. Ideally, this is a Boolean value, but we can implement it as an optional attribute as well.
The following is a version of Hand.split()
that can detect splittable versus unsplittable hands via an optional attribute:
def split( self, deck ): assert self.cards[0].rank == self.cards[1].rank try: self.split_count raise CannotResplit except AttributeError: h0 = Hand( self.dealer_card, self.cards[0], deck.pop() ) h1 = Hand( self.dealer_card, self.cards[1], deck.pop() ) h0.split_count= h1.split_count= 1 return h0, h1
In effect, the split()
method tests to see if there's a split_count
attribute. If this attribute exists, then this is a split hand and the method raises an exception. If the split_count
attribute does not exist, this is an initial deal, and splitting is allowed.
An optional attribute has the advantage of leaving the __init__()
method relatively uncluttered with status flags. It has the disadvantage of obscuring some aspects of object state. This use of a try:
block to determine object state can be very confusing and should be avoided.