Learn by reading through in order

Python OOP Wrap-Up Problems — Put What You Learned into Code

Four end-of-chapter Python OOP problems: special methods on Point, super() in SavingsAccount, @property validation in Celsius, and area() polymorphism across shapes.

Problem 1: Design a Point class with special methods

Build a Point class that represents a 2D coordinate. You'll initialize attributes in __init__, define what + does between two points with __add__, and shape what print() shows with __str__ — three special methods working together.

How the three special methods fire in order
Point(1, 2)__init__: self.x=1, self.y=2p1 + p2__add__: returns Point(4, 6)print(p1 + p2)__str__: "(4, 6)"+print
__init__ sets attributes → __add__ returns a new instance → __str__ formats the string — each gets called in turn.

Write a Point class that holds an x and y coordinate.

Design it so that calling print(Point(1, 2) + Point(3, 4)) prints (4, 6) to the screen.

Python Editor

Run code to see output

Problem 2: Extend a bank account with inheritance and super()

Inherit from a parent class BankAccount to build a SavingsAccount that can apply interest. The pattern is classic inheritance: call the parent's initializer with super().__init__(...), then add the child-specific attributes on top.

Call super() to reuse the parent's __init__, then add your own
BankAccount (parent)name / balanceSavingsAccount (child)name / balance / rate__init__self.name, self.balance__init__super().__init__(...)add self.rateapply_interest()balance += balance * rateinheritssuper callextends
Calling super().__init__(...) from the child's __init__ lets you reuse the parent's setup and only add what's new in the child.

Write a SavingsAccount class that inherits from BankAccount. In addition to the parent's name and balance, SavingsAccount carries an interest rate.

Add an apply_interest() method that adds interest (balance × rate, truncated to an integer) to the current balance. Build SavingsAccount("Alice", 100000, 0.05), call apply_interest(), and design the class so print() shows Alice: 105000.

Python Editor

Run code to see output

Problem 3: Validate temperature with @property

Write a Celsius class that rejects assignments below absolute zero (-273.15). You'll use @property and a @value.setter to slot validation right into attribute assignment — that's the classic encapsulation pattern.

The setter validates every assignment
Celsius(25)self.value = 25 → settert.value = -300reassignment → setter25 >= -273.15validation passes-300 < -273.15validation failsself._value = 25storedraise ValueErrorexception
t.value = X looks like a plain assignment, but it routes through the setter, which validates the value first. Out of range → ValueError, in range → store in _value.

Write a Celsius class with a value attribute that raises ValueError("Temperature below absolute zero is not allowed") when you try to assign anything less than -273.15.

Build Celsius(25) and print the temperature, then wrap an assignment of t.value = -300 in try / except and print the error message in the form Error: ....

Python Editor

Run code to see output

Problem 4: Use polymorphism to compute areas in one loop

Give two different classes a method with the same name (area()), put their instances in a list, and process them with one for loop. The caller doesn't care which class is which — that's polymorphism in a nutshell.

Same method name, different behavior per type
Rectanglewidth × heightshapes list[Rectangle, Circle]Circleπ × radius²Rectangle.area()= w × hfor shape in shapes:shape.area()Circle.area()= π × r²forif Rectangleif Circle
When you call shape.area() on each item in the list, the method matching the item's actual type is automatically picked.

Write two classes, Rectangle(width, height) and Circle(radius), both with an area() method that returns the area. Rectangle computes width × height and Circle computes π × radius squared.

Put Rectangle(3, 4) and Circle(5) in a list, loop over it, and print each area as Area: 12.00 — formatted to two decimal places. Use math.pi for π.

Python Editor

Run code to see output

Nice work getting through this

That wraps up Python OOP. You've covered class and instance design, special methods like __init__ / __add__ / __str__, inheritance with super(), encapsulation with @property, polymorphism, the with statement, and type hints — pretty much every tool you need to bundle data and behavior into a single type. Now you can define your own types, attach methods to them, and treat several types through a shared interface.

The next chapter, Python Advanced, is about going beyond a single file: organizing modules and packages, and using the standard librarydatetime, os, re, json, collections, dataclasses, asyncio, and more — to build the kind of processing you'll actually run into in production code.