Python • Functions & OOP

Functions, Methods & Classes Deep Dive

Master functions, parameters, methods, classes, inheritance, polymorphism, and production‑grade OOP patterns – with detailed examples, solved problems, and practice challenges.

Topics
1
2
3
4
5
6
7

📌 What you'll learn: Functions are the building blocks of any Python program. Methods bring functions inside classes to manipulate object data. Classes let you model real‑world entities, promote code reuse, and structure large applications. This page covers it all: from simple def to abstract base classes and production patterns.

🎯 Goal: Write clean, reusable, and extensible code using the full power of Python's function and class mechanics — whether you're a beginner or preparing for production systems.

01

Functions

defreturn
02

Parameters

*args**kwargs
03

Methods

self@classmethod
04

Classes

__init__attributes
05

Inheritance

super()polymorphism
06

Advanced OOP

propertydataclass
07

Production Patterns

type hintsfactory

Why Functions, Methods & Classes Matter

What problems do they solve?

Functions eliminate code duplication, make programs modular, and improve readability. Classes group related data and behavior, allowing you to model complex systems naturally. Methods encapsulate operations that belong to specific objects.

Why are they required?

Without functions, even small scripts become unmaintainable spaghetti code. Without classes, managing complex state (like a user account or a database connection) becomes error‑prone and repetitive.

Role in Production

Well‑designed classes and functions lead to testable, scalable, and maintainable codebases. Factories, singletons, dependency injection, and immutable dataclasses are used daily in production to reduce bugs and improve performance.

1 Functions – The Fundamental Building Block

Define a function with def, optionally return a value. Use docstrings to document behavior. Functions are first‑class objects: you can pass them as arguments, store them in lists, and return them from other functions.

function_basics.py
def greet(name: str) -> str:
    """Return a friendly greeting."""
    return f"Hello, {name}!"

# First‑class function example
def apply(func, value):
    return func(value)

print(apply(greet, "World"))  # Hello, World!
🔍 Key Insight: Always use return to give back a value. If no return, function returns None. For reusable code, keep functions pure (no side effects) when possible.

2 Parameters – Positional, Keyword, *args, **kwargs

Python offers a rich parameter system. Use positional‑only (/), keyword‑only (*), default values, and variable‑length arguments.

parameter_examples.py
def config(host, port=8080, *, timeout=10):
    """host is positional, port has default, timeout is keyword‑only."""
    print(f"{host}:{port}, timeout={timeout}")

# Valid calls
config("localhost")
config("localhost", 3000, timeout=20)
config("localhost", timeout=5)  # port defaults to 8080

# Variable arguments
def log_all(*args, **kwargs):
    for a in args: print(a)
    for k,v in kwargs.items(): print(f"{k}={v}")

log_all("error", "warning", user="admin", code=500)
⚠️ Mutable default trap: Never use a mutable object (list, dict) as a default value. Use None and initialize inside the function.

3 Methods – Instance, Class, Static & Dunder

Methods are functions defined inside a class. The first parameter is usually self (instance). Use @classmethod for methods that need the class (first parameter is cls). @staticmethod for utility functions that don't need instance/class data.

method_types.py
class Product:
    discount = 0.1  # class attribute

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def final_price(self):  # instance method
        return self.price * (1 - Product.discount)

    @classmethod
    def update_discount(cls, new_discount):
        cls.discount = new_discount

    @staticmethod
    def is_expensive(price):
        return price > 1000

Dunder (double underscore) methods like __str__, __repr__, __eq__ let you control how objects behave with built‑ins.

4 Classes – Blueprints for Objects

A class defines attributes (data) and methods (behavior). Use __init__ to initialize instance attributes. Class attributes are shared across all instances.

class_basics.py
class BankAccount:
    bank_name = "National Bank"  # class attribute

    def __init__(self, owner, balance=0):
        self.owner = owner          # instance attribute
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        return self.balance

acc1 = BankAccount("Alice", 100)
acc2 = BankAccount("Bob")
print(acc1.bank_name)  # National Bank
acc1.deposit(50)       # 150

5 Inheritance & Polymorphism

Create a new class (subclass) from an existing one (superclass). Use super() to call the parent's methods. Override methods to provide specialized behavior.

inheritance.py
class Animal:
    def speak(self):
        return "Some sound"

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

def animal_sound(animal: Animal):
    print(animal.speak())      # polymorphism

animal_sound(Dog())  # Woof!
animal_sound(Animal())  # Some sound
💡 Use abstract base classes (ABCs) to enforce that subclasses implement certain methods. Import from abc import ABC, abstractmethod.

6 Advanced OOP – Properties, Dataclasses, Mixins

Properties allow getter/setter logic while using attribute syntax. Dataclasses auto‑generate __init__, __repr__, etc. Mixins add reusable functionality via multiple inheritance.

property_and_dataclass.py
from dataclasses import dataclass

class Thermometer:
    def __init__(self, celsius):
        self._celsius = celsius
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32

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

7 Production‑Grade Patterns

Use type hints for clarity, @dataclass for data containers, factory functions for complex object creation, and context managers for resource safety.

context_manager_class.py
class DatabaseConnection:
    def __init__(self, url):
        self.url = url
    def __enter__(self):
        print("Connecting...")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Closing connection")
        return False  # don't suppress exceptions

with DatabaseConnection("postgres://...") as conn:
    print("Running queries")

Solved Practice Problems

🔰 Easy (3 problems)

1. Greet Function with Default
def greet(name="World"):
    return f"Hello, {name}!"
print(greet("Alice"))  # Hello, Alice!
2. Rectangle Class
class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h
    def area(self):
        return self.w * self.h
3. Dog Class with Bark Method
class Dog:
    def bark(self):
        print("Woof!")
d = Dog()
d.bark()

⚡ Intermediate (3 problems)

4. Circle with Class Method for Diameter
class Circle:
    def __init__(self, radius):
        self.radius = radius
    @classmethod
    def from_diameter(cls, d):
        return cls(d/2)
5. BankAccount with Minimum Balance
class BankAccount:
    def __init__(self, balance, min_balance):
        self.balance = balance
        self.min_balance = min_balance
    def withdraw(self, amount):
        if self.balance - amount < self.min_balance:
            raise ValueError("Below minimum balance")
        self.balance -= amount
6. Temperature Converter Property
class Temperature:
    def __init__(self, celsius):
        self._c = celsius
    @property
    def fahrenheit(self):
        return self._c * 9/5 + 32

🚀 Advanced (3 problems)

7. Immutable Point Dataclass
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
    x: float
    y: float
8. Context Manager for File Writing
class FileWriter:
    def __init__(self, filename):
        self.filename = filename
    def __enter__(self):
        self.file = open(self.filename, 'w')
        return self.file
    def __exit__(self, *args):
        self.file.close()
9. Abstract Shape with Area Method
from abc import ABC, abstractmethod
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
class Square(Shape):
    def __init__(self, side):
        self.side = side
    def area(self):
        return self.side ** 2

Unsolved Practice Problems

🔰 Easy (3 problems)

1. Greet User Function

Write a function greet_user(name) that returns "Hello, {name}". If no name given, default to "Guest".

2. Calculator Class

Create a class Calculator with methods add(a,b), subtract(a,b). Don't use instance attributes.

3. Student Class

Define a Student class with attributes name and grades (list). Add a method average() that returns the average grade.

⚡ Intermediate (3 problems)

4. Class with @classmethod counter

Implement a Person class that counts how many instances have been created using a class variable and @classmethod.

5. Inheritance: Vehicle and Car

Create Vehicle with method move(). Then Car subclass that overrides move() to print "Driving".

6. Dataclass with Validation

Write a dataclass Email with fields address and subject. Validate that address contains '@' in __post_init__.

🚀 Advanced (3 problems)

7. Context Manager for Timer

Build a Timer context manager that prints the elapsed time when exiting the with block.

8. Mixin for Logging

Create a mixin class LoggingMixin that provides a log() method. Use it in a Database class.

9. Factory Function for Shapes

Write a function create_shape(shape_type, *args) that returns a Circle or Rectangle object based on input.