Tip
Avoid clone methods
A clone method that unnecessarily duplicates an object is rarely needed in Python. Using cloning may be an indication of failure to understand the object-oriented design principles available in Python.
A clone method encapsulates the knowledge of object creation in the wrong place. The source object that's being cloned cannot know about the structure of the target object that was built from the clone. However, the reverse (targets having knowledge about a source) is acceptable if the source provides a reasonably well-encapsulated interface.
The examples we have shown here are effectively cloning because they're so simple. We'll expand on them in the next chapter. However, to show ways in which these fundamental techniques are used to do more than trivial cloning, we'll look at turning a mutable Hand
object into a frozen, immutable Hand
object.
The following is an example of a Hand
object that can be built in either of the two ways:
class Hand3: def __init__( self, *args, **kw ): if len(args) == 1 and isinstance(args[0],Hand3): # Clone an existing hand; often a bad idea other= args[0] self.dealer_card= other.dealer_card self.cards= other.cards else: # Build a fresh, new hand. dealer_card, *cards = args self.dealer_card= dealer_card self.cards= list(cards)
In the first case, a Hand3
instance has been built from an existing Hand3
object. In the second case, a Hand3
object has been built from individual Card
instances.
This parallels the way a frozenset
object can be built from individual items or an existing set
object. We look more at creating immutable objects in the next chapter. Creating a new Hand
from an existing Hand
allows us to create a memento of a Hand
object using a construct like the following code snippet:
h = Hand( deck.pop(), deck.pop(), deck.pop() ) memento= Hand( h )
We saved the Hand
object in the memento
variable. This can be used to compare the final with the original hand that was dealt, or we can freeze it for use in a set or mapping too.