Intro to Classes & Constructors
This lesson is based directly on the class-based Tic-tac-toe implementation in 2025-08-18-tictactoe-code.ipynb. All code cells below are taken from that notebook (unchanged or only lightly formatted) so you can study how class, __init__, methods, and object collaboration work in a real example. This will be a comprehensive guide about class methods, and how they can be applied into your code projects!
What you’ll learn
- How to define classes with
__init__and instance variables
You’ll see how to create a class in Python, use the__init__method to set up initial state, and store data in instance variables that are unique to each object. - How to add methods that operate on instance state
Learn how to write functions (methods) inside a class that can access and modify the object’s data, making your code more organized and reusable. - How classes collaborate (composition)
Understand how one class can use another class as a building block, allowing you to break complex problems into smaller, manageable pieces. - How to test classes with short demos
Practice writing small test cases or demo code to check that your classes and methods work as expected, helping you catch bugs early and build confidence in your code.
How a class works
A class is a blueprint for creating objects (instances). It groups data (attributes) and behavior (methods) that belong together. Think of a class like a blueprint for a car; each car you create from that blueprint is an instance with its own state (color, mileage). init is a special method Python calls automatically when you create (instantiate) an object. Purpose: initialize the instance’s attributes (its state). Signature pattern: def init(self, …) — self is the new instance being created.
class Player:
def __init__(self, name, symbol):
self.name = name
self.symbol = symbol
Board class — encapsulates grid and rules
Below is the Board implementation copied from the notebook. It contains display helpers, move validation, undo, and win detection.
class Board:
def __init__(self):
self.grid = [" "] * 9
def display(self):
print("\n")
print(" " + self.grid[0] + " | " + self.grid[1] + " | " + self.grid[2])
print("---+---+---")
print(" " + self.grid[3] + " | " + self.grid[4] + " | " + self.grid[5])
print("---+---+---")
print(" " + self.grid[6] + " | " + self.grid[7] + " | " + self.grid[8])
print("\n")
def display_reference(self):
reference = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
print("Board positions:\n")
print("\n")
print(" " + reference[0] + " | " + reference[1] + " | " + reference[2])
print("---+---+---")
print(" " + reference[3] + " | " + reference[4] + " | " + reference[5])
print("---+---+---")
print(" " + reference[6] + " | " + reference[7] + " | " + reference[8])
print("\n")
def is_full(self):
return " " not in self.grid
def make_move(self, position, symbol):
index = position - 1
if index < 0 or index > 8:
print("Invalid position. Choose a number between 1 and 9.")
return False
if self.grid[index] != " ":
print("That spot is already taken. Try again.")
return False
self.grid[index] = symbol
return True
def check_winner(self, symbol):
win_combinations = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], # Rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], # Columns
[0, 4, 8], [2, 4, 6] # Diagonals
]
for combo in win_combinations:
if (self.grid[combo[0]] == symbol and
self.grid[combo[1]] == symbol and
self.grid[combo[2]] == symbol):
return True
return False
class TicTacToe:
def __init__(self, player1, player2):
self.board = Board()
self.players = [player1, player2]
self.current_player = player1
def switch_player(self):
self.current_player = (
self.players[1] if self.current_player == self.players[0] else self.players[0]
)
def play(self):
print("Welcome to Tic-Tac-Toe!")
print(f"{self.players[0].name} is '{self.players[0].symbol}'")
print(f"{self.players[1].name} is '{self.players[1].symbol}'")
print("Players take turns choosing a position (1–9).\n")
self.board.display_reference()
self.board.display()
while True:
try:
move = int(input(f"{self.current_player.name} ({self.current_player.symbol}), enter your move (1-9): "))
except ValueError:
print("Invalid input. Please enter a number from 1 to 9.")
continue
if not self.board.make_move(move, self.current_player.symbol):
continue
self.board.display()
if self.board.check_winner(self.current_player.symbol):
print(f"{self.current_player.name} ({self.current_player.symbol}) wins!")
break
if self.board.is_full():
print("It's a tie!")
break
self.switch_player()
if __name__ == "__main__":
player1 = Player("Player 1", "X")
player2 = Player("Player 2", "O")
game = TicTacToe(player1, player2)
game.play()
AIPlayer subclass (optional)
The original notebook includes an AIPlayer subclass; include it here for completeness—it’s an extension of Player that provides smarter get_move logic.
class AIPlayer(Player):
def __init__(self, name, symbol):
super().__init__(name, symbol)
self.is_ai = True
def get_move(self, board):
"""
Implements the AI strategy in this order:
1. Win if possible
2. Block opponent's winning move
3. Take center
4. Take corner
5. Take any available space
"""
# Try to win
winning_move = self._find_winning_move(board, self.symbol)
if winning_move is not None:
return winning_move
# Block opponent's winning move
opponent_symbol = "O" if self.symbol == "X" else "X"
blocking_move = self._find_winning_move(board, opponent_symbol)
if blocking_move is not None:
return blocking_move
# Take center if available
if board.grid[4] == " ":
return 5
# Take a corner if available
corners = [0, 2, 6, 8]
import random
random.shuffle(corners) # Add randomness to corner selection
for corner in corners:
if board.grid[corner] == " ":
return corner + 1
# Take any available space
for i in range(9):
if board.grid[i] == " ":
return i + 1
def _find_winning_move(self, board, symbol):
"""Check all possible moves to find a winning move"""
for i in range(9):
if board.grid[i] == " ":
# Try move
board.grid[i] = symbol
if board.check_winner(symbol):
board.grid[i] = " " # Reset the move
return i + 1
board.grid[i] = " " # Reset the move
return None
Simulated game demo
Use the code below to run a short simulated game or to drive the full interactive loop if you have a terminal. This demo is taken from the notebook and shows how classes collaborate.
# Simulated game demo
print("Simulated Tic-Tac-Toe Game")
# Create players
player1 = Player("Alice", "X")
player2 = Player("Bob", "O")
# Create a game instance
game = TicTacToe(player1, player2)
# Simulate a few moves
game.board.display_reference()
game.board.display()
# Player 1 makes a move
game.board.make_move(1, player1.symbol)
game.board.display()
# Player 2 makes a move
game.board.make_move(5, player2.symbol)
game.board.display()
# Player 1 makes another move
game.board.make_move(2, player1.symbol)
game.board.display()
# Player 2 makes another move
game.board.make_move(9, player2.symbol)
game.board.display()
# Player 1 makes the winning move
game.board.make_move(3, player1.symbol)
game.board.display()
# Check for a winner
if game.board.check_winner(player1.symbol):
print(f"{player1.name} ({player1.symbol}) wins!")
elif game.board.check_winner(player2.symbol):
print(f"{player2.name} ({player2.symbol}) wins!")
elif game.board.is_full():
print("It's a tie!")
else:
print("The game is still ongoing.")