The Old Way: open() and close()
To interact with a file in Python, you first have to open() it. The open() function takes a file path and a mode as arguments. After you are done, you must explicitly close() the file to free up system resources.
Common File Modes:
- 'r': Read (default mode). Opens a file for reading, raises an error if the file does not exist.
- 'w': Write. Opens a file for writing. Creates the file if it does not exist, or truncates (empties) the file if it exists.
- 'a': Append. Opens a file for appending. Creates the file if it does not exist, and new data is written to the end of the file.
- 'r+': Read and Write.
Python
# This is the old, error-prone way.
file = open("my_log.txt", "w")
try:
    file.write("Log entry 1.\n")
    # What if an error happens here? The file might not be closed.
    # For example: result = 10 / 0
    file.write("Log entry 2.\n")
finally:
    # You MUST remember to close the file, even if errors occur.
    file.close()
The problem with this method is that if an error occurs before file.close() is called, the file might be left open, which can lead to resource leaks or data corruption. The try...finally block helps, but there's a much cleaner way.
The Modern Way: Context Managers (with statement)
A context manager, used with the with statement, provides a much safer and more elegant way to handle resources like files. It automatically handles the setup and teardown of the resource.
When you use with open(...), Python guarantees that the file will be closed automatically when the with block is exited, even if errors occur inside it.
Python
# The modern, preferred way
with open("my_log.txt", "w") as file:
    file.write("Log entry 1: This is much safer.\n")
    file.write("Log entry 2: The file will close automatically.\n")
# Once this block is exited, my_log.txt is guaranteed to be closed.
# Reading from a file
try:
    with open("my_log.txt", "r") as file:
        content = file.read() # read the entire file content
        print("--- File Content ---")
        print(content)
except FileNotFoundError:
    print("The log file doesn't exist yet!")
The with statement is the universally recommended way to work with files in Python. It's cleaner, less error-prone, and more readable.
Reading a File Line by Line
If a file is very large, reading the entire content into memory with .read() can be inefficient. A better approach is to iterate over the file object, which reads it line by line.
Python
with open("shopping_list.txt", "w") as f:
    f.write("Milk\n")
    f.write("Bread\n")
    f.write("Eggs\n")
print("--- Shopping List ---")
with open("shopping_list.txt", "r") as f:
    for line in f:
        # .strip() removes leading/trailing whitespace, including the newline character
        print(f"- {line.strip()}")