Q1What does the second argument "r" in with open("data/notes.txt", "r") as f: mean?
File I/O — Safely Read and Write with with open()
Learn Python file I/O. with open() auto-close, read / readlines / readline differences, w / a modes, and the read-process-write pattern that saves to a separate file — hands-on.
Most of the data programs deal with lives on disk as files. Read a task list, append to a log, load a config — all are everyday operations.
In Python, with open() lets you do this in one safe syntactic unit. This article covers the basic form of with open(), the three reading methods (read / readlines / readline), writing (w) and appending (a), and handling paths with directories — by actually running code against files.
with open() Opens and Closes Files Safely
Opening a file is an operation that makes the OS hold a resource, and once opened, you must close it. Forgetting causes file-descriptor exhaustion, or write data getting stuck in buffers and never reaching disk.
open() makes the OS allocate a file descriptor (FD). Until close(), the FD stays held and writes stay buffered, unflushed. Forgetting to close is a serious resource leak.What is a file descriptor (FD)?
FD (file descriptor) is the integer ID the OS assigns to each currently-open file. Every open() hands out a new FD; close() returns it to the OS. The OS sets a per-process upper limit (Linux's default is around 1,024). Hit that limit and new open() calls start failing with OSError: Too many open files. The danger of forget-to-close is exactly this: holding a finite resource forever.
Write the form with open(path, mode) as f: and the file automatically closes the moment you leave the block, so the mistake is impossible to make. The f in as f is the variable that receives the file object; inside the with block, you read and write through f.
Paths use forward slashes / to separate directories. open("data/tasks.txt", "r") opens tasks.txt inside the data folder under the current directory. Even on Windows, Python convention uses / — internally, it's translated per OS.
with opens the file and binds it to as f. Leaving the block — normally or via exception — close() runs automatically.Reading — read / readlines / readline
To read a file, use r (read) for the second argument of open(path, "r"). The returned file object has three reading methods for different needs.
- f.read() — reads the whole file as one string.
- f.readlines() — reads it as a list of lines (strings).
- f.readline() — reads one line. Repeated calls advance to the next line; an empty string "" signals end of file.
# Get the whole file as one string
with open("data/tasks.txt", "r") as f:
content = f.read()
print(type(content)) # <class 'str'>
# Get a list of lines
with open("data/tasks.txt", "r") as f:
lines = f.readlines()
print(type(lines)) # <class 'list'>
# Get one line at a time (next call -> next line)
with open("data/tasks.txt", "r") as f:
first = f.readline() # "Write the report\n"
second = f.readline() # "Reply to emails\n"
From here on, the console runs in a browser-side virtual file system (VFS)
The console on the right is an in-browser virtual file system — files like data/tasks.txt are pre-staged for you. To run the same code on your local PC, create a data/ folder next to your Python file (e.g., main.py) and place tasks.txt inside it before running. The path syntax ("data/tasks.txt" with /) is identical to real Python.
When the file is one record per line (CSV without a header, a newline-separated list, log lines), f.readlines() is convenient. It returns a list of strings, one per line, and each element keeps its trailing \n.
Printing it directly produces double-spacing, so the standard pattern is to apply .rstrip("\n") to drop the trailing newline.
Big files? Read line by line — readline and walrus
f.read() and f.readlines() load the whole file into memory. That's fine for config-sized files, but a multi-GB server log can crash the program by running out of memory.
f.readline() loads only one line at a time, so memory usage stays at a single line's worth. When readline() reaches end of file, it returns the empty string "", which you use as the loop's exit condition.
"" to signal the loop to stop.# Classic style: break on empty string
with open("data/log.txt", "r") as f:
while True:
line = f.readline()
if not line: # empty string = EOF
break
print(line.rstrip())
The walrus operator := writes the same in one fewer line
Python 3.8 introduced the walrus operator (:=), which lets you assign and check inside an expression. Writing while line := f.readline(): means "put readline()'s result into line, and keep looping while it's non-empty" — the whole loop in one line.
Writing — w mode and a mode
Writing is just open() with a different second argument. Two modes carry the practical weight.
- "w" (write) — create new or overwrite. Existing contents are destroyed.
- "a" (append) — append to the end. Existing contents stay.
Two write methods exist too. f.write(s) writes the string s directly; f.writelines(lst) writes a list of strings in one go. If the path includes a folder, the file is created inside it (the folder must already exist).
# w mode: write strings one at a time
with open("data/done.txt", "w") as f:
f.write("Write the report\n")
f.write("Reply to emails\n")
# w mode: write a list at once with writelines
with open("data/done.txt", "w") as f:
f.writelines(["Write the report\n", "Reply to emails\n", "Attend the meeting\n"])
# a mode: append to the end
with open("data/done.txt", "a") as f:
f.write("Do the shopping\n")
Newlines \n are not added automatically
Unlike print(), neither f.write() nor f.writelines() adds newlines for you. Calling f.write("Hello") twice gives you HelloHello in the file. Insert explicit \n wherever you actually want a line break.
Going further — Analyze a file and save the result to another file
One of the most common patterns in real work is read an input file, do some analysis, and write the result to a separate file. Just use open() twice — once with "r", once with "w". The cleanest style is to put input → process → output into three separate with blocks.
# Analyze data/log.txt and save the result to data/log_summary.txt
# (1) Input: read the log into a list of lines
with open("data/log.txt", "r") as src:
lines = src.readlines()
# (2) Process: count lines, capture first/last entries
total = len(lines)
first = lines[0].rstrip()
last = lines[-1].rstrip()
summary = f"count: {total}\nfirst: {first}\nlast: {last}\n"
# (3) Output: write to a different file
with open("data/log_summary.txt", "w") as dst:
dst.write(summary)
print("Saved analysis to data/log_summary.txt")
encoding="utf-8" is the standard for text encoding
On real Python, you typically write open(path, "r", encoding="utf-8") to specify the encoding explicitly. Trying to read a Shift-JIS file as UTF-8 raises UnicodeDecodeError, and the reverse mojibakes silently. The world standard is UTF-8 — write new files in UTF-8 by default. The browser environment hard-codes UTF-8 internally so omitting encoding= works here, but make it a habit when writing local scripts.
Knowledge Check
Answer each question one by one.
Q2When you call f.readline() repeatedly and the file ends, what value comes back?
Q3If data/done.txt already has content and you open it with with open("data/done.txt", "w") as f:, what happens to the existing content?