Python OOP Tutorial - Classes, Objects, Inheritance Examples
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
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.
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.
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
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.
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!
Level Up Your Python Skills
Master Python with these hand-picked resources
10,000+ learners
Free forever
Updated 2026
Related Python Topics