Initializing with static or class-level methods

When we have multiple ways to create an object, it's sometimes more clear to use static methods to create and return instances rather than complex __init__() methods.

The term static is borrowed from other languages. Python has three kinds of binding for method functions. The default case is to bind a method to the instance; the first positional parameter is self, the instance variable. A method can be bound to the class; this requires the @staticmethod decorator. A method can also be bound to the class, but receive the class as the first positional parameter; this requires the @classmethod decorator.

In the case of freezing or splitting a Hand object, we might want to create two new static methods to freeze or split a Hand object. Using static methods as surrogate constructors is a tiny syntax change in construction, but it has huge advantages when organizing code.

The following is a version of Hand with static methods that can be used to build new instances of Hand from an existing Hand instance:

class Hand5:

def __init__(self, dealer_card: Card, *cards: Card) -> None:
self.dealer_card = dealer_card
self.cards = list(cards)

@staticmethod
def freeze(other) -> "Hand5":
hand = Hand5(other.dealer_card, *other.cards)
return hand

@staticmethod
def split(other, card0, card1) -> Tuple["Hand5", "Hand5"]:
hand0 = Hand5(other.dealer_card, other.cards[0], card0)
hand1 = Hand5(other.dealer_card, other.cards[1], card1)
return hand0, hand1

def __str__(self) -> str:
return ", ".join(map(str, self.cards))

The freeze() method freezes or creates a memento version. The split() method splits a Hand5 instance to create two new child instances of Hand5. The __init__() method builds a hand from individual Card instances.

This is often more readable and preserves the use of the parameter names to explain the interface. Note how the class name must be provided as a string when used as a type hint within the class definition. You will recall that the class name doesn't exist until after the execution of the class statement. Using strings instead of type objects permits a reference to a type that doesn't exist yet. When mypy evaluates the type hints, it will resolve the type objects from the strings.

The following code snippet shows how we can split a Hand5 instance with this version of the class:

d = Deck() 
h = Hand5(d.pop(), d.pop(), d.pop()) 
s1, s2 = Hand5.split(h, d.pop(), d.pop()) 

We created an initial h instance of Hand5, split it into two other hands, s1 and s2, and dealt an additional Card class into each. The split() static method is much simpler than the equivalent functionality implemented via __init__()

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset