Fundamentals 12 min read

Master Python OOP: Classes, Inheritance, Magic Methods & More

This comprehensive guide walks through Python's object‑oriented programming essentials, covering class definitions, attributes, common magic methods, inheritance, polymorphism, exception handling, as well as module and package creation and usage, complete with clear code examples for each concept.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Master Python OOP: Classes, Inheritance, Magic Methods & More

1. Classes and Objects

Definition format using the class keyword, class attributes, __init__ initializer, instance methods, class methods, and static methods.

class ClassName:
    # class attribute
    class_attribute = "I am a class attribute"
    # initializer
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
    # instance method
    def instance_method(self):
        return f"Param1: {self.param1}, Param2: {self.param2}"
    # class method
    @classmethod
    def class_method(cls):
        return "This is a class method"
    # static method
    @staticmethod
    def static_method():
        return "This is a static method"

Creating an object and accessing or adding attributes:

obj = ClassName("value1", "value2")
print(obj.param1)          # value1
obj.new_attribute = "new_value"
print(obj.new_attribute)   # new_value

2. Common Magic Methods

Magic methods are special methods that start and end with double underscores. Common examples include __init__, __str__, __repr__, __len__, __call__, __getitem__, __setitem__, __iter__, and __next__.

class MyClass:
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return f"MyClass with value: {self.value}"
    def __repr__(self):
        return f"MyClass({self.value})"
    def __len__(self):
        return len(self.value)
    def __call__(self):
        print(f"Called with value: {self.value}")
    def __getitem__(self, index):
        return self.value[index]
    def __setitem__(self, index, value):
        self.value[index] = value
    def __iter__(self):
        self.index = 0
        return self
    def __next__(self):
        if self.index < len(self.value):
            result = self.value[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

obj = MyClass([1, 2, 3])
print(obj)               # MyClass with value: [1, 2, 3]
print(repr(obj))         # MyClass([1, 2, 3])
print(len(obj))          # 3
obj()                    # Called with value: [1, 2, 3]
print(obj[1])            # 2
obj[1] = 4
print(obj[1])            # 4
for item in obj:
    print(item)          # 1 4 3

3. Inheritance

Inheritance allows a subclass to acquire attributes and methods from a parent class.

class Animal:
    def __init__(self, name):
        self.name = name
    def speak(self):
        pass

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

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

dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak())   # Buddy says Woof!
print(cat.speak())   # Whiskers says Meow!

4. Public and Private Attributes

Public attributes are accessible directly; private attributes are prefixed with double underscores to trigger name mangling.

class MyClass:
    def __init__(self):
        self.public_attr = "Public"
        self.__private_attr = "Private"
    def get_private_attr(self):
        return self.__private_attr

obj = MyClass()
print(obj.public_attr)          # Public
# print(obj.__private_attr)    # AttributeError
print(obj.get_private_attr())   # Private

5. Polymorphism

Polymorphism lets different classes respond to the same method call, typically via method overriding.

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

def animal_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()
animal_sound(dog)   # Woof!
animal_sound(cat)   # Meow!

6. Class and Instance Attributes

Class attributes belong to the class itself and are shared by all instances; instance attributes belong to each object individually.

class MyClass:
    class_attribute = "I am a class attribute"
    def __init__(self, instance_attribute):
        self.instance_attribute = instance_attribute

print(MyClass.class_attribute)          # I am a class attribute
obj = MyClass("I am an instance attribute")
print(obj.instance_attribute)          # I am an instance attribute

7. Class Methods and Static Methods

Class methods receive the class ( cls) as the first argument; static methods receive no implicit first argument.

class MyClass:
    class_attribute = "I am a class attribute"
    @classmethod
    def class_method(cls):
        return f"Class method: {cls.class_attribute}"
    @staticmethod
    def static_method():
        return "Static method"

print(MyClass.class_method())   # Class method: I am a class attribute
print(MyClass.static_method())  # Static method

8. Exception Handling

Use try / except / else / finally to manage runtime errors, and define custom exception classes when needed.

try:
    result = 10 / 0
except ZeroDivisionError as e:
    print(f"Error: {e}")
else:
    print("No error occurred")
finally:
    print("This will always execute")

def divide(x, y):
    try:
        return x / y
    except ZeroDivisionError as e:
        print(f"Error in divide: {e}")
        raise

try:
    result = divide(10, 0)
except ZeroDivisionError as e:
    print(f"Caught in main: {e}")

class MyCustomError(Exception):
    def __init__(self, message):
        super().__init__(message)

try:
    raise MyCustomError("This is a custom error")
except MyCustomError as e:
    print(f"Caught custom error: {e}")

def check_positive(value):
    if value <= 0:
        raise ValueError("Value must be positive")
    return value

try:
    result = check_positive(-10)
except ValueError as e:
    print(f"Error: {e}")

9. Modules

Modules are Python files that can be imported to organize and reuse code.

# mymodule.py
def greet(name):
    return f"Hello, {name}!"

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

Importing a module:

import mymodule
print(mymodule.greet("Alice"))

Importing specific names:

from mymodule import greet, Person
print(greet("Alice"))
p = Person("Bob", 25)
print(p.greet())

Controlling wildcard imports with __all__:

# mymodule.py
__all__ = ['greet']

def greet(name):
    return f"Hello, {name}!"

def farewell(name):
    return f"Goodbye, {name}!"

from mymodule import *
print(greet("Alice"))   # works
# print(farewell("Alice"))  # NameError

The __name__ variable indicates whether a module is run as a script or imported.

# mymodule.py
def greet(name):
    return f"Hello, {name}!"

if __name__ == '__main__':
    print(greet("Alice"))

10. Packages

Packages are directories containing an __init__.py file, allowing hierarchical organization of modules.

mypackage/
    __init__.py
    module1.py
    module2.py

Importing a package and its modules:

import mypackage
from mypackage import module1
from mypackage.module1 import some_function
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonException HandlingOOPclassesModulesInheritancePackages
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.