Post

12. File Handling in Python

๐Ÿ“„ Unlock the power of data management in Python! This comprehensive guide covers everything from basic file operations and context managers to efficiently handling CSV and JSON data. โœจ

12. File Handling in Python

What we will learn in this post?

  • ๐Ÿ‘‰ Introduction to File Handling
  • ๐Ÿ‘‰ Opening and Closing Files
  • ๐Ÿ‘‰ Reading Files
  • ๐Ÿ‘‰ Writing to Files
  • ๐Ÿ‘‰ Using 'with' Statement (Context Manager)
  • ๐Ÿ‘‰ File Paths and the os Module
  • ๐Ÿ‘‰ Working with CSV Files
  • ๐Ÿ‘‰ Working with JSON Files
  • ๐Ÿ‘‰ Conclusion!

๐Ÿ’พ Python File Handling: Your Dataโ€™s Best Friend!

File handling enables programs to save and load data to persistent storage, essential for applications like databases, log systems, and configuration management. Mastering file operations is crucial for building production-ready applications that store user data, process large datasets, and maintain application state across sessions.


๐ŸŒŸ Why Bother? Data Persistence is Key!

Think of data persistence as the ability to keep data forever (or until you delete it!). Without file handling, any data your program creates exists only while the program is active. Once it closes, poof! Itโ€™s gone. Files provide a way to store data on your computerโ€™s storage, making it available for future use.


๐Ÿ“ What Files Can Python Handle?

Python is super versatile and can work with many file types:

  • Text Files (e.g., .txt): Simple human-readable files, great for basic notes or configuration.
  • Binary Files: For non-text data like images (.jpg), audio, or compiled programs.
  • CSV Files (e.g., .csv): Stands for Comma Separated Values. Perfect for tabular data, just like a simple spreadsheet.
  • JSON Files (e.g., .json): (JavaScript Object Notation) โ€“ A popular format for structured data, widely used for web APIs.

โžก๏ธ Basic File Operation Flow

Understanding the file handling workflow helps you write robust data management code.

graph LR
    A["Start Program"]:::style1 --> B["Open File"]:::style2
    B --> C{"Read or Write?"}:::style3
    C -- "Read" --> D["Process Input"]:::style4
    C -- "Write" --> E["Process Output"]:::style5
    D --> F["Close File"]:::style2
    E --> F
    F --> G["End Program"]:::style1

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style2 fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style3 fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

File Fun with Pythonโ€™s open()! ๐Ÿ“‚

The open() function is the foundation of file operations in Python, used in applications ranging from log processing to configuration management.

open() Function: Your File Gateway ๐Ÿšช

Pythonโ€™s open() function is your go-to tool for interacting with files on your computer. It takes a filename (like "my_document.txt") and a mode (telling Python what you want to do), then returns a special file object.

1
2
# Example: Opening a file for reading
my_file = open("report.txt", "r")

Understanding File Modes: Your Fileโ€™s Intent ๐Ÿšฆ

File modes are like instructions for how Python should handle the file:

  • 'r' (read): The default mode. Opens the file for reading. The file must exist.
  • 'w' (write): Opens for writing. Creates a new file or overwrites an existing one entirely. Be careful!
  • 'a' (append): Opens for appending. Adds new content to the end of the file. Creates the file if it doesnโ€™t exist.
  • 'r+', 'w+', 'a+': These modes allow both reading and writing.
  • Add 'b' (binary) to any mode (e.g., 'rb', 'wb') for non-text files like images or executables.

The Mighty File Object: Your Interaction Hub ๐Ÿ’ก

When open() returns, you get a file object. This object is your direct connection to the file and has methods to help you work with it:

  • read(): To grab all content at once.
  • readline(): To read content line by line.
  • write("hello"): To put text into the file.
  • close(): Absolutely critical!

Donโ€™t Forget to close()! ๐Ÿค

Calling file_object.close() is super important after youโ€™re done! It:

  • Saves any unsaved changes from memory to disk.
  • Releases system resources, preventing file locking issues or memory leaks.
  • Prevents potential data corruption.
  • Pro Tip: Using with open(...) as file: is even better, as it automatically ensures the file is closed for you, even if errors occur!
  • For more info: Python Official Docs - File I/O
graph TD
    A["Start Operation"]:::style1 --> B["Call open()"]:::style2
    B -- "Creates" --> C["File Object"]:::style4
    C --> D["Perform Operations"]:::style2
    D -- "read(), write(), seek()" --> E["Process Content"]:::style5
    E --> F["Call close()"]:::style2
    F --> G["Release Resources"]:::style4
    G --> H["End Operation"]:::style1

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style2 fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

File Reading Fun! ๐Ÿ“–โœจ

Python provides multiple methods for reading files, each optimized for different use cases from loading configuration files to processing large log files.


read(): Gulp it All Down! ๐Ÿฅค

The read() method reads the entire file content and returns it as a single string. Itโ€™s ideal when your file is relatively small, and you need to process all its contents at once.

1
2
3
4
5
6
7
8
9
10
11
12
# Let's imagine 'my_file.txt' contains:
# Hello, world!
# This is line 2.
# And the final line.

with open("my_file.txt", "r") as file:
    whole_content = file.read()
    print(whole_content)
# Output:
# Hello, world!
# This is line 2.
# And the final line.

readline(): Line by Line Journey ๐Ÿšถโ€โ™€๏ธ

Wanna read just one line at a time? readline() is your go-to! Each call reads the next single line from the file. Itโ€™s super efficient for very large files, as it saves memory by not loading the entire file at once.

1
2
3
4
5
6
7
8
with open("my_file.txt", "r") as file:
    first_line = file.readline().strip() # .strip() removes the newline char
    second_line = file.readline().strip()
    print(f"First Line: {first_line}")
    print(f"Second Line: {second_line}")
# Output:
# First Line: Hello, world!
# Second Line: This is line 2.

readlines(): All Lines, in a List! ๐Ÿ“

This method reads all lines from the file and neatly stores them into a list of strings. Each string in the list represents a line from the file (including its newline character \n). Great for when you need to iterate or process each line individually later.

1
2
3
4
5
6
7
8
with open("my_file.txt", "r") as file:
    all_file_lines = file.readlines()
    for line in all_file_lines:
        print(f"-> {line.strip()}")
# Output:
# -> Hello, world!
# -> This is line 2.
# -> And the final line.

When to Choose? ๐Ÿค”๐Ÿ’ก

Selecting the right reading method depends on file size and processing requirements.

graph TD
    A["Start"]:::style1 --> B{"Need entire<br>file as string?"}:::style3
    B -- "Yes" --> C["Use read()"]:::style4
    B -- "No" --> D{"Need all<br>lines as list?"}:::style3
    D -- "Yes" --> E["Use readlines()"]:::style4
    D -- "No" --> F{"Process line<br>by line?"}:::style3
    F -- "Yes" --> G["Use readline()<br>in loop"]:::style4
    C --> H["End"]:::style1
    E --> H
    G --> H

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style3 fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Writing to Files ๐Ÿ“

Writing data to files is essential for saving application state, generating reports, and logging system events.

Basic Writing Operations

Use write() to add content to files. Remember to specify 'w' mode for writing (overwrites) or 'a' for appending.

1
2
3
4
5
6
7
8
# Writing to a file - overwrites existing content
with open("output.txt", "w") as f:
    f.write("Hello, World!\n")
    f.write("This is line 2.\n")

# Appending to a file - adds to end
with open("log.txt", "a") as f:
    f.write("New log entry: System started\n")

Real-World Example: Application Logging

1
2
3
4
5
6
7
8
9
10
11
12
13
import datetime

def log_event(event_type, message):
    """Log application events with timestamps"""
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_entry = f"[{timestamp}] {event_type}: {message}\n"
    
    with open("app.log", "a") as log_file:
        log_file.write(log_entry)

# Usage in production
log_event("INFO", "User logged in successfully")
log_event("ERROR", "Database connection failed")

โœจ The with Statement: Your File Safety Net! โœจ

The with statement provides automatic resource management, preventing file handle leaks and ensuring data integrity in production applications.

๐Ÿ”’ Automatic File Closing: No More Leaks!

When you use with open('my_file.txt', 'w') as f:, Python guarantees that the file will be automatically closed for you. This happens whether your code finishes successfully or if an error (exception) pops up inside the with block. You completely avoid needing to manually call f.close().

Itโ€™s the industry standard because it prevents serious issues like resource leaks (files staying open, consuming memory) and makes your code much safer, cleaner, and easier to read. Itโ€™s robust and less error-prone.

๐Ÿงฉ Context Managers Explained

The with statement works by interacting with objects known as context managers. A file object returned by open() is a perfect example! These objects have special methods (__enter__ and __exit__) that handle the โ€˜setupโ€™ (like opening the file) and โ€˜teardownโ€™ (like closing it) automatically when you enter and exit the with block.

1
2
3
4
5
6
7
8
9
10
# Example: Writing and Reading a file safely
with open('my_message.txt', 'w') as f:
    f.write("Python makes file handling easy!")
    # The file 'f' is active here
# File 'f' is automatically closed here, no matter what!

with open('my_message.txt', 'r') as f:
    message = f.read()
    print(f"Read: *{message}*")
# File 'f' is automatically closed here.

๐Ÿ“Š How with Works Visually

Understanding the context manager protocol helps you write cleaner, more reliable file handling code.

graph TD
    A["Enter with block"]:::style1 --> B["Call __enter__()"]:::style2
    B --> C["Resource Ready"]:::style4
    C --> D["Execute Code"]:::style2
    D --> E{"Finishes or<br>Error?"}:::style3
    E -- "Success" --> F["Call __exit__()"]:::style2
    E -- "Error" --> G["Call __exit__()<br>(handle error)"]:::style5
    F --> H["Cleanup Complete"]:::style4
    G --> H
    H --> I["Exit Block"]:::style1

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style2 fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style3 fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Real-World Example: Safe Configuration File Update

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def update_config(config_path, new_settings):
    """Safely update configuration file with error handling"""
    try:
        with open(config_path, 'r') as f:
            current_config = f.read()
        
        # Process and update configuration
        with open(config_path, 'w') as f:
            f.write(new_settings)
        
        print(f"โœ… Configuration updated successfully")
        return True
    except FileNotFoundError:
        print(f"โŒ Config file not found: {config_path}")
        return False
    except PermissionError:
        print(f"โŒ No permission to write: {config_path}")
        return False

# Usage
update_config("app_config.ini", "debug=true\nport=8080")

For more info:


๐Ÿ“‚ Pythonโ€™s File & Path Powerhouse

The os module provides cross-platform file system operations essential for building applications that work across Windows, macOS, and Linux environments.


๐Ÿ—บ๏ธ Smartly Handling File Paths

๐Ÿ”— Joining Paths Together

os.path.join() is fantastic for building paths. It smartly combines path components using the correct separator (/ or \) for your operating system.

1
2
3
4
5
6
7
8
9
10
11
import os

# Example: Joining path components
base_dir = "my_documents"
sub_dir = "reports"
file_name = "monthly_summary.txt"

full_path = os.path.join(base_dir, sub_dir, file_name)
print(f"Combined Path: {full_path}")
# Output (on Linux/macOS): Combined Path: my_documents/reports/monthly_summary.txt
# Output (on Windows): Combined Path: my_documents\reports\monthly_summary.txt

โœ‚๏ธ Splitting & Extracting Path Bits

You can easily break down a path into its components:

  • os.path.basename(): Gets the last part of a path (usually the filename or folder name).
  • os.path.dirname(): Gets the directory part of a path.
1
2
3
4
5
import os

sample_path = "/users/admin/data/config.ini"
print(f"Filename: {os.path.basename(sample_path)}")  # Output: Filename: config.ini
print(f"Directory: {os.path.dirname(sample_path)}")   # Output: Directory: /users/admin/data

โœ… Checking & Creating Things

๐Ÿ‘€ Does it Exist?

Use os.path.exists() to check if a file or directory is already there.

1
2
3
4
import os

# Check if a folder exists (replace 'non_existent_folder' with a real path to test)
print(f"Does 'my_data' exist? {os.path.exists('my_data')}") # Output: Does 'my_data' exist? False (if it doesn't)

๐Ÿก Making New Homes (Directories)

  • os.mkdir(): Creates a single new directory.
  • os.makedirs(): Creates all necessary parent directories if they donโ€™t exist, perfect for nested folders. Use exist_ok=True to prevent errors if the folder is already there.
1
2
3
4
5
6
7
8
import os

# Create a single folder
# os.mkdir("new_folder_example") # Use this for single, non-nested folder

# Create nested folders, handling existing ones gracefully
os.makedirs("my_projects/2024_plans/quarter1", exist_ok=True)
print("Created 'my_projects/2024_plans/quarter1'") # Output: Created 'my_projects/2024_plans/quarter1'

โœจ The Magic of Cross-Platform Compatibility

The true brilliance of os and os.path lies in their ability to abstract away operating system differences. You write your path logic once, and these modules automatically adapt to the specific conventions of the OS your code is running on. This makes your Python scripts highly portable!


๐Ÿ“ Path Management Flow

Common workflow for ensuring directories exist before file operations.

graph TD
    A["Start"]:::style1 --> B["Get Target Path"]:::style2
    B --> C{"Path Exists?<br>os.path.exists()"}:::style3
    C -- "No" --> D["Create Directory<br>os.makedirs()"]:::style5
    C -- "Yes" --> E["Directory Ready"]:::style4
    D --> E
    E --> F["Perform<br>File Operations"]:::style2
    F --> G["End"]:::style1

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style2 fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style3 fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Real-World Example: Project Directory Setup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import os

def setup_project_structure(project_name):
    """Create standard project directory structure"""
    base_path = os.path.join(os.getcwd(), project_name)
    
    # Define directory structure
    directories = [
        'src',
        'tests',
        'docs',
        os.path.join('src', 'utils'),
        os.path.join('src', 'models')
    ]
    
    # Create all directories
    for directory in directories:
        full_path = os.path.join(base_path, directory)
        os.makedirs(full_path, exist_ok=True)
        print(f"โœ… Created: {full_path}")
    
    # Create initial files
    readme_path = os.path.join(base_path, 'README.md')
    with open(readme_path, 'w') as f:
        f.write(f"# {project_name}\n\nProject documentation here.")
    
    print(f"๐ŸŽ‰ Project '{project_name}' structure created!")

# Usage
setup_project_structure("my_awesome_app")

๐Ÿ“š Dive Deeper!

Want to explore more functions? Check out the official Python documentation:

Pythonโ€™s csv Module: Your Data Pal! ๐Ÿค

The csv module simplifies handling tabular data, commonly used in data analysis, report generation, and database imports/exports.

Reading CSVs: csv.reader() & csv.DictReader() ๐Ÿ“š

When you need to bring CSV data into Python, you have great options:

  • csv.reader(): Reads a CSV file line by line, giving you each row as a list of strings. Simple and direct.
  • csv.DictReader(): Super useful! It reads each row as a dictionary. The column headers (from the first row) automatically become your dictionary keys, making data access by name very intuitive.

Writing CSVs: csv.writer() & csv.DictWriter() โœ๏ธ

To save your Python data into a CSV file:

  • csv.writer(): Writes data from lists into a CSV file. Each list you provide turns into a new row.
  • csv.DictWriter(): Ideal for writing data from dictionaries. You specify the fieldnames (column headers), and it handles matching dictionary keys to columns and writing the values. Donโ€™t forget writeheader()!

How it Works: A Quick Flow ๐Ÿš€

graph TD
    A["Start"]:::style1 --> B["CSV Operation"]:::style2
    B -- "Read" --> C{"Reader Type?"}:::style3
    C -- "List-based" --> D["csv.reader"]:::style4
    C -- "Dict-based" --> E["csv.DictReader"]:::style4
    D --> F["Row as List"]:::style5
    E --> G["Row as Dict"]:::style5
    F --> H["Process Data"]:::style2
    G --> H
    
    B -- "Write" --> I{"Writer Type?"}:::style3
    I -- "List-based" --> J["csv.writer"]:::style4
    I -- "Dict-based" --> K["csv.DictWriter"]:::style4
    J --> L["Write Lists"]:::style5
    K --> M["Write Dicts"]:::style5
    L --> N["CSV Updated"]:::style4
    M --> N
    H --> O["End"]:::style1
    N --> O

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style2 fill:#6b5bff,stroke:#4a3f6b,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style3 fill:#ffd700,stroke:#d99120,color:#222,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Practical Examples ๐Ÿ’ก

Reading CSV Files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import csv

# Using csv.reader() - simple list-based reading
with open('employees.csv', 'r') as file:
    reader = csv.reader(file)
    header = next(reader)  # Skip header
    for row in reader:
        print(f"Employee: {row[0]}, Department: {row[1]}")

# Using csv.DictReader() - dictionary-based reading
with open('employees.csv', 'r') as file:
    dict_reader = csv.DictReader(file)
    for row in dict_reader:
        print(f"Name: {row['Name']}, Salary: {row['Salary']}")

Writing CSV Files

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import csv

# Using csv.writer()
data = [
    ['Name', 'Age', 'City'],
    ['Alice', 30, 'New York'],
    ['Bob', 24, 'San Francisco']
]
with open('people.csv', 'w', newline='') as file:
    writer = csv.writer(file)
    writer.writerows(data)

# Using csv.DictWriter()
employees = [
    {'Name': 'Alice', 'Department': 'Engineering', 'Salary': 90000},
    {'Name': 'Bob', 'Department': 'Marketing', 'Salary': 75000}
]
fieldnames = ['Name', 'Department', 'Salary']
with open('employees.csv', 'w', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(employees)

Real-World Example: Sales Report Generator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import csv
from datetime import datetime

def generate_sales_report(sales_data, filename):
    """Generate CSV sales report with summary statistics"""
    fieldnames = ['Date', 'Product', 'Quantity', 'Price', 'Total']
    
    with open(filename, 'w', newline='') as file:
        writer = csv.DictWriter(file, fieldnames=fieldnames)
        writer.writeheader()
        
        total_revenue = 0
        for sale in sales_data:
            sale['Total'] = sale['Quantity'] * sale['Price']
            total_revenue += sale['Total']
            writer.writerow(sale)
        
        # Add summary row
        writer.writerow({
            'Date': '',
            'Product': 'TOTAL REVENUE',
            'Quantity': '',
            'Price': '',
            'Total': total_revenue
        })
    
    print(f"โœ… Report generated: {filename}")
    print(f"๐Ÿ’ฐ Total Revenue: ${total_revenue:,.2f}")

# Usage
sales = [
    {'Date': '2025-11-01', 'Product': 'Laptop', 'Quantity': 5, 'Price': 1200},
    {'Date': '2025-11-02', 'Product': 'Mouse', 'Quantity': 20, 'Price': 25}
]
generate_sales_report(sales, 'monthly_sales.csv')

Further Resources ๐Ÿ”—

Understanding Pythonโ€™s JSON Module ๐Ÿ“

JSON (JavaScript Object Notation) is the standard format for web APIs, configuration files, and data exchange between systems.


Working with JSON Strings: dumps() & loads() โ†”๏ธ

When you need to work with JSON data as plain text strings, dumps() and loads() are your friends.

  • json.dumps(): Dumps a Python object into a JSON string. This is great for sending data over networks or printing.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    import json
    
    python_data = {"name": "Alice", "age": 30, "is_student": False}
    json_output_string = json.dumps(python_data, indent=4) # 'indent' makes it pretty!
    print(json_output_string)
    # Output:
    # {
    #     "name": "Alice",
    #     "age": 30,
    #     "is_student": false
    # }
    
  • json.loads(): Loads a JSON string and transforms it back into a Python object. Perfect for receiving data.
    1
    2
    3
    4
    5
    
    json_input_string = '{"city": "New York", "population": 8000000}'
    python_object = json.loads(json_input_string)
    print(f"City: {python_object['city']}") # You can now access it like a dictionary!
    # Output:
    # City: New York
    

Handling JSON Files: dump() & load() ๐Ÿ’พ

For saving or reading JSON data directly from files, dump() and load() are the functions to use.

  • json.dump(): Dumps a Python object directly to a JSON file. This function needs both the object and an open file handle.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    data_for_file = {"product": "Laptop", "price": 1200, "in_stock": True}
    with open("product_info.json", "w") as f:
        json.dump(data_for_file, f, indent=4)
    # A file named 'product_info.json' is created with content:
    # {
    #     "product": "Laptop",
    #     "price": 1200,
    #     "in_stock": true
    # }
    
  • json.load(): Loads data from a JSON file and converts it into a Python object.
    1
    2
    3
    4
    5
    
    with open("product_info.json", "r") as f:
        loaded_file_data = json.load(f)
    print(f"Product price: ${loaded_file_data['price']}")
    # Output:
    # Product price: $1200
    

Python โ†”๏ธ JSON Data Flow ๐Ÿ”„

Understanding JSON serialization helps you build API clients and configuration systems.

graph LR
    A["Python Object<br>(dict/list)"]:::style1 -->|"dumps()<br>to string"| B["JSON String"]:::style4
    B -->|"loads()<br>from string"| A
    A -->|"dump()<br>to file"| C["JSON File"]:::style5
    C -->|"load()<br>from file"| A

    classDef style1 fill:#ff4f81,stroke:#c43e3e,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style4 fill:#00bfae,stroke:#005f99,color:#fff,font-size:16px,stroke-width:3px,rx:14;
    classDef style5 fill:#ff9800,stroke:#f57c00,color:#fff,font-size:16px,stroke-width:3px,rx:14;

    linkStyle default stroke:#e67e22,stroke-width:3px;

Real-World Example: API Response Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import json
import requests

def fetch_and_save_user_data(user_id, output_file):
    """Fetch user data from API and save to JSON file"""
    # Simulate API call (replace with actual API endpoint)
    api_url = f"https://api.example.com/users/{user_id}"
    
    try:
        # Make API request
        response = requests.get(api_url)
        response.raise_for_status()
        
        # Parse JSON response
        user_data = response.json()  # Equivalent to json.loads(response.text)
        
        # Add metadata
        user_data['fetched_at'] = datetime.now().isoformat()
        user_data['source'] = api_url
        
        # Save to file
        with open(output_file, 'w') as f:
            json.dump(user_data, f, indent=4)
        
        print(f"โœ… User data saved to {output_file}")
        return user_data
    
    except requests.RequestException as e:
        print(f"โŒ API Error: {e}")
        return None

# Usage
user_info = fetch_and_save_user_data(12345, 'user_12345.json')

Real-World Example: Configuration Manager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import json
import os

class ConfigManager:
    """Manage application configuration with JSON files"""
    
    def __init__(self, config_path='config.json'):
        self.config_path = config_path
        self.config = self.load_config()
    
    def load_config(self):
        """Load configuration from JSON file"""
        if os.path.exists(self.config_path):
            with open(self.config_path, 'r') as f:
                return json.load(f)
        return self.get_default_config()
    
    def save_config(self):
        """Save configuration to JSON file"""
        with open(self.config_path, 'w') as f:
            json.dump(self.config, f, indent=4)
        print(f"โœ… Configuration saved to {self.config_path}")
    
    def get_default_config(self):
        """Return default configuration"""
        return {
            'app_name': 'MyApp',
            'version': '1.0.0',
            'database': {
                'host': 'localhost',
                'port': 5432
            },
            'logging': {
                'level': 'INFO',
                'file': 'app.log'
            }
        }
    
    def update_setting(self, key_path, value):
        """Update nested configuration setting"""
        keys = key_path.split('.')
        config = self.config
        
        for key in keys[:-1]:
            config = config.setdefault(key, {})
        
        config[keys[-1]] = value
        self.save_config()

# Usage
config = ConfigManager()
config.update_setting('database.port', 5433)
print(f"Database port: {config.config['database']['port']}")

For more details, check out the Python Official Docs on the json module.

๐ŸŽฏ Conclusion

Youโ€™ve now mastered Python file handling from basic operations to advanced CSV and JSON processing! These skills are fundamental for building data-driven applications, logging systems, configuration management, and API integrations. Practice by creating a project that combines multiple file typesโ€”perhaps a data analysis tool that reads CSV files, processes them, and exports results to JSON. Remember to always use the with statement for automatic resource management and leverage the os module for cross-platform compatibility.

This post is licensed under CC BY 4.0 by the author.