CodingNic

Object Oriented Programming

Object Oriented Programming Exercises

Object Oriented Programming 40 min read

Object Oriented Programming Exercises

Objectives

This chapter introduces no new concepts. It’s a chance to check your understanding of object-oriented programming.

Part I: Recall Questions

  1. What is a class? What is an instance?
  2. What is encapsulation? Give an example.
  3. What is abstraction? Give an example.
  4. What is inheritance, and how does it reduce code duplication?
  5. What is multiple inheritance?
  6. What is polymorphism? Does it require inheritance?
  7. What is method resolution order (MRO), and how would you check it for a class?

Part II: Properties, Dataclasses, and Exceptions

  1. Write a Temperature class storing a value in Celsius, with a @property called fahrenheit that computes and returns the Fahrenheit equivalent, and a matching setter, so assigning to .fahrenheit updates the underlying Celsius value instead.
  2. Rewrite a simple class you’ve already written in this module (something with just a few stored attributes and no real behavior) as a @dataclass, and confirm __repr__ and __eq__ work without you writing them.
  3. Write a custom exception NegativeDepositError, and use it inside a small Wallet class whose deposit(amount) method raises it if amount is negative.

Part III: Abstract Base Classes

  1. Define an abstract base class Shape with an @abstractmethod called area(). Write two subclasses, Circle and Rectangle, each implementing area() correctly. Confirm that trying to instantiate Shape directly raises a TypeError.

Part IV: Build a Deck of Cards

Model a standard 52-card deck using two classes.

Card

  • Has a suit (Hearts, Diamonds, Clubs, or Spades) and a value (A, 2–10, J, Q, K)
  • Implement __str__ (or __repr__) so printing a card shows something readable, like "A of Spades"

Deck

  • On creation, builds all 52 unique Card combinations
  • shuffle(): resets the deck to all 52 cards, then randomizes their order (try the random module from the previous module)
  • deal(): removes and returns one card from the deck, so the same card can never be dealt twice in a row without a reshuffle

Starter Structure

python
import random

SUITS = ["Hearts", "Diamonds", "Clubs", "Spades"]
VALUES = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]

class Card:
    def __init__(self, suit, value):
        self.suit = suit
        self.value = value

    def __str__(self):
        return f"{self.value} of {self.suit}"


class Deck:
    def __init__(self):
        self.cards = self._build_full_deck()

    def _build_full_deck(self):
        return [Card(suit, value) for suit in SUITS for value in VALUES]

    def shuffle(self):
        self.cards = self._build_full_deck()
        random.shuffle(self.cards)

    def deal(self):
        return self.cards.pop() if self.cards else None

Try extending it further: track how many cards remain, prevent dealing from an empty deck, or add a Hand class that collects cards dealt to a player.

Try It

  1. Build the Card and Deck classes above (or your own version), create a deck, shuffle it, and deal out five cards.
  2. Print len(deck.cards) before and after dealing to confirm it shrinks by one each time.
  3. Bonus: add a Hand class that a Deck can deal cards into.
  4. Build the Temperature class from Part II, and confirm both reading .fahrenheit and assigning to it work correctly.
  5. Build the Shape/Circle/Rectangle classes from Part III, and confirm Shape() on its own raises TypeError.

Recap

You can now describe encapsulation, abstraction, inheritance, and polymorphism, write your own classes with instance methods, class methods, and static methods, control attribute access with @property, cut boilerplate with @dataclass, define your own exception classes, enforce required methods with abc, and use super() and MRO to reason about multiple inheritance. That’s the full toolkit Module 6 set out to build.

Next lesson: a mini project pulling all of this together into a Library Management System.