1
\$\begingroup\$

This example of a blackjack insurance object is typical of how I find myself writing classes in Python. I particularly wanted some feedback on my use __new__ to implement the Null Object Pattern and whether this as considered a good approach or not.

# -*- coding: utf-8 -*-
import unittest
from contextlib import redirect_stdout
from io import StringIO


class Insurance:
    class NoInsurance:
        def payout(self, is_dealer_21):
            return 0

    def __new__(cls, player_name, max_bet):
        ans = input("{}, insurance amount? [0-{}] ".format(
                player_name,
                max_bet))
        try:
            bet = min(max_bet, max(0, int(ans)))
        except ValueError:
            bet = 0
        obj = super().__new__(cls) if bet > 0 else cls.NoInsurance()
        obj.bet = bet
        return obj

    def __init__(self, name, max_bet):
        self.name = name
        self.max_bet = max_bet

    def get_payout(self, is_dealer_21):
        return (self.bet*2, "got an insurance payout of") if is_dealer_21 \
          else (-self.bet, "lost insurance amount of")

    def payout(self, is_dealer_21):
        payout, msg = self.get_payout(is_dealer_21)
        return payout


class RedirectStdin:
    def __init__(self, inputs=[]):
        self.inputs = inputs

    def custom_input(self, *args):
        return self.inputs.pop(0)

    def __enter__(self):
        global input
        self.old_input = input
        input = self.custom_input

    def __exit__(self, *args):
        global input
        input = self.old_input


class TestInsurance(unittest.TestCase):
    def test_payouts(self):
        void = StringIO()
        input_list = ["0", "4", "text", "500"]
        results = []
        expected_results = [
                {'False': 0, 'True': 0, 'class': 'NoInsurance'},
                {'False': -4, 'True': 8, 'class': 'Insurance'},
                {'False': 0, 'True': 0, 'class': 'NoInsurance'},
                {'False': -100, 'True': 200, 'class': 'Insurance'}
            ]
        with RedirectStdin(input_list), redirect_stdout(void):
            for _ in range(len(input_list)):
                x = Insurance('', 100)
                results.append({
                        "True": x.payout(True),
                        "False": x.payout(False),
                        "class": x.__class__.__name__
                        })
        for result, expected_result in zip(results, expected_results):
            self.assertEqual(result, expected_result)


def test():
    unittest.main()


def main():
    msg_21 = "Dealer with blackjack    -> your payout is: {}"
    msg_not_21 = "Dealer without blackjack -> your payout is: {}"
    x = Insurance('Player Name', 100)
    print(msg_21.format(x.payout(True)))
    print(msg_not_21.format(x.payout(False)))
    print("The type of the insurance is: {}".format(x.__class__.__name__))

if __name__ == "__main__":
    main()

Sample run:

>>> main()

Player Name, insurance amount? [0-100] 43242
Dealer with blackjack    -> your payout is: 200
Dealer without blackjack -> your payout is: -100
The type of the insurance is: Insurance
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.