Python

P
PythonHub
Progress 0%

Python

  • Home
  • History of Python
  • Applications of Python
  • Introduction To Python
    • What is Python
    • Character Set
    • Tokens in Python
    • Python Execution Mode
    • Variable And Identifiers
    • Data Types in Python
    • Operators And Expressions
    • Constants in Python
    • Assignment Statement
    • Input / Output in Python
    • Simple Python Scripts
    • Namespace in Python
    • πŸ“ Assignments
  • Operators in Python
    • Arithmetic Operators
    • Assignment Operators
    • Shorthand Assignment Operators
    • Relational Operators
    • Logical Operators
    • Bitwise Operators
    • Special Operators
    • πŸ“ Assignments
  • Input Output in Python
    • Accept Input
    • Output Formatting
    • πŸ“ Assignments
  • Conditional Statement
    • Decision Making
    • if Statement
    • IF-ELSE STATEMENT
    • IF-ELSE LADDER
    • NESTED IF-ELSE
    • Short Hand IF-ELSE
    • πŸ“ Assignments
  • Loops
    • Introduction to Loops
    • While Loop
    • Nested While Loop
    • πŸ“ While Loop Assignments
    • For Loop
    • For Loop Examples
    • Nested For Loop
    • Nested For Loop Examples
    • Infinite While Loops
    • Infinite For Loops
    • Break, Continue and Else in Loops
    • Difference Between For and While Loop
    • For Each Loop
    • πŸ“ For Each Assignments
    • πŸ“ All Loops Assignments
  • List
    • List in Python
    • Access List Elements
    • List Functions
    • Iterate (Loop) List
    • List Comprehension
    • πŸ“ Assignments
  • Tuple
    • Tuple in Python
    • Access Tuple Elements
    • Tuple Functions
    • Iterate (Loop) Tuple
    • Unpack Tuple
    • Tuple Comprehension
    • πŸ“ Assignments
  • Set
    • Set in Python
    • Access Set Elements
    • Set Methods
    • Iterate (Loop) Set
    • Pack/Unpack Set
    • Set Comprehension
    • πŸ“ Assignments
  • Dictionary
    • Dictionary
    • Access Dictionary Items
    • Dictionary Methods
    • Iterate (Loop) Dictionary
    • Formatting Dictionaries
    • Nested Dictionaries
    • Dictionary Comprehension
    • πŸ“ Assignments
  • Diff List Tuple Set Dictionary
    • List vs Tuple
    • List vs Set
    • List vs Dictionary
    • Tuple vs Set
    • Tuple vs Dictionary
    • Dictionary vs Set
    • πŸ“ Assignments
  • Exception
    • Error vs Exception
    • Types of Exception
    • Exception Handling
    • User Defined Exception
    • Logging Exception
    • πŸ“ Assignments
  • Functions
    • Introduction to Functions
    • Modular Programming
    • Types of Functions
    • Inbuilt Functions
    • Need For User-Defined Function
    • Elements of User Defined Function
    • Function Arguments
    • Nesting of Functions
    • Recursion
    • Global Local and Non Local
    • Python Lambda Functions
    • πŸ“ Assignments
  • Python Module
    • Introduction to Module
    • Inbuilt Modules in Python
    • User Defined Module
    • πŸ“ Assignments
  • File Handling
    • Introduction to Files
    • Create File
    • Read Files
    • Write to File
    • Rename File
    • Copy File
    • Move File
    • List Files in Directory
    • Binary Files
    • Zipping and Unzipping Files
    • πŸ“ Assignments
  • Strings
    • Basics of Strings
    • String Special Operators
    • String Formatting Operators
    • String Methods
    • πŸ“ Assignments
  • Regular Expressions
  • Python OOPS
    • Basics of Object Oriented
    • What are Classes and Objects?
    • Creating Class and Object
    • OOP vs Procedural Programming
    • Difference Between Classes and Objects
    • Constructors
    • Destructor
    • Built Class Methods and Attributes
    • Class and Instance Variables
    • Inheritance in Python
    • Single Inheritance
    • Multiple Inheritance
    • Multilevel Inheritance
    • Hierarchical Inheritance
    • Hybrid Inheritance
    • Abstraction
    • Method Overriding
    • Abstract Method
    • Interfaces in Python
    • Abstract Class vs Interface
    • Public, Private and Protected
    • Overloading vs Overriding
    • Inheritance vs Composition
    • Encapsulation
    • Polymorphism
    • Inner Classes
    • πŸ“ Assignments
  • Advanced Python
    • Iterator in Python
    • Generator in Python
    • Decorator in Python
    • Type Hints in Python
    • Async/Await Programming
    • Dataclasses in Python
    • Context Managers in Python
    • Property Decorator in Python
    • __slots__ in Python
    • Enums in Python
    • Walrus Operator in Python
    • Match-Case in Python
    • πŸ“ Assignments
  • Python Standard Library
    • Collections Module
    • Itertools Module
    • Functools Module
    • Datetime Module
    • JSON Module
    • OS Module
    • Sys Module
    • Random Module
    • Math Module
    • πŸ“ Assignments
  • Python Testing
    • Unit Testing in Python
    • Pytest Framework
    • Mocking in Python
    • πŸ“ Assignments
  • Python Best Practices
    • PEP 8 Style Guide
    • Docstrings in Python
    • Logging in Python
    • Code Optimization Tips
    • Debugging Techniques
    • πŸ“ Assignments
  • MySQL Database in Python
    • Introduction to MySQL with Python
    • DBMS vs File System
    • Connecting to MySQL Database
    • Create Database in MySQL
    • Create Table in MySQL
    • Insert Data in MySQL
    • Insert Multiple Rows
    • Select Data from MySQL
    • WHERE Clause in MySQL
    • Update Data in MySQL
    • Delete Data from MySQL
    • Parameterized Queries
    • Transaction Management
    • Error Handling
    • Connection Pooling
    • MySQL Drivers Guide
    • Joins in MySQL
    • Aggregation Functions
    • Backup MySQL Database
    • Best Practices
    • πŸ“ Assignments
  • MySQL Database Operations
    • SELECT Statement
    • MySQL Operators
    • DDL Statements
    • DML Statements
    • Subqueries
    • JOIN Operations
    • Aggregation
    • Case Study
    • πŸ“ Assignments
  • Graphics in Python
  • Threads in Python
    • Introduction to Threads
    • Process vs Threads
    • Concurrent Programming & GIL
    • Uses of Threads
    • Creating Threads
    • Single Tasking
    • Multi Tasking
    • Thread Synchronization
    • πŸ“ Assignments
  • Interview Questions & Answers
  • Python Case Studies
  • Multiple Choice Questions
  • πŸ“ Practice Problems
Get in Touch
  • tech2dsm@gmail.com

© Sankalan Data Tech

Python Language Interactive Tutorial

Python: Inner Classes

Python Inner Classes - Complete Guide

Learn how to use classes inside other classes.

Created by Sankalan Data Tech Team Verified
Data Engineers, Analysts, Scientists & Trainers
Created by experienced Python developers, data engineers, and data scientists to make programming easy through practical examples, real-world experience, and clear explanations.
On this page:
  • What are Inner Classes?
  • Why Use Inner Classes?
  • Types of Inner Classes
  • Accessing Outer Class Members
  • Real-World Example
  • Best Practices
  • Try It Yourself
  • Quick Quiz
  • Frequently Asked Questions
Share this tutorial:
Twitter LinkedIn Facebook WhatsApp Reddit Telegram Email Copy Link
What You'll Learn Here
  • What are inner classes β€” classes defined inside other classes
  • Why use them β€” grouping related classes, encapsulation
  • Types of inner classes β€” simple nested, static inner classes
  • Accessing outer class β€” how inner classes can access outer class members
  • Real-world use β€” practical examples you can use

What are Inner Classes?

An inner class (also called a nested class) is a class defined inside another class. It's a way to group related classes together and keep your code organized.

Think of it like a drawer inside a desk. The desk is the outer class, and the drawer is the inner class. The drawer belongs to the desk and has access to the desk's contents. But from outside, you access the drawer through the desk.

Inner classes are useful when:

  • You have a class that's only used by another class
  • You want to keep related classes close together
  • You want to encapsulate helper classes
  • You want to logically group classes that belong together

πŸ’‘ Key concept: Inner classes are defined within the scope of another class. They help organize code and can access the outer class's members.

Why Use Inner Classes?

1

The Benefits of Inner Classes

Inner classes aren't just a fancy feature β€” they have real benefits that can make your code cleaner and more maintainable.

# Why Use Inner Classes?

print("=" * 50)
print("WHY USE INNER CLASSES?")
print("=" * 50)

# ============================================================
# WITHOUT INNER CLASSES β€” Separate Top-Level Classes
# ============================================================

print("\n WITHOUT INNER CLASSES:")

class Node:
    """A node in a linked list"""
    def __init__(self, data):
        self.data = data
        self.next = None

class LinkedList:
    """A linked list"""
    def __init__(self):
        self.head = None
    
    def add(self, data):
        new_node = Node(data)
        new_node.next = self.head
        self.head = new_node
    
    def display(self):
        current = self.head
        while current:
            print(current.data, end=" -> ")
            current = current.next
        print("None")

# Node class is at the top level β€” anyone can use it
print("   Problem: Node class is globally accessible")
print("   Problem: Node class is separated from LinkedList")
print("   Problem: Namespace pollution")


# ============================================================
# WITH INNER CLASSES β€” Grouped Together
# ============================================================

print("\n WITH INNER CLASSES:")

class LinkedListWithInner:
    """A linked list with an inner Node class"""
    
    class Node:
        """Inner class β€” only used by LinkedList"""
        def __init__(self, data):
            self.data = data
            self.next = None
    
    def __init__(self):
        self.head = None
    
    def add(self, data):
        new_node = self.Node(data)  # Use the inner class
        new_node.next = self.head
        self.head = new_node
    
    def display(self):
        current = self.head
        while current:
            print(current.data, end=" -> ")
            current = current.next
        print("None")

# Node is now INSIDE LinkedList β€” organized
linked_list = LinkedListWithInner()
linked_list.add(3)
linked_list.add(2)
linked_list.add(1)
linked_list.display()

print("\n Benefits of Inner Classes:")
print("  1. Organization: Node belongs INSIDE LinkedList")
print("  2. Encapsulation: Node is hidden from external use")
print("  3. Readability: Related code is grouped together")
print("  4. Namespace: No global pollution (Node is not at top level)")


# ============================================================
# BENEFITS SUMMARIZED
# ============================================================

print("\n" + "-" * 30)
print("BENEFITS OF INNER CLASSES")
print("-" * 30)
print("""
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ BENEFIT             β”‚ EXPLANATION                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Organization        β”‚ Group related classes together            β”‚
β”‚ Encapsulation       β”‚ Hide implementation details               β”‚
β”‚ Code readability    β”‚ Clearer structure and intent              β”‚
β”‚ Namespace           β”‚ Avoid global namespace pollution          β”‚
β”‚ Logical grouping    β”‚ Classes that belong together stay togetherβ”‚
β”‚ Access to outer     β”‚ Inner classes can access outer members    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
""")

Benefits of inner classes:

  • Organization β€” keep related classes together
  • Encapsulation β€” hide helper classes from external use
  • Readability β€” clearer code structure
  • Namespace β€” avoid global pollution
  • Access β€” inner classes can access outer class members

Quick Check: What's the main benefit of using an inner class? (Answer: It groups related classes together and hides implementation details)

Types of Inner Classes

2

Simple Inner Classes and Static Inner Classes

In Python, there are two main types of inner classes:

  • Simple Inner Class β€” defined inside an outer class, can access outer class instance members
  • Static Inner Class β€” defined inside an outer class but doesn't need access to outer instance

The main difference is whether the inner class needs access to the outer class's instance data.

# Types of Inner Classes

print("=" * 50)
print("TYPES OF INNER CLASSES")
print("=" * 50)

# ============================================================
# TYPE 1: SIMPLE INNER CLASS
# ============================================================

print("\n1. SIMPLE INNER CLASS")

class University:
    """Outer class β€” university"""
    
    def __init__(self, name):
        self.name = name
        self._departments = []
    
    def add_department(self, dept_name):
        """Add a department"""
        dept = self.Department(dept_name, self)
        self._departments.append(dept)
        return dept
    
    def get_departments(self):
        return [dept.name for dept in self._departments]
    
    class Department:
        """Inner class β€” department belongs to university"""
        
        def __init__(self, name, university):
            self.name = name
            self._university = university  # Reference to outer
            self._courses = []
        
        def add_course(self, course_name):
            self._courses.append(course_name)
        
        def get_courses(self):
            return self._courses
        
        def get_university_name(self):
            """Access outer class through reference"""
            return self._university.name

# Using simple inner class
uni = University("MIT")
dept = uni.add_department("Computer Science")
dept.add_course("Python Programming")
dept.add_course("Data Structures")

print(f"   University: {uni.name}")
print(f"   Departments: {uni.get_departments()}")
print(f"   Courses in CS: {dept.get_courses()}")
print(f"   From inner class: {dept.get_university_name()}")


# ============================================================
# TYPE 2: STATIC INNER CLASS (doesn't need outer instance)
# ============================================================

print("\n2. STATIC INNER CLASS")

class Geometry:
    """Outer class β€” geometry utilities"""
    
    # Static inner class β€” doesn't need outer instance
    class Point:
        """A 2D point β€” independent of Geometry instance"""
        
        def __init__(self, x, y):
            self.x = x
            self.y = y
        
        def distance(self, other):
            import math
            return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
        
        def __repr__(self):
            return f"Point({self.x}, {self.y})"
    
    class Rectangle:
        """A rectangle β€” independent of Geometry instance"""
        
        def __init__(self, x, y, width, height):
            self.x = x
            self.y = y
            self.width = width
            self.height = height
        
        def area(self):
            return self.width * self.height
        
        def perimeter(self):
            return 2 * (self.width + self.height)

# Using static inner classes β€” no outer instance needed
p1 = Geometry.Point(0, 0)
p2 = Geometry.Point(3, 4)
rect = Geometry.Rectangle(0, 0, 5, 3)

print(f"   Point 1: {p1}")
print(f"   Point 2: {p2}")
print(f"   Distance: {p1.distance(p2):.2f}")
print(f"   Rectangle area: {rect.area()}")
print(f"   Rectangle perimeter: {rect.perimeter()}")


# ============================================================
# COMPARISON
# ============================================================

print("\n" + "-" * 30)
print("COMPARISON")
print("-" * 30)
print("""
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ FEATURE              β”‚ SIMPLE INNER      β”‚ STATIC INNER                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Needs outer instance β”‚ Usually yes       β”‚ No                           β”‚
β”‚ Access to outer      β”‚ Through reference β”‚ No direct access             β”‚
β”‚ Creation             β”‚ Via outer method  β”‚ Directly instantiated        β”‚
β”‚ Use case             β”‚ Helper tied to    β”‚ Utility that groups          β”‚
β”‚                      β”‚ outer instance    β”‚ related functionality        β”‚
β”‚ Example              β”‚ University.Department β”‚ Geometry.Point           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
""")

Types of inner classes:

  • Simple Inner Class β€” needs access to outer class instance, created through outer class
  • Static Inner Class β€” independent of outer instance, created directly
  • Simple inner β€” used when the inner class needs to know about its outer container
  • Static inner β€” used for grouping utilities that don't need outer context

Quick Check: What's the difference between a simple inner class and a static inner class? (Answer: A simple inner class needs a reference to the outer instance; a static inner class doesn't)

Accessing Outer Class Members

3

How Inner Classes Access the Outer Class

One of the key features of inner classes is that they can access the outer class's members. But there's a catch β€” they need a reference to the outer class instance.

Unlike languages like Java, Python doesn't automatically give inner classes access to the outer instance. You need to pass the outer instance to the inner class explicitly.

# Accessing Outer Class Members

print("=" * 50)
print("ACCESSING OUTER CLASS MEMBERS")
print("=" * 50)

class Car:
    """Outer class β€” car"""
    
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year
        self._mileage = 0
        self._engine = self.Engine()  # Inner class instance
    
    def drive(self, miles):
        self._mileage += miles
        return f"Drove {miles} miles. Total: {self._mileage}"
    
    def get_engine_status(self):
        return self._engine.get_status()
    
    def get_car_info(self):
        return f"{self.year} {self.make} {self.model}"
    
    class Engine:
        """Inner class β€” engine belongs to a car"""
        
        def __init__(self):
            self._is_running = False
            self._temperature = 20  # Celsius
        
        def start(self):
            self._is_running = True
            self._temperature = 40
            return "Engine started"
        
        def stop(self):
            self._is_running = False
            self._temperature = 20
            return "Engine stopped"
        
        def get_status(self):
            return f"Running: {self._is_running}, Temp: {self._temperature}Β°C"


# ============================================================
# EXAMPLE 2: INNER CLASS WITH OUTER REFERENCE
# ============================================================

print("\n1. SIMPLE INNER CLASS (Car.Engine)")

car = Car("Tesla", "Model 3", 2023)
print(f"   {car.get_car_info()}")
print(f"   {car.get_engine_status()}")
print(f"   {car._engine.start()}")
print(f"   {car.get_engine_status()}")
print(f"   {car.drive(50)}")

# The engine is only accessible through the car
# engine = Car.Engine()  # Would work but isn't connected to a car


# ============================================================
# EXAMPLE 3: INNER CLASS WITH OUTER REFERENCE (explicit)
# ============================================================

print("\n2. INNER CLASS WITH EXPLICIT OUTER REFERENCE")

class Employee:
    """Outer class β€” employee"""
    
    def __init__(self, name, department):
        self.name = name
        self.department = department
        self._projects = []
    
    def add_project(self, name, deadline):
        project = self.Project(name, deadline, self)
        self._projects.append(project)
        return project
    
    def get_projects(self):
        return [p.get_info() for p in self._projects]
    
    class Project:
        """Inner class β€” project belongs to an employee"""
        
        def __init__(self, name, deadline, employee):
            self.name = name
            self.deadline = deadline
            self._employee = employee  # Explicit reference to outer
            self.status = "pending"
        
        def complete(self):
            self.status = "completed"
            return f"Project '{self.name}' completed by {self._employee.name}"
        
        def get_info(self):
            return f"{self.name} (due: {self.deadline}) - {self.status}"

# Using the inner class
emp = Employee("Alice", "Engineering")
proj = emp.add_project("AI Model", "2024-12-31")

print(f"   Employee: {emp.name}, Dept: {emp.department}")
print(f"   Projects: {emp.get_projects()}")
print(f"   {proj.complete()}")
print(f"   Projects: {emp.get_projects()}")


# ============================================================
# ACCESS PATTERNS
# ============================================================

print("\n" + "-" * 30)
print("ACCESS PATTERNS")
print("-" * 30)

class Outer:
    class_variable = "I'm a class variable"
    
    def __init__(self, value):
        self.value = value
        self._inner = self.InnerWithRef(self)
    
    class InnerNoRef:
        """Inner class without outer reference"""
        def get_outer_value(self):
            # Can't access outer instance members
            return "No access to outer instance"
    
    class InnerWithRef:
        """Inner class with outer reference"""
        def __init__(self, outer):
            self._outer = outer
        
        def get_outer_value(self):
            return self._outer.value
        
        def get_outer_class_var(self):
            return Outer.class_variable

outer = Outer("hello")
no_ref = Outer.InnerNoRef()
with_ref = Outer.InnerWithRef(outer)

print(f"   InnerNoRef: {no_ref.get_outer_value()}")
print(f"   InnerWithRef (instance): {with_ref.get_outer_value()}")
print(f"   InnerWithRef (class var): {with_ref.get_outer_class_var()}")

Accessing outer class key points:

  • Explicit reference needed β€” inner class needs a reference to the outer instance
  • Pass outer instance β€” typically passed in the constructor
  • Access through reference β€” use self._outer.value to access outer data
  • Class variables β€” can be accessed directly through the outer class name

Quick Check: How does an inner class access an outer class's instance variables? (Answer: Through a reference to the outer instance passed to the inner class)

Real-World Example

4

Building a Document Editor

# Real-World Example: Document Editor

import uuid
from datetime import datetime

print("=" * 60)
print("DOCUMENT EDITOR β€” INNER CLASSES IN ACTION")
print("=" * 60)

class Document:
    """Document with inner classes for content and formatting"""
    
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.id = str(uuid.uuid4())[:8]
        self.created = datetime.now()
        self.modified = datetime.now()
        self._sections = []
        self._formatting = self.Formatting()
    
    def add_section(self, title):
        section = self.Section(title, self)
        self._sections.append(section)
        return section
    
    def add_paragraph(self, section_title, content):
        for section in self._sections:
            if section.title == section_title:
                return section.add_paragraph(content)
        return f"Section '{section_title}' not found"
    
    def get_sections(self):
        return [s.get_summary() for s in self._sections]
    
    def get_info(self):
        return {
            "id": self.id,
            "title": self.title,
            "author": self.author,
            "created": self.created.strftime("%Y-%m-%d %H:%M"),
            "modified": self.modified.strftime("%Y-%m-%d %H:%M"),
            "sections": len(self._sections)
        }
    
    class Section:
        """Inner class β€” section belongs to document"""
        
        def __init__(self, title, document):
            self.title = title
            self._document = document
            self._paragraphs = []
            self._formatting = document._formatting
        
        def add_paragraph(self, content, style="normal"):
            para = self.Paragraph(content, style, self)
            self._paragraphs.append(para)
            self._document.modified = datetime.now()
            return para
        
        def get_summary(self):
            return {
                "title": self.title,
                "paragraphs": len(self._paragraphs),
                "styles": [p.style for p in self._paragraphs]
            }
        
        class Paragraph:
            """Inner-inner class β€” paragraph belongs to section"""
            
            def __init__(self, content, style, section):
                self.content = content
                self.style = style
                self._section = section
                self._document = section._document
                self.id = str(uuid.uuid4())[:6]
            
            def get_text(self):
                if self.style == "bold":
                    return f"**{self.content}**"
                elif self.style == "italic":
                    return f"*{self.content}*"
                elif self.style == "heading":
                    return f"\n=== {self.content} ==="
                else:
                    return self.content
            
            def change_style(self, new_style):
                self.style = new_style
                self._document.modified = datetime.now()
                return f"Style changed to {new_style}"
    
    class Formatting:
        """Inner class β€” formatting settings for document"""
        
        def __init__(self):
            self.font = "Arial"
            self.font_size = 12
            self.line_spacing = 1.5
            self.margins = {"top": 1, "bottom": 1, "left": 1, "right": 1}
        
        def set_font(self, font):
            self.font = font
        
        def get_formatting(self):
            return {
                "font": self.font,
                "font_size": self.font_size,
                "line_spacing": self.line_spacing,
                "margins": self.margins
            }

# ============================================================
# DEMONSTRATION
# ============================================================

print("\n1. CREATING A DOCUMENT")
doc = Document("Python Tutorial", "Alice")

print(f"   Document: {doc.title} by {doc.author}")
print(f"   ID: {doc.id}")

print("\n2. ADDING SECTIONS")
section1 = doc.add_section("Introduction")
section2 = doc.add_section("Examples")

print(f"   Sections: {doc.get_sections()}")

print("\n3. ADDING PARAGRAPHS")
p1 = section1.add_paragraph("Python is a powerful language.", "normal")
p2 = section1.add_paragraph("It's great for beginners.", "bold")
p3 = section2.add_paragraph("Here are some examples.", "heading")
p4 = section2.add_paragraph("Example 1: Variables", "italic")

print(f"   Paragraph 1: {p1.get_text()}")
print(f"   Paragraph 2: {p2.get_text()}")
print(f"   Paragraph 3: {p3.get_text()}")
print(f"   Paragraph 4: {p4.get_text()}")

print("\n4. CHANGING STYLES")
print(f"   {p4.change_style('bold')}")
print(f"   Updated: {p4.get_text()}")

print("\n5. DOCUMENT INFO")
info = doc.get_info()
print(f"   {info}")

print("\n6. FORMATTING SETTINGS")
formatting = doc._formatting.get_formatting()
print(f"   {formatting}")

print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("Inner classes organize related code")
print(" Document β†’ Section β†’ Paragraph hierarchy is clear")
print(" Inner classes can access outer members via references")
print(" Encapsulation: implementation details are hidden")
print(" This makes the code more maintainable and readable")

Real-world example key points:

  • Hierarchical structure β€” Document β†’ Section β†’ Paragraph
  • Inner classes β€” Section and Paragraph are defined inside Document
  • Access through references β€” each inner class has a reference to its parent
  • Encapsulation β€” formatting, sections, and paragraphs are hidden
  • Organization β€” related code is grouped together

Quick Check: What's the advantage of using inner classes in the document editor? (Answer: It creates a clear hierarchy and keeps related classes organized together)

Best Practices

5

Using Inner Classes Effectively

# Best Practices for Inner Classes

print("=" * 60)
print("BEST PRACTICES FOR INNER CLASSES")
print("=" * 60)

# ============================================================
# 1. USE INNER CLASSES FOR LOGICAL GROUPING
# ============================================================

print("\n1. USE INNER CLASSES FOR LOGICAL GROUPING")

#  DO: Use inner classes when the class is only used by the outer class
class Database:
    class Connection:
        def __init__(self):
            self.is_connected = False
        
        def connect(self):
            self.is_connected = True
    
    def get_connection(self):
        return self.Connection()

#  DON'T: Use inner classes for classes that are used widely
# class Utility:
#     class MathHelper:
#         # This should be a top-level class
#         pass


# ============================================================
# 2. USE STATIC INNER CLASSES WHEN APPROPRIATE
# ============================================================

print("\n2. USE STATIC INNER CLASSES WHEN APPROPRIATE")

class HTTP:
    """HTTP utilities with static inner classes"""
    
    class Request:
        def __init__(self, method, url):
            self.method = method
            self.url = url
        
        def send(self):
            return f"{self.method} {self.url}"
    
    class Response:
        def __init__(self, status, body):
            self.status = status
            self.body = body
        
        def get_info(self):
            return f"Status: {self.status}, Body: {self.body}"

#  DO: Use static inner for independent utilities
request = HTTP.Request("GET", "https://api.example.com")
response = HTTP.Response(200, "Success")
print(f"   Request: {request.send()}")
print(f"   Response: {response.get_info()}")


# ============================================================
# 3. PASS THE OUTER REFERENCE EXPLICITLY
# ============================================================

print("\n3. PASS THE OUTER REFERENCE EXPLICITLY")

class Order:
    def __init__(self, customer):
        self.customer = customer
        self._items = []
    
    def add_item(self, product, quantity):
        item = self.OrderItem(product, quantity, self)
        self._items.append(item)
        return item
    
    class OrderItem:
        def __init__(self, product, quantity, order):
            self.product = product
            self.quantity = quantity
            self._order = order  # Explicit reference
        
        def get_total(self, price):
            return self.quantity * price
        
        def get_order_customer(self):
            return self._order.customer

#  DO: Pass the outer reference explicitly
order = Order("Alice")
item = order.add_item("Laptop", 2)
print(f"   Customer: {item.get_order_customer()}")


# ============================================================
# 4. DON'T OVERUSE INNER CLASSES
# ============================================================

print("\n4. DON'T OVERUSE INNER CLASSES")

#  DON'T: Use inner classes for everything
class Everything:
    class A:
        pass
    class B:
        pass
    class C:
        pass
    # This makes the code hard to read and maintain

#  DO: Use inner classes only when they truly belong together
class PaymentProcessor:
    class Payment:
        pass
    class Refund:
        pass
    # These make sense together


# ============================================================
# 5. KEEP NAMING CLEAR
# ============================================================

print("\n5. KEEP NAMING CLEAR")

#  DO: Use clear, descriptive names
class Library:
    class Book:
        pass
    
    class Member:
        pass

#  DON'T: Use vague names
class Data:
    class Inner:
        pass
    class Another:
        pass


# ============================================================
# 6. SUMMARY
# ============================================================

print("\n" + "=" * 60)
print("INNER CLASSES BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ PRACTICE                    β”‚ WHY IT MATTERS                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Use for logical grouping    β”‚ Keep related code together                 β”‚
β”‚                             β”‚                                             β”‚
β”‚ Use static inner for        β”‚ Avoid unnecessary coupling                 β”‚
β”‚ independent utilities       β”‚                                             β”‚
β”‚                             β”‚                                             β”‚
β”‚ Pass outer reference        β”‚ Inner class needs access to outer          β”‚
β”‚ explicitly                  β”‚                                             β”‚
β”‚                             β”‚                                             β”‚
β”‚ Don't overuse               β”‚ Too many inner classes hurt readability    β”‚
β”‚                             β”‚                                             β”‚
β”‚ Keep naming clear           β”‚ Make the relationship obvious              β”‚
β”‚                             β”‚                                             β”‚
β”‚ Use for encapsulation       β”‚ Hide implementation details                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

 REMEMBER:
   β€’ Inner classes help organize code
   β€’ Use them when the class belongs INSIDE another class
   β€’ Don't use them just for the sake of using them
   β€’ Clear naming makes the relationship obvious
""")

Best practices summary:

  • Use for logical grouping β€” when the class is only used by the outer class
  • Use static inner when appropriate β€” for utilities that don't need outer instance
  • Pass outer reference explicitly β€” don't rely on magic
  • Don't overuse β€” too many inner classes hurt readability
  • Keep naming clear β€” make the relationship obvious
  • Use for encapsulation β€” hide implementation details

Quick Check: When should you use an inner class? (Answer: When the class is only used by the outer class and logically belongs inside it)

Try It Yourself

Experiment with inner classes in the editor below.

Loading Pyodide... 0%
Python Code Editor
==================================================
INNER CLASSES - PRACTICE
==================================================

1. SIMPLE INNER CLASS
Book: Python Programming by John Smith
Chapters: ['Chapter Introduction (starts on page 1)', 'Chapter Variables (starts on page 25)']
From inner: Python Programming

2. STATIC INNER CLASS
Add: 8
Multiply: 15
25Β°C = 77.0Β°F

3. NESTED INNER CLASSES
Company: TechCorp
Department: Engineering
Employee: Alice (Developer)
From inner: TechCorp - Engineering
πŸ†

You've Got It!

You now understand inner classes in Python. You know when to use them, how to create them, and how to access outer class members through references.

Quick Quiz

Test what you've learned:

1. What is an inner class in Python?
2. What's the main benefit of using inner classes?
3. How does an inner class access the outer class's instance variables?
4. What's a static inner class?
5. When should you NOT use an inner class?

Frequently Asked Questions

What is an inner class in Python? β–Ό

An inner class (or nested class) is a class defined inside another class. It's used to group related classes together and keep the code organized. Inner classes can access the outer class's members through a reference.

How does an inner class access the outer class? β–Ό

Unlike Java, Python doesn't automatically give inner classes access to the outer instance. You need to pass a reference to the outer instance explicitly, typically through the inner class's constructor.

When should I use an inner class? β–Ό

Use an inner class when the class is only used by the outer class and logically belongs inside it. Examples include helper classes, iterators, and classes that represent parts of the outer object (like a Book.Chapter).

What's the difference between an inner class and a static inner class? β–Ό

A regular inner class is tied to an instance of the outer class and needs a reference to it. A static inner class (just a class defined inside another class) doesn't need an outer instance and can be instantiated directly. Static inner classes are used for grouping related utilities.

Can I have nested inner classes (inner classes inside inner classes)? β–Ό

Yes, you can nest inner classes multiple levels deep. For example, Company β†’ Department β†’ Employee. However, going too deep can make the code hard to read and maintain. Use nesting sparingly.

Should I always use inner classes for helpers? β–Ό

No. Only use inner classes when the helper is only used by the outer class and logically belongs inside it. If the helper is used by multiple classes, make it a top-level class. Overusing inner classes can make your code less maintainable.

Where to Go From Here

Now that you understand inner classes in Python, check out these related topics:

Encapsulation

Learn how inner classes help with encapsulation.

Learn More β†’

Classes and Objects

Review the basics of classes and objects in Python.

Learn More β†’

Inheritance

Learn how inheritance works with inner classes.

Learn More β†’
Interview Resources
  • Python Syntax & Variables Interview Questions
  • Top SQL Interview Questions & Answers
  • SQL Joins: Displaying Data from Multiple Tables FAQ
  • Python Lists and Dictionaries Interview Questions
  • Python OOP Interview Questions
  • SQL Set Operators Interview Questions
Previous: Polymorphism Next: OOPS Assignments β†’