- Don't inherit from
(object); Python 2 needs to die (and you already rely on Python 3 given your current code) - An OO approach would have a
Suitclass (even if it's a lightweight named tuple) with properties of, perhaps: symbol, name, letter, colour. This is a little less awkward thanis_red. These properties can then be factored away fromCard, and only a reference to a suit object kept for each card. - Having the
colordictionary onCarddoesn't make much sense; you'll want to have a module or class that takes care of screen operations such as clear, and ANSI escape sequences for colour etc. - The pattern
Card.color['r'] + self.symbol + r + Card.color['w']calls out for a utility or two. One pattern you could follow is a@contextmanagerthat sets the colour, runs the inner code, and then clears the colour after (even if an exception is thrown). - You have some typehints - good! But they're inconsistent. For instance,
def get_symbol(self, colored=0) -> str:
needs a hint for colored. Since you asked, your hand=None should be hand: Optional[List[Card]] = None.
- Your
Blackjackconstructor shouldn't be responsible for running the entire game; just initialisation. - The call
self.dealer.__init__()is concerning. Presumably that object is already initialised viaHand(), so - calling it again is surprising. If you just want to replace the instance, replace the instance. length,is_bustedetc. are a good fit to be@propertys. The purpose of a property is not particularly to do a character-by-character code length trade; it's to communicate to callers of your class that certain values from your class are available without needing to pass parameters, ideally without mutating the class, and without a whole lot of computation. Properties are also convenient when using modern debuggers as they appear without having to be explicitly called.- You have some manually-formatted currency strings. locale.currency is made for this.
self.bankroll // 1000 // 1000 * 1000seems like you're doing a little bit of trickery to get a round effect. That's less obvious than just calling round. You say I was trying to give two differentmin_betfor users. $5 for someone with less than $1 million bankroll, and roughly 0.1% of bankroll rounded down to a thousand dollars for someone with a million or above bankroll. Clear code for this behaviour is important (also, don't name a variabled):
if self.bankroll < 1e6:
default = self.min_bet
else:
default = round(self.bankroll/1000, -3)
user_input < lo or user_input > hiis equivalent tonot (lo <= user_input <= hi)- Apparently this is controversial, but my opinion is that $100m is ten cents. $100M is 100 million dollars.