Tutorials Logic, IN info@tutorialslogic.com

Python OOP Classes, Objects, Inheritance

Python OOP Classes, Objects, Inheritance

Python in Python is best learned by connecting the rule to an automation script. Start with the smallest function or script, observe the output, and then add one realistic constraint so the concept becomes practical.

The key habit for this lesson is to watch input value and returned object as it changes. That makes the topic easier to debug, easier to explain in interviews, and easier to use in real code without memorizing isolated syntax.

Object-Oriented Programming

Python is an object-oriented language. A class is a blueprint for creating objects. An object is an instance of a class with its own data (attributes) and behavior (methods).

Defining a Class

Basic Class

Basic Class
class Dog:
    # Class attribute - shared by all instances
    species = "Canis familiaris"

    # __init__ is the constructor
    def __init__(self, name: str, age: int):
        # Instance attributes - unique to each object
        self.name = name
        self.age = age

    # Instance method
    def bark(self) -> str:
        return f"{self.name} says: Woof!"

    def describe(self) -> str:
        return f"{self.name} is {self.age} years old"

# Create objects (instances)
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)

print(dog1.bark())       # Buddy says: Woof!
print(dog2.describe())   # Max is 5 years old
print(dog1.species)      # Canis familiaris
print(Dog.species)       # Canis familiaris (via class)

Special (Dunder) Methods

Methods with double underscores like __init__, __str__, __repr__ are called dunder (magic) methods. They define how objects behave with built-in operations.

Dunder Methods

Dunder Methods
class Point:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def __str__(self) -> str:
        """Called by print() and str()"""
        return f"Point({self.x}, {self.y})"

    def __repr__(self) -> str:
        """Called in REPL and for debugging"""
        return f"Point(x={self.x}, y={self.y})"

    def __add__(self, other: "Point") -> "Point":
        """Called by + operator"""
        return Point(self.x + other.x, self.y + other.y)

    def __eq__(self, other: "Point") -> bool:
        """Called by == operator"""
        return self.x == other.x and self.y == other.y

    def __len__(self) -> int:
        """Called by len()"""
        import math
        return int(math.sqrt(self.x**2 + self.y**2))

p1 = Point(1, 2)
p2 = Point(3, 4)

print(p1)           # Point(1, 2)
print(p1 + p2)      # Point(4, 6)
print(p1 == p2)     # False
print(p1 == Point(1, 2))  # True

Inheritance

A child class inherits attributes and methods from a parent class. Use super() to call the parent's methods.

Inheritance

Inheritance
class Animal:
    def __init__(self, name: str):
        self.name = name

    def speak(self) -> str:
        return f"{self.name} makes a sound"

class Dog(Animal):
    def speak(self) -> str:
        return f"{self.name} says: Woof!"

class Cat(Animal):
    def speak(self) -> str:
        return f"{self.name} says: Meow!"

class GuideDog(Dog):
    def __init__(self, name: str, owner: str):
        super().__init__(name)   # call parent __init__
        self.owner = owner

    def describe(self) -> str:
        return f"{self.name} guides {self.owner}"

animals = [Dog("Buddy"), Cat("Whiskers"), GuideDog("Rex", "Alice")]
for animal in animals:
    print(animal.speak())

# isinstance and issubclass
print(isinstance(animals[0], Dog))     # True
print(isinstance(animals[0], Animal))  # True
print(issubclass(Dog, Animal))         # True

Class Methods, Static Methods & Properties

Decorators

Decorators
class Circle:
    PI = 3.14159

    def __init__(self, radius: float):
        self._radius = radius   # _ prefix = "private by convention"

    @property
    def radius(self) -> float:
        """Getter"""
        return self._radius

    @radius.setter
    def radius(self, value: float):
        """Setter with validation"""
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

    @property
    def area(self) -> float:
        return Circle.PI * self._radius ** 2

    @classmethod
    def from_diameter(cls, diameter: float) -> "Circle":
        """Alternative constructor"""
        return cls(diameter / 2)

    @staticmethod
    def is_valid_radius(r: float) -> bool:
        """Utility - doesn't need self or cls"""
        return r >= 0

c = Circle(5)
print(c.area)          # 78.53975
c.radius = 10          # uses setter
print(c.radius)        # 10

c2 = Circle.from_diameter(20)
print(c2.radius)       # 10.0

print(Circle.is_valid_radius(-1))  # False

Dataclasses (Python 3.7+)

The @dataclass decorator auto-generates __init__, __repr__, and __eq__ for you.

Dataclasses

Dataclasses
from dataclasses import dataclass, field

@dataclass
class Student:
    name: str
    age: int
    grades: list[int] = field(default_factory=list)

    def average(self) -> float:
        return sum(self.grades) / len(self.grades) if self.grades else 0.0

s = Student("Alice", 20, [90, 85, 92])
print(s)              # Student(name='Alice', age=20, grades=[90, 85, 92])
print(s.average())    # 89.0
print(s == Student("Alice", 20, [90, 85, 92]))  # True

# Frozen dataclass (immutable)
@dataclass(frozen=True)
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
# p.x = 5  # FrozenInstanceError!

Applied guide for Python

Use Python when the program needs a clear answer to a specific problem, not because the keyword looks familiar. In a real Python task, first name the input, then name the transformation, then name the output. This small discipline shows whether the topic is being used correctly or only copied from an example.

A reliable practice flow is: create the smallest working function or script, add one normal case, add one edge case such as missing, repeated, empty, or boundary input, and then confirm the result with traceback and printed inspection. If the result surprises you, reduce the code until the behavior is visible again.

The most common trap here is copying the syntax before understanding the behavior. Avoid it by writing one sentence before the code that explains why Python is the right choice. After the code runs, verify the lesson by doing this: change one input and explain the changed output.

  • Identify the exact problem solved by Python.
  • Trace input value and returned object before and after the main operation.
  • Keep one intentionally broken version and explain the fix.
  • Connect the example to an automation script so the idea feels concrete.
Key Takeaways
  • I can explain where Python fits inside an automation script.
  • I can point to the exact input value and returned object affected by this topic.
  • I tested a normal case and an edge case involving missing, repeated, empty, or boundary input.
  • I verified the result with traceback and printed inspection instead of assuming it worked.
  • I can describe the main mistake: copying the syntax before understanding the behavior.
Common Mistakes to Avoid
WRONG Copying the syntax before understanding the behavior.
RIGHT Write the expected behavior first, then make the example prove it.
A one-line expectation turns the code from copied syntax into a testable idea.
WRONG Practicing only the perfect input.
RIGHT Also test missing, repeated, empty, or boundary input before considering the lesson complete.
The edge case is where most interview follow-up questions begin.
WRONG Looking only at the final output.
RIGHT Trace input value and returned object through each important step.
Tracing makes debugging faster because you can see the first incorrect state.

Practice Tasks

  • Build one small function or script that demonstrates Python in an automation script.
  • Change the example to include missing, repeated, empty, or boundary input and record the difference.
  • Break the example by deliberately copying the syntax before understanding the behavior, then write the corrected version.
  • Explain the finished example in five bullet points: input, operation, output, failure case, and verification.

Frequently Asked Questions

Use it when the problem matches the behavior shown in the example and when the result can be verified through traceback and printed inspection.

Start with a tiny case, then test missing, repeated, empty, or boundary input. The main warning sign is copying the syntax before understanding the behavior.

Trace input value and returned object, predict the result, run the example, and compare your prediction with the actual output.

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.