This week, I stepped away from data science tools to strengthen a core Python skill: object-oriented programming. I applied what I learned by building a "complete" Blackjack game and discovered why planning object relationships early matters.
In This Post
- The __init__ That Started It
- Building Blackjack with Objects
- The Refactor That Changed Everything
- What I Would Do Differently Next Time
The __init__ That Started It
While reading Deep Learning with Python, Third Edition, I came across __init__ several times as the book used classes to build parts of a neural network from scratch. The examples included dense layers, sequential models, and a batch generator. I had seen __init__ in other people’s Python projects, but I had never understood what it did or how it fit into the larger structure of a program.
Most of my Python education has focused on data science fundamentals, including pandas, NumPy, Matplotlib, data cleaning, and machine learning. I had written plenty of Python code, but I had never created a class of my own.
Researching object-oriented programming helped me see how classes can make code more organized, reusable, and easier to expand. I could also see how reusable objects might help organize repeated tasks in exploratory data analysis.
My goal was not just to understand one line in a textbook. I wanted to prepare myself for larger and more complex Python projects.
Building Blackjack with Objects
I was already approaching the OOP section of my Udemy Python Data Science Bootcamp, so I spent several days working through videos and more than 40 exercises on inheritance, polymorphism, and encapsulation. The concepts made sense, although I still had to slow down to remember details such as when to use self.variable and when a method needed parentheses.
I knew I would understand the material better by building something independently. I chose Blackjack because I enjoy card games, and it is a classic example of a problem that can be divided into objects with different responsibilities.
I began with the parent classes and built the hierarchy downward. The Player class contains shared behavior, while the Dealer and BettingPlayer(HumanPlayer and NPCPlayer) handle certain actions differently. I also used composition: players contain hand objects, and each hand contains card objects.
The project grew quickly. Within about a week of studying and building, I had a complete game loop with betting, dealer rules, computer players, multiple rounds, and final payout results. The project was small enough to finish quickly, but complex enough to show why object structure matters.
The Refactor That Changed Everything
The biggest challenge came when I tried to add the option to split a pair. My original design stored cards directly with each player, which worked as long as every player had only one hand. Splitting requires one player to manage multiple hands, with each hand containing its own cards.
To support that feature, I created a separate Hand class. The new structure allowed a player to contain multiple hands and each hand to contain multiple cards. It was a better design, but I introduced it after the main game loop was already working.
That change affected nearly every part of the program. Player actions, hand totals, Blackjack checks, payouts, and the game loop all had to be updated. I stopped before fully completing the split mechanic, but the refactor taught me more about object design than the original version of the game did.
What I Would Do Differently Next Time
I wanted this to be a quick project that demonstrated the OOP concepts I had learned without taking too much time away from my larger portfolio projects. Instead, it showed me how quickly even a small program can grow.
The main thing I would do differently is plan the object relationships and major features before writing the full game loop. If I had considered split hands earlier, I would have created the Hand class from the beginning rather than storing cards directly with each player.
I can already see another feature that would require similar planning. I would like the game to calculate the probability of busting based on the visible cards that have already been played. To support that cleanly, I would likely need a dedicated Deck object that tracks which cards remain.
For now, Blackjack is going on the shelf while I return to larger portfolio projects. If I revisit it, I would like to finish splitting, improve the computer players so their decisions consider the dealer’s visible card, and add bust-probability calculations.
My biggest takeaways were the importance of planning the object structure early and how OOP can reduce repetition by letting related objects share reusable behavior.
Want to try the game yourself? Check out the project on GitHub, clone the repository, install the colorama package for the terminal colors, and run python main.py from your command-line terminal. Feel free to explore the code, play a few rounds, or share any feedback.




Comments
Post a Comment