Complexities and the callable interface

Let's see how well this interface design holds up as our processing becomes more complex. The following is the double-up on each loss strategy (also known as the Martingale betting system):

class BettingMartingale(BettingStrategy):

def __init__(self) -> None:
self._win = 0
self._loss = 0
self.stage = 1

@property
def win(self) -> int:
return self._win

@win.setter
def win(self, value: int) -> None:
self._win = value
self.stage = 1

@property
def loss(self) -> int:
return self._loss

@loss.setter
def loss(self, value: int) -> None:
self._loss = value
self.stage *= 2

def __call__(self) -> int:
return self.stage

Each loss doubles the betting by multiplying the stage by two. This goes on until we win and recoup our losses, reach the table limit, or go broke and can no longer place any bets. Casinos limit this by imposing table limits.

Whenever we win, the betting is reset to the base bet. The stage variable is reset to have a value of 1.

The goal is to easily access an attribute value. The client of this class will be able to use bet.win += 1. This can depend on the property setter methods to make additional state changes based on the wins and losses. We only really care about the setter properties, but we must define the getter properties in order to clearly create the setter properties. In addition to counting wins and losses, the setter methods also set the stage instance variable.

We can see this class in action in the following code snippet:

>>> bet= BettingMartingale() 
>>> bet() 
1 
>>> bet.win += 1 
>>> bet() 
1 
>>> bet.loss += 1 
>>> bet() 
2 

The interface to this object is still quite simple. We can either count the wins and reset the bet to the base, or we can count the losses, and the bets will double.

The use of properties made the class definition long and hideous. Since we're really only interested in the setter properties and not the getter properties, we can use __setattr__() to streamline the class definition somewhat, as shown in the following code:

class BettingMartingale2(BettingStrategy):

def __init__(self) -> None:
self.win = 0
self.loss = 0
self.stage = 1

def __setattr__(self, name: str, value: int) -> None:
if name == "win":
self.stage = 1
elif name == "loss":
self.stage *= 2
super().__setattr__(name, value)

def __call__(self) -> int:
return self.stage

We used __setattr__() to monitor the changes to the win and loss attributes. In addition to setting the instance variables using super().__setattr__(), we also updated the internal state for the betting amount.

This is a nicer looking class definition, and it retains the same, simple interface as the original callable object with two attributes.

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

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