9: Files#
What are files?#
From Python’s perspective, files are data that is outside of the program’s main memory. The PY4E textbook calls it “secondary memory”.
Secondary memory is essential, because main memory, which holds all the data you create while your Python program is running, goes away once the program stops.
Secondary memory is a place to have data that is persistent. Sort of like long-term memory in humans.
I like this picture from the PY4E textbook to illustrate the point:
The middle box is where the Python program “lives”.
Sometimes Python needs a way to connect to the outside world: “input and output devices” on the left, “network” (e.g., the internet!) on the right, and “secondary memory” (e.g., the hard drive! files!) on the right.
So far our programs have been self-contained (except for talking to the outside world via input() and print()).
Now we will talk about how to access and write to files in secondary memory so our data can persist beyond a single Python session/program, and also access data that is… more than we can just write into a single Python file or Jupyter cell.
The open() function, and the file handle object#
Python interacts with files using the file handle object. The open() function, as you might suspect, opens a file handle for a file.
Here is an example. What do you think is in fhand?
1fhand = open('assets/mbox-email-receipts.txt', 'r')
2print(fhand)
<_io.TextIOWrapper name='assets/mbox-email-receipts.txt' mode='r' encoding='UTF-8'>
The main parameters of .open() are:
Path: The path to the file you want to connect to
Mode: A specification of how you want to connect (to read, to write, etc.).
But its return value is not the contents of the file! Instead, its output is a file handle object: io.TextIOWrapper.
I like this picture from the PY4E textbook:
What kind of thing is it? What does it allow us to do?
Just like lists have methods like .append(), strings have methods like .upper() and .split(), and dictionaries have methods like .update() and .get(), file handle objects have key methods that enable us to work with the actual file:
read the contents of the file (with
.read()or.readlines())write to the file (with
.write()or.writelines())
So, in the example above, I’ve opened a file handle for the mbox-email-receipts.txt file, in the reading mode (r), which enables me to use .read() to read the contents of the file.
We’ll return to the concepts of mode and operations in a bit. First, we need to understand how to direct Python to a file so we can actually open a file handle to it!
File paths#
File paths are a way of giving Python directions to the file’s location.
In the example above, the file path was 'assets/mbox-email-receipts.txt'
There are two parts to a file path:
The filename itself.
The path/directions to the folder it’s in “from where you are”
The filename is obvious, but the path/directions part is not. So let’s take a closer look.
Path, aka directions to a folder#
Think of a file path like giving directions to a room in a building. To write a path, you need to know:
Where you are (your program’s current working directory)
Where the file is (the target folder)
How to get there (the path connecting them)
The building blocks of a path are:
Folder names — the names of directories you need to go through
/— a separator between folder names (and between the last folder and the filename). Think of it as “go into…”..— means “go up one level” (to the parent folder). You only need this when the file is outside of your current directory — for example, in a sibling folder or a parent folder.
For example, in our earlier path 'assets/mbox-email-receipts.txt':
assetsis the folder name/separates the folder from the filenamembox-email-receipts.txtis the file
This path works because the assets folder is inside our current working directory. We can verify this using the os library:
1import os # get all the data and functions from the os library
2
3cwd = os.getcwd() # show me where I am on the hard drive
4current_view = os.listdir() # list all the names of things I can immediately see in my current location
5
6print(f"You are currently in {cwd}\n")
7print(f"Here are all the things you can see:")
8for thingname in current_view:
9 print(thingname)
You are currently in /home/runner/work/inst126-intro-programming-notes/inst126-intro-programming-notes
Here are all the things you can see:
.DS_Store
Practice - Defining Functions.ipynb
6_Iteration.ipynb.bak
README.md
Practice_Module-2_Lists.ipynb
Practice - Module 2 Review.ipynb
myfile.txt
.ipynb_checkpoints
.github
2b_Variables.md
Practice_Module-1-Projects.ipynb
Problem-Formulation.md
assets
5_lists.md
9_Files.ipynb.bak
2a_Expressions.md
4_Conditionals.md
sample_files.zip
requirements.txt
10_Pandas-1.ipynb
sample_files_updated.zip
Practice_Module-2-Projects.md
Help-Seeking-Template.md
intro.md
_config.yml
laptop-weights-by-company.csv
Practice_Dictionaries_Scenarios.md
what-is-programming.md
Practice_Module-2-Projects.ipynb.bak
.jupyter
LICENSE
complex_dictionary.json
Practice_Strings_Integrative.md
Practice_Conditionals.ipynb
marvel-movies.csv
7_Strings.md
Practice_Debugging_examples-Solutions.ipynb
Practice_Warmup_Tracing.md
laptop-report.txt
.git
9_Files.md
11_Pandas-2.ipynb
CleanShot 2026-04-29 at 11.58.27.png
_toc.yml
4_Conditionals.qmd
Practice - Defining Functions (Errors).ipynb
.gitignore
dictionary.json
Practice_Debugging_examples.ipynb
module-4-review-scratchpad.ipynb
3_Functions.md
_static
ncaa-team-data.csv
6_Iteration.md
sample_files_updated
8_Dictionaries.ipynb.bak
exam_draft_module2.md
slides_7_Strings.html
8_Dictionaries.md
Practice_Indexing_FITB.md
notes.md
Debugging-Helpseeking.md
sample_files
7_Strings.ipynb.bak
INST courses.csv
_build
dictionary.txt
data
example-course-copier.ipynb
When would you need ..?#
Imagine your files are organized like this:
my_project/
├── code/
│ └── analysis.py ← your program is here
│ └── inputs/
└── rawdata.csv
├── outputs/
│ └── results.csv
└── README.md
If your program analysis.py is running inside the code/ folder, you can read rawdata.csv with the filepath "inputs/rawdata.csv" (which basically says “look for the inputs folder, then go inside and look for the file with the name rawdata.csv). But you can’t do that with the file results.csv, because it is not in a subfolder — it’s in a sibling folder called outputs/. To reach it, you need to first go up one level (from code/ to my_project/), then down into outputs/, and after you are in that folder, look for the file with the name results.csv:
# from inside code/, go up one level (..), then into outputs/
fpath = "../outputs/results.csv"
You can read .. as “go to the parent folder.” Here’s how the path breaks down:
..— go up fromcode/tomy_project//outputs— go into theoutputs/folder/results.csv— that’s the file
If you wanted to reach README.md (which is one level up, not in any subfolder):
# just go up one level
fpath = "../README.md"
You can even chain .. to go up multiple levels: ../../some_file.txt goes up two levels. But if you find yourself doing that, it’s often a sign that you should reorganize your files!
Relative vs. absolute file paths#
So far we have been discussing relative file paths: paths that describe how to locate a file relative to your program’s current working directory.
It is also possible to specify absolute file paths: the full location of a file from the root of your filesystem, like /Users/joel/Documents/data.csv on Mac or C:\Users\joel\Documents\data.csv on Windows.
Absolute paths are almost never a good idea for code you plan to share or submit. If you use an absolute path, your program will break on anyone else’s computer, because their filesystem will have different usernames, folder structures, and locations.
For this reason, in this class, we want you to practice writing relative file paths for all of your programs that deal with files.
A practical note: “where am I?” depends on your setup#
There’s one wrinkle with relative paths that trips up a lot of beginners: your current working directory depends on how you run your script, not where the script file lives.
For example, if your script analysis.py is in project1/code/, you might expect that open("notes.txt") opens a file in the same folder. And it will — if Python’s working directory is project1/code/. But some editors (like VS Code) run your script from the top-level folder you opened in the editor, not from the script’s own folder. So if you opened the school/ folder in VS Code, the working directory might be school/, and open("notes.txt") would look for notes.txt in school/ — not in project1/code/.
The simplification we use in this class#
To avoid this confusion, in our assignments we include a small “hotfix” at the top of your Python files:
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
This tells Python: “change the working directory to whatever folder this script is in.” With this line in place, relative paths like "notes.txt" and "Subfile/data.csv" always work as you’d expect — relative to the script itself.
You’ll see this in your PCE assignments with a DO NOT MODIFY comment. Just leave it there and write your paths relative to the script’s location. I’ll also include it in any practice files I share in class, and recommend that you put it in your own practice files as well while you’re getting comfortable with the idea of relative paths. To simplify things in VSCode for practice purposes, you can also enable the following setting in the Python extension (that lets you run Python files in VSCode):
Open Settings: Press Ctrl+, (Windows/Linux) or Cmd+, (macOS).
Search: Type python.terminal.executeInFileDir (or search for “Python Terminal”).
Enable: Check the box for “Python > Terminal: Execute In File Dir”. This ensures the terminal changes its directory to the file’s folder before executing your code.
What about “real” projects?#
In professional settings, programs are often run from the top-level project directory (the root of the repository). In that case, all paths are relative to the project root, not to any individual script.
For example, consider our example above again:
my_project/
├── code/
│ └── analysis.py ← your program is here
│ └── inputs/
└── rawdata.csv
├── outputs/
│ └── results.csv
└── README.md
a script in code/analysis.py might open a file in a sibling folder with the path outputs/results.csv (no .. operator!) and succeed if the program is run from the project root (my_project) where both code/ and outputs/ are visible.
The core intuition of relative paths is the same as in our program/script-centric opening examples — you’re still describing “how to get from some starting point to where the file is.” The only thing that changes is your starting point: the script’s folder (what we use in this class) vs. the project root (common in professional practice).
Running programs in this way (from the project root) is helpful especially when (like in our PCEs), functions within .py (or other files) are imported and used in other files, which may be in a different location. This is one reason why, for example, VSCode’s Python defaults to running scripts from the “currently open folder” (which often in practice will be the project root - i.e., the option Terminal: Execute in File Dir is disabled by default; when you get to professional practice, you’l’l probably want to go back to disabling this setting).
Working with files#
The second parameter of open() specifies the mode — what you intend to do with the file. Think of it as a permission system: you can only do operations that match the mode you opened with. Let’s look at each mode together with the operations it enables.
Reading files (mode 'r')#
To read a file, open it with 'r' (or leave the mode out entirely — 'r' is the default).
1path = "assets/"
2fname = "mbox-email-receipts.txt"
3fpath = f"{path}{fname}"
4
5# open in read mode
6fhand = open(fpath, 'r')
Once you have a file handle open for reading, there are two main ways to get the contents:
.read() reads the whole file as a single string:
1fhand = open(fpath, 'r')
2content_s = fhand.read()
3content_s
'From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008\nFrom louis@media.berkeley.edu Fri Jan 4 18:10:48 2008\nFrom zqian@umich.edu Fri Jan 4 16:10:39 2008\nFrom rjlowe@iupui.edu Fri Jan 4 15:46:24 2008\nFrom zqian@umich.edu Fri Jan 4 15:03:18 2008\nFrom rjlowe@iupui.edu Fri Jan 4 14:50:18 2008\nFrom cwen@iupui.edu Fri Jan 4 11:37:30 2008\nFrom cwen@iupui.edu Fri Jan 4 11:35:08 2008\nFrom gsilver@umich.edu Fri Jan 4 11:12:37 2008\nFrom gsilver@umich.edu Fri Jan 4 11:11:52 2008\nFrom zqian@umich.edu Fri Jan 4 11:11:03 2008\nFrom gsilver@umich.edu Fri Jan 4 11:10:22 2008\nFrom wagnermr@iupui.edu Fri Jan 4 10:38:42 2008\nFrom zqian@umich.edu Fri Jan 4 10:17:43 2008\nFrom antranig@caret.cam.ac.uk Fri Jan 4 10:04:14 2008\nFrom gopal.ramasammycook@gmail.com Fri Jan 4 09:05:31 2008\nFrom david.horwitz@uct.ac.za Fri Jan 4 07:02:32 2008\nFrom david.horwitz@uct.ac.za Fri Jan 4 06:08:27 2008\nFrom david.horwitz@uct.ac.za Fri Jan 4 04:49:08 2008\nFrom david.horwitz@uct.ac.za Fri Jan 4 04:33:44 2008\nFrom stephen.marquard@uct.ac.za Fri Jan 4 04:07:34 2008\nFrom louis@media.berkeley.edu Thu Jan 3 19:51:21 2008\nFrom louis@media.berkeley.edu Thu Jan 3 17:18:23 2008\nFrom ray@media.berkeley.edu Thu Jan 3 17:07:00 2008\nFrom cwen@iupui.edu Thu Jan 3 16:34:40 2008\nFrom cwen@iupui.edu Thu Jan 3 16:29:07 2008\nFrom cwen@iupui.edu Thu Jan 3 16:23:48 2008\n'
.readlines() reads the whole file as a list of strings (one per line):
1fhand = open(fpath, 'r')
2content_list = fhand.readlines()
3content_list
['From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008\n',
'From louis@media.berkeley.edu Fri Jan 4 18:10:48 2008\n',
'From zqian@umich.edu Fri Jan 4 16:10:39 2008\n',
'From rjlowe@iupui.edu Fri Jan 4 15:46:24 2008\n',
'From zqian@umich.edu Fri Jan 4 15:03:18 2008\n',
'From rjlowe@iupui.edu Fri Jan 4 14:50:18 2008\n',
'From cwen@iupui.edu Fri Jan 4 11:37:30 2008\n',
'From cwen@iupui.edu Fri Jan 4 11:35:08 2008\n',
'From gsilver@umich.edu Fri Jan 4 11:12:37 2008\n',
'From gsilver@umich.edu Fri Jan 4 11:11:52 2008\n',
'From zqian@umich.edu Fri Jan 4 11:11:03 2008\n',
'From gsilver@umich.edu Fri Jan 4 11:10:22 2008\n',
'From wagnermr@iupui.edu Fri Jan 4 10:38:42 2008\n',
'From zqian@umich.edu Fri Jan 4 10:17:43 2008\n',
'From antranig@caret.cam.ac.uk Fri Jan 4 10:04:14 2008\n',
'From gopal.ramasammycook@gmail.com Fri Jan 4 09:05:31 2008\n',
'From david.horwitz@uct.ac.za Fri Jan 4 07:02:32 2008\n',
'From david.horwitz@uct.ac.za Fri Jan 4 06:08:27 2008\n',
'From david.horwitz@uct.ac.za Fri Jan 4 04:49:08 2008\n',
'From david.horwitz@uct.ac.za Fri Jan 4 04:33:44 2008\n',
'From stephen.marquard@uct.ac.za Fri Jan 4 04:07:34 2008\n',
'From louis@media.berkeley.edu Thu Jan 3 19:51:21 2008\n',
'From louis@media.berkeley.edu Thu Jan 3 17:18:23 2008\n',
'From ray@media.berkeley.edu Thu Jan 3 17:07:00 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:34:40 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:29:07 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:23:48 2008\n']
In both cases, you end up with strings. You can then parse them to do what you want — for example, splitting the single string on "\n" to get individual lines:
1content_s.split("\n")
['From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008',
'From louis@media.berkeley.edu Fri Jan 4 18:10:48 2008',
'From zqian@umich.edu Fri Jan 4 16:10:39 2008',
'From rjlowe@iupui.edu Fri Jan 4 15:46:24 2008',
'From zqian@umich.edu Fri Jan 4 15:03:18 2008',
'From rjlowe@iupui.edu Fri Jan 4 14:50:18 2008',
'From cwen@iupui.edu Fri Jan 4 11:37:30 2008',
'From cwen@iupui.edu Fri Jan 4 11:35:08 2008',
'From gsilver@umich.edu Fri Jan 4 11:12:37 2008',
'From gsilver@umich.edu Fri Jan 4 11:11:52 2008',
'From zqian@umich.edu Fri Jan 4 11:11:03 2008',
'From gsilver@umich.edu Fri Jan 4 11:10:22 2008',
'From wagnermr@iupui.edu Fri Jan 4 10:38:42 2008',
'From zqian@umich.edu Fri Jan 4 10:17:43 2008',
'From antranig@caret.cam.ac.uk Fri Jan 4 10:04:14 2008',
'From gopal.ramasammycook@gmail.com Fri Jan 4 09:05:31 2008',
'From david.horwitz@uct.ac.za Fri Jan 4 07:02:32 2008',
'From david.horwitz@uct.ac.za Fri Jan 4 06:08:27 2008',
'From david.horwitz@uct.ac.za Fri Jan 4 04:49:08 2008',
'From david.horwitz@uct.ac.za Fri Jan 4 04:33:44 2008',
'From stephen.marquard@uct.ac.za Fri Jan 4 04:07:34 2008',
'From louis@media.berkeley.edu Thu Jan 3 19:51:21 2008',
'From louis@media.berkeley.edu Thu Jan 3 17:18:23 2008',
'From ray@media.berkeley.edu Thu Jan 3 17:07:00 2008',
'From cwen@iupui.edu Thu Jan 3 16:34:40 2008',
'From cwen@iupui.edu Thu Jan 3 16:29:07 2008',
'From cwen@iupui.edu Thu Jan 3 16:23:48 2008',
'']
Iterating through a file line by line#
You can also loop directly over a file handle, which gives you one line at a time — just like iterating over a list. This is handy when you want to process each line without loading the entire file into memory.
1thursday_records = []
2fhand = open(fpath, 'r')
3for line in fhand:
4 if 'Thu' in line:
5 thursday_records.append(line)
6
7thursday_records
['From louis@media.berkeley.edu Thu Jan 3 19:51:21 2008\n',
'From louis@media.berkeley.edu Thu Jan 3 17:18:23 2008\n',
'From ray@media.berkeley.edu Thu Jan 3 17:07:00 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:34:40 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:29:07 2008\n',
'From cwen@iupui.edu Thu Jan 3 16:23:48 2008\n']
Chaining open + read#
Since open() returns a file object, you can chain .read() or .readlines() directly onto it — no need for an intermediate variable:
1fstring = open("assets/newfile2.txt", "r").read()
2fstring
'hello world! \n\nThis is a file with a lot of stuff in it.\n\n'
This is the same chaining concept from strings: each expression produces a result, and you can immediately call a method on that result. open(...) produces a file object → .read() is called on it → returns a string.
In the next module we will learn how the pandas library connects to files to cover common parsing situations (e.g., I have a spreadsheet, I want to go straight into a dataframe for analysis). More on that later! The concepts of accessing files will still apply.
Writing files (mode 'w')#
To write to a file, open it with 'w'. This gives you access to the .write() method — think of it as similar to print(), except it writes to a file instead of the screen.
1path = "assets/"
2fname = "newfile.txt"
3fpath = f"{path}{fname}"
4
5fhand = open(fpath, 'w')
6fhand.write("Hello world, my programming friends!")
7fhand.close()
A few important things to know about 'w' mode:
It creates the file if it doesn’t exist. You don’t need to create the file beforehand — Python will make it for you.
1path = "assets/"
2fname = "newfile-from-class.txt"
3fpath = f"{path}{fname}"
4
5fhand = open(fpath, 'w')
6fhand.write("This is a new file from class!")
7fhand.close()
It overwrites the file if it already exists! Be careful — opening in 'w' mode erases the previous contents.
1path = "assets/"
2fname = "newfile.txt"
3fpath = f"{path}{fname}"
4
5fhand = open(fpath, 'w')
6fhand.write("Hello world from INST126!")
7fhand.close()
You can write multiple times before closing:
1path = "assets/"
2fname = "newfile5.txt"
3fpath = f"{path}{fname}"
4
5fhand = open(fpath, 'w')
6fhand.write("Hello INST126!")
7fhand.write("\n\nAnother line")
8fhand.close()
If you opened in read mode, Python won’t let you write — this is good for security!
1fhand = open("assets/newfile.txt", 'r')
2fhand.write("This will fail!")
3fhand.close()
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
Cell In[13], line 2
1 fhand = open("assets/newfile.txt", 'r')
----> 2 fhand.write("This will fail!")
3 fhand.close()
UnsupportedOperation: not writable
Closing files and the with pattern#
You may be told that you need to .close() a file to safely exit the connection. As best we can tell, this used to be very important, but in Python 3 we haven’t been able to find concrete, repeatable consequences of forgetting it in this course. However, it can matter in professional settings: https://realpython.com/why-close-file-python/
To avoid worrying about it, you can use the with pattern, which automatically closes the file when the block finishes:
1path = "assets/"
2fname = "newfile.txt"
3fpath = f"{path}{fname}"
4
5with open(fpath, 'w') as fhand:
6 fhand.write("Hello world! Something new")
7# file is automatically closed here
The with pattern works for reading too:
1with open("assets/newfile2.txt", 'r') as fhand:
2 content = fhand.read()
3print(content)
hello world!
This is a file with a lot of stuff in it.
Appending to files (mode 'a')#
Append mode is a variant of write mode: it lets you add content to the end of a file without erasing what’s already there.
1path = "assets/"
2fname = "newfile.txt"
3fpath = f"{path}{fname}"
4
5fhand = open(fpath, 'a')
6fhand.write("\nMore stuff from INST126!")
7fhand.close()
This is useful when you want to build up a file over time — like a log file.
Summary of modes#
Mode |
What it does |
Creates file? |
Overwrites? |
|---|---|---|---|
|
Read only (default) |
No — error if missing |
No |
|
Write (from scratch) |
Yes |
Yes! |
|
Append (add to end) |
Yes |
No |
There are more advanced modes (e.g., 'rb' for reading binary files), but 'r', 'w', and 'a' cover most of what you’ll need.
Practice: working with files#
Practice: Code Tracing with Files#
For each question, predict what happens when the code runs. Use this folder structure for all questions:
my_project/
├── app/
│ ├── main.py ← your program
│ └── config.txt (contains: "debug=True")
├── data/
│ ├── names.txt (contains: "Alice\nBob\nCharlie")
│ └── scores.csv (contains: "Alice,95\nBob,82\nCharlie,91")
└── readme.txt (contains: "Welcome to my project")
Trace 1#
fhand = open("config.txt", "r")
content = fhand.read()
print(content)
print(type(content))
A)
debug=Truethen<class 'str'>B)
debug=Truethen<class 'list'>C)
['debug=True']then<class 'list'>D)
config.txtthen<class 'str'>
Answer:
A) debug=True then <class 'str'>
.read() returns the entire file contents as a single string. It doesn’t split it into lines or wrap it in a list.
Trace 2#
fhand = open("../data/names.txt", "r")
lines = fhand.readlines()
print(len(lines))
print(lines[0])
A)
1thenAlice\nBob\nCharlieB)
3thenAlice\nC)
3thenAliceD)
15thenA
Answer:
B) 3 then Alice\n
.readlines() returns a list of strings, one per line. The file has 3 lines, so len(lines) is 3. Each line includes the trailing \n newline character (except possibly the last line). So lines[0] is "Alice\n", not "Alice".
Note the path: ../data/names.txt goes up from app/ to my_project/, then into data/.
Trace 3#
fhand = open("config.txt", "w")
fhand.write("debug=False")
fhand.close()
fhand = open("config.txt", "r")
print(fhand.read())
A)
debug=True\ndebug=FalseB)
debug=TrueC)
debug=FalseD) Error: can’t open the same file twice
Answer:
C) debug=False
Opening in 'w' mode erases the previous contents and starts fresh. The original "debug=True" is gone. After writing and closing, re-opening in 'r' mode shows only the new content. If you wanted to keep the original and add to it, you’d use 'a' (append) mode.
Trace 4#
fhand = open("config.txt", "a")
fhand.write("\nverbose=True")
fhand.close()
fhand = open("config.txt", "r")
print(fhand.read())
A)
verbose=TrueB)
debug=TrueC)
debug=True\nverbose=TrueD) Error: can’t append and then read
Answer:
C) debug=True\nverbose=True
'a' mode appends to the end of the file without erasing existing content. So the original "debug=True" stays, and "\nverbose=True" is added after it. When printed, the \n creates a line break, so you’d see:
debug=True
verbose=True
Trace 5#
fhand = open("config.txt", "r")
fhand.write("new content")
A) The file now contains
"new content"B) The file is unchanged; the write is silently ignored
C)
UnsupportedOperationerrorD)
FileNotFoundError
Answer:
C) UnsupportedOperation error
The file was opened in read mode ('r'), so Python won’t let you write to it. This is a safety feature — the mode acts as a permission system. You’d need 'w' or 'a' to write.
Trace 6#
fhand = open("names.txt", "r")
content = fhand.read()
print(content)
A)
Alice\nBob\nCharlieB)
FileNotFoundErrorC) An empty string
""D)
None
Answer:
B) FileNotFoundError
names.txt is in the data/ folder, not in app/ where our program runs. The correct path would be "../data/names.txt". Python is very literal — if the path doesn’t exactly match where the file is, it crashes.
Trace 7#
fhand = open("../data/scores.csv", "r")
names = []
for line in fhand:
parts = line.strip().split(",")
names.append(parts[0])
print(names)
A)
['Alice,95', 'Bob,82', 'Charlie,91']B)
['Alice', 'Bob', 'Charlie']C)
['95', '82', '91']D)
[['Alice', '95'], ['Bob', '82'], ['Charlie', '91']]
Answer:
B) ['Alice', 'Bob', 'Charlie']
Each iteration gives one line. .strip() removes the trailing \n. .split(",") splits into ['Alice', '95'] etc. parts[0] grabs just the name. This is a common pattern for parsing CSV-like data line by line.
Trace 8#
fhand = open("config.txt", "w")
fhand.write("mode=test")
# forgot to close!
fhand2 = open("config.txt", "r")
content = fhand2.read()
print(len(content))
A)
9B)
0C)
9or0(unpredictable)D) Error: file is locked
Answer:
C) 9 or 0 (unpredictable)
When you write without closing (or using with), the data might still be in a buffer and not yet written to disk. Reading the file before the write is flushed could give you an empty file (0) or the full content (9), depending on timing and your OS. This is why closing files (or using with) matters!
Trace 9#
with open("../data/names.txt", "r") as f:
first_line = f.readline()
print(first_line.strip())
print(f.read())
A)
AlicethenBob\nCharlieB)
AlicethenValueError(file is closed)C)
Alice\nthenBob\nCharlieD)
Alicethen an empty string
Answer:
B) Alice then ValueError (file is closed)
The with block automatically closes the file when the block ends. first_line works fine (it was read inside the block). But f.read() on line 3 is outside the block — the file is already closed, so Python raises a ValueError: I/O operation on closed file.
Trace 10#
data = open("../data/scores.csv", "r").read().strip().split("\n")
print(len(data))
print(data[1])
A)
3thenBob,82B)
1thenAlice,95\nBob,82\nCharlie,91C)
3thenBobD) Error: too many operations chained
Answer:
A) 3 then Bob,82
This chains four operations: open() returns a file object → .read() returns the whole file as one string → .strip() removes trailing whitespace/newlines → .split("\n") splits into a list of lines. The result is ['Alice,95', 'Bob,82', 'Charlie,91'] — 3 items, and index [1] is 'Bob,82'.
Practice: file paths#
Sample files for all path exercises are in the sample_files/ folder if you want to test your answers!
Each set starts with easier problems (same folder, subfolders) and builds up to harder ones (going up with ..).
Each set starts with easier problems (same folder, subfolders) and builds up to harder ones (going up with ..).
Practice Set 1: School project#
school/
├── projects/
│ ├── project1/
│ │ ├── code/
│ │ │ ├── analysis.py ← your program (for P1-P4)
│ │ │ └── notes.txt
│ │ └── data/
│ │ └── survey.csv
│ └── project2/
│ ├── main.py ← your program (for P5-P6)
│ └── output/
│ └── results.csv
├── notes/
│ └── lecture1.txt
└── grades.csv
P1. Your program is analysis.py. Open notes.txt, which is in the same folder.
1# fpath = ???
Answer:
fpath = "notes.txt"
The file is right next to your program — just use the filename, no path needed.
P2. Your program is main.py. Open results.csv, which is in a subfolder called output/.
1# fpath = ???
Answer:
fpath = "output/results.csv"
Go into the output/ subfolder, then the file. No .. needed — you’re going down.
P3. Your program is analysis.py. Open survey.csv, which is in a sibling folder (data/).
1# fpath = ???
Answer:
fpath = "../data/survey.csv"
From code/, go up to project1/ (..), then into data/, then the file.
P4. Your program is analysis.py. Open grades.csv, which is several levels up.
1# fpath = ???
Answer:
fpath = "../../../grades.csv"
From code/, go up to project1/ (..), up to projects/ (../..), up to school/ (../../..), and there’s grades.csv.
P5. Your program is main.py. Open lecture1.txt (in the notes/ folder).
1# fpath = ???
Answer:
fpath = "../../notes/lecture1.txt"
From project2/, go up to projects/ (..), up to school/ (../..), then into notes/, then the file.
P6. Your program is running from the school/ folder. Open survey.csv, lecture1.txt, and grades.csv.
1# fpath_survey = ???
2# fpath_lecture = ???
3# fpath_grades = ???
Answer:
fpath_survey = "projects/project1/data/survey.csv"
fpath_lecture = "notes/lecture1.txt"
fpath_grades = "grades.csv"
From school/, everything is below you — no .. needed. grades.csv is right here, so it’s just the filename.
Practice Set 2: Web project#
webapp/
├── src/
│ ├── pages/
│ │ ├── home.py ← your program
│ │ └── styles.css
│ └── utils/
│ └── helpers.py
├── config/
│ └── settings.json
├── public/
│ ├── index.html
│ └── images/
│ └── logo.png
└── README.md
P7. Your program is home.py. Open styles.css, which is in the same folder.
1# fpath = ???
Answer:
fpath = "styles.css"
Same folder — just the filename.
P8. Your program is home.py. Open helpers.py (in the sibling utils/ folder).
1# fpath = ???
Answer:
fpath = "../utils/helpers.py"
From pages/, go up to src/ (..), then into utils/, then the file.
P9. Your program is home.py. Open settings.json (in the config/ folder).
1# fpath = ???
Answer:
fpath = "../../config/settings.json"
From pages/, go up to src/ (..), up to webapp/ (../..), then into config/, then the file.
P10. Your program is home.py. Open README.md (at the top of the project).
1# fpath = ???
Answer:
fpath = "../../README.md"
From pages/, go up to src/ (..), up to webapp/ (../..), and README.md is right there.
Practice Set 3: Research project#
research/
├── experiment1/
│ ├── raw_data/
│ │ └── responses.csv
│ └── scripts/
│ ├── analyze.py ← your program
│ └── log.txt
├── experiment2/
│ └── raw_data/
│ └── responses.csv
├── shared/
│ └── templates/
│ └── report_template.txt
└── participants.csv
P11. Your program is analyze.py. Open log.txt, which is in the same folder.
1# fpath = ???
Answer:
fpath = "log.txt"
Same folder — just the filename.
P12. Your program is analyze.py. Open experiment 1’s responses.csv (in the sibling raw_data/ folder).
1# fpath = ???
Answer:
fpath = "../raw_data/responses.csv"
From scripts/, go up to experiment1/ (..), then into raw_data/, then the file.
P13. Your program is analyze.py. Open experiment 2’s responses.csv.
1# fpath = ???
Answer:
fpath = "../../experiment2/raw_data/responses.csv"
From scripts/, go up to experiment1/ (..), up to research/ (../..), then into experiment2/, then raw_data/, then the file.
P14. Your program is analyze.py. Open participants.csv (at the top level).
1# fpath = ???
Answer:
fpath = "../../participants.csv"
From scripts/, go up to experiment1/ (..), up to research/ (../..), and participants.csv is right there.
Practice Set 4: Music app#
music_app/
├── library/
│ ├── rock/
│ │ └── tracks.csv
│ └── jazz/
│ └── tracks.csv
├── playlists/
│ └── my_playlists.csv
├── tools/
│ ├── organizer.py ← your program (for P15-P18)
│ ├── settings.txt
│ └── output/
│ └── report.csv
└── config.json
P15. Your program is organizer.py. Open settings.txt, which is in the same folder.
1# fpath = ???
Answer:
fpath = "settings.txt"
Same folder — just the filename.
P16. Your program is organizer.py. Open report.csv, which is in a subfolder called output/.
1# fpath = ???
Answer:
fpath = "output/report.csv"
Go into the output/ subfolder, then the file. No .. needed.
P17. Your program is organizer.py. Open config.json (one level up).
1# fpath = ???
Answer:
fpath = "../config.json"
From tools/, go up to music_app/ (..), and config.json is right there.
P18. Your program is organizer.py. Open the rock tracks.csv.
1# fpath = ???
Answer:
fpath = "../library/rock/tracks.csv"
From tools/, go up to music_app/ (..), then into library/, then rock/, then the file.
P19. Your program is running from the music_app/ folder. Open the jazz tracks.csv, my_playlists.csv, and config.json.
1# fpath_jazz = ???
2# fpath_playlists = ???
3# fpath_config = ???
Answer:
fpath_jazz = "library/jazz/tracks.csv"
fpath_playlists = "playlists/my_playlists.csv"
fpath_config = "config.json"
From music_app/, everything is below you — no .. needed.
Practice: reading, writing, and iterating#
Sample files for these exercises are in the sample_files/file_ops/ folder:
sample_files/file_ops/
├── grocery_list.txt (one item per line: milk, eggs, bread, ...)
├── scores.csv (name,score — header row + 6 students)
├── todo.txt (one task per line, 6 tasks)
├── contacts.txt (name,email,phone per line, 5 contacts)
├── log.txt (INFO/WARNING/ERROR log entries, 10 lines)
├── messy_names.txt (names with messy whitespace and casing)
└── inventory.txt (item:quantity:price per line, 7 items)
Reading basics#
P20. Read the entire contents of sample_files/file_ops/grocery_list.txt into a single string and print it.
1# your code here
Answer:
fhand = open("sample_files/file_ops/grocery_list.txt", "r")
content = fhand.read()
print(content)
P21. Read sample_files/file_ops/grocery_list.txt using .readlines() and print how many items are on the list.
1# your code here
Answer:
fhand = open("sample_files/file_ops/grocery_list.txt", "r")
lines = fhand.readlines()
print(len(lines))
P22. Read sample_files/file_ops/todo.txt and print only the first task (the first line). Use .readline() (singular — reads one line at a time).
1# your code here
Answer:
fhand = open("sample_files/file_ops/todo.txt", "r")
first = fhand.readline()
print(first.strip())
P23. Read sample_files/file_ops/scores.csv into a string, then split it into lines and print the second line (the first data row, skipping the header).
1# your code here
Answer:
fhand = open("sample_files/file_ops/scores.csv", "r")
content = fhand.read()
lines = content.strip().split("\n")
print(lines[1]) # "Joel,81"
Writing basics#
P24. Write your name and your favorite programming concept (on separate lines) to a new file called sample_files/file_ops/about_me.txt.
1# your code here
Answer:
with open("sample_files/file_ops/about_me.txt", "w") as fhand:
fhand.write("Joel\n")
fhand.write("Dictionaries\n")
P25. The file sample_files/file_ops/about_me.txt now exists from P24. Append a third line that says "INST126" without erasing what’s already there.
1# your code here
Answer:
fhand = open("sample_files/file_ops/about_me.txt", "a")
fhand.write("INST126\n")
fhand.close()
Key: use 'a' (append), not 'w' (write). 'w' would erase the existing content!
P26. Write a list of names to a new file called sample_files/file_ops/roster.txt, one name per line.
1names = ["Joel", "Sarah", "Rony", "Kacie"]
2# your code here
Answer:
names = ["Joel", "Sarah", "Rony", "Kacie"]
with open("sample_files/file_ops/roster.txt", "w") as fhand:
for name in names:
fhand.write(name + "\n")
P27. Write a dictionary to a file called sample_files/file_ops/config.txt, one key=value pair per line.
1settings = {"debug": "True", "port": "8080", "host": "localhost"}
2# your code here
Answer:
settings = {"debug": "True", "port": "8080", "host": "localhost"}
with open("sample_files/file_ops/config.txt", "w") as fhand:
for key, value in settings.items():
fhand.write(f"{key}={value}\n")
Iterating + filtering#
P28. Iterate through sample_files/file_ops/log.txt line by line. Collect only the lines that start with "ERROR" into a list, and print them.
1# your code here
Answer:
errors = []
fhand = open("sample_files/file_ops/log.txt", "r")
for line in fhand:
if line.startswith("ERROR"):
errors.append(line.strip())
print(errors)
P29. Iterate through sample_files/file_ops/grocery_list.txt. Print only the items that are longer than 5 characters.
1# your code here
Answer:
fhand = open("sample_files/file_ops/grocery_list.txt", "r")
for line in fhand:
item = line.strip()
if len(item) > 5:
print(item)
P30. Iterate through sample_files/file_ops/contacts.txt. Each line is "name,email,phone". Print only the contacts with a @umd.edu email address, formatted as: "{name}: {email}".
1# your code here
Answer:
fhand = open("sample_files/file_ops/contacts.txt", "r")
for line in fhand:
name, email, phone = line.strip().split(",")
if email.endswith("@umd.edu"):
print(f"{name}: {email}")
Parsing + computing#
P31. Read sample_files/file_ops/scores.csv (has a header row). Parse each data line and compute the average score. Print "Average: {avg:.1f}".
1# your code here
Answer:
fhand = open("sample_files/file_ops/scores.csv", "r")
lines = fhand.readlines()
total = 0
count = 0
for line in lines[1:]: # skip header
name, score = line.strip().split(",")
total += int(score)
count += 1
print(f"Average: {total / count:.1f}")
P32. Read sample_files/file_ops/inventory.txt. Each line is "item:quantity:price". Find and print all items that are out of stock (quantity is 0), formatted as: "{item} — OUT OF STOCK".
1# your code here
Answer:
fhand = open("sample_files/file_ops/inventory.txt", "r")
for line in fhand:
item, qty, price = line.strip().split(":")
if int(qty) == 0:
print(f"{item} — OUT OF STOCK")
P33. Read sample_files/file_ops/inventory.txt and compute the total value of the inventory (quantity × price for each item). Print "Total inventory value: ${total:.2f}".
1# your code here
Answer:
fhand = open("sample_files/file_ops/inventory.txt", "r")
total = 0
for line in fhand:
item, qty, price = line.strip().split(":")
total += int(qty) * float(price)
print(f"Total inventory value: ${total:.2f}")
Cleaning + writing#
P34. Read sample_files/file_ops/messy_names.txt, clean each name (strip whitespace, convert to title case), and write the cleaned names to sample_files/file_ops/clean_names.txt.
1# your code here
Answer:
with open("sample_files/file_ops/messy_names.txt", "r") as infile:
with open("sample_files/file_ops/clean_names.txt", "w") as outfile:
for line in infile:
clean = line.strip().title()
outfile.write(clean + "\n")
P35. Read sample_files/file_ops/log.txt, count how many lines are INFO, WARNING, and ERROR, and write a summary to sample_files/file_ops/log_summary.txt in the format:
INFO: 6
WARNING: 2
ERROR: 2
1# your code here
Answer:
counts = {}
with open("sample_files/file_ops/log.txt", "r") as fhand:
for line in fhand:
level = line.strip().split(":")[0]
counts[level] = counts.get(level, 0) + 1
with open("sample_files/file_ops/log_summary.txt", "w") as fhand:
for level, count in counts.items():
fhand.write(f"{level}: {count}\n")
Read → process → write (full pipeline)#
P36. Read sample_files/file_ops/scores.csv, find all students who scored 90 or above, and write their names to sample_files/file_ops/honor_roll.txt (one per line).
1# your code here
Answer:
with open("sample_files/file_ops/scores.csv", "r") as infile:
lines = infile.readlines()
with open("sample_files/file_ops/honor_roll.txt", "w") as outfile:
for line in lines[1:]: # skip header
name, score = line.strip().split(",")
if int(score) >= 90:
outfile.write(name + "\n")
P37. Read sample_files/file_ops/contacts.txt, extract just the email addresses, and write them to sample_files/file_ops/emails_only.txt (one per line).
1# your code here
Answer:
with open("sample_files/file_ops/contacts.txt", "r") as infile:
with open("sample_files/file_ops/emails_only.txt", "w") as outfile:
for line in infile:
name, email, phone = line.strip().split(",")
outfile.write(email + "\n")
P38. Read sample_files/file_ops/inventory.txt, increase the price of every item by 10%, and write the updated inventory to sample_files/file_ops/inventory_updated.txt in the same "item:quantity:price" format.
1# your code here
Answer:
with open("sample_files/file_ops/inventory.txt", "r") as infile:
with open("sample_files/file_ops/inventory_updated.txt", "w") as outfile:
for line in infile:
item, qty, price = line.strip().split(":")
new_price = float(price) * 1.10
outfile.write(f"{item}:{qty}:{new_price:.2f}\n")
P39. Read sample_files/file_ops/todo.txt and sample_files/file_ops/grocery_list.txt. Combine them into a single file called sample_files/file_ops/combined_tasks.txt. Add a section header before each group: "=== TODO ====" and "=== GROCERIES ===".
1# your code here
Answer:
with open("sample_files/file_ops/combined_tasks.txt", "w") as outfile:
outfile.write("=== TODO ===\n")
with open("sample_files/file_ops/todo.txt", "r") as f:
for line in f:
outfile.write(line)
outfile.write("\n=== GROCERIES ===\n")
with open("sample_files/file_ops/grocery_list.txt", "r") as f:
for line in f:
outfile.write(line)
JSON reading and writing#
These use two additional sample files:
sample_files/file_ops/courses.json— a dictionary of course codes to course infosample_files/file_ops/playlist.json— a playlist with nested track data
P40. Read sample_files/file_ops/courses.json into a Python dictionary using the json library. Print the instructor for INST126.
1import json
2# your code here
Answer:
import json
with open("sample_files/file_ops/courses.json", "r") as f:
courses = json.load(f)
print(courses["INST126"]["instructor"])
P41. Read sample_files/file_ops/courses.json, add a new course "INST314" with title "Statistics", instructor "Kacie", and credits 3. Write the updated dictionary back to the same file.
1import json
2# your code here
Answer:
import json
with open("sample_files/file_ops/courses.json", "r") as f:
courses = json.load(f)
courses["INST314"] = {"title": "Statistics", "instructor": "Kacie", "credits": 3}
with open("sample_files/file_ops/courses.json", "w") as f:
json.dump(courses, f, indent=4)
Note the read-modify-write pattern: load the JSON, change the dictionary in Python, then dump it back. Using indent=4 keeps the file human-readable.
P42. Read sample_files/file_ops/playlist.json. Print the playlist name, then print each track formatted as: "{title} by {artist} ({duration_sec}s)".
1import json
2# your code here
Answer:
import json
with open("sample_files/file_ops/playlist.json", "r") as f:
playlist = json.load(f)
print(playlist["name"])
for track in playlist["tracks"]:
print(f"{track['title']} by {track['artist']} ({track['duration_sec']}s)")
P43. Read sample_files/file_ops/playlist.json, add a new track ({"title": "Sunset Drive", "artist": "Waves", "duration_sec": 275}), and write the updated playlist back to the file.
1import json
2# your code here
Answer:
import json
with open("sample_files/file_ops/playlist.json", "r") as f:
playlist = json.load(f)
playlist["tracks"].append({"title": "Sunset Drive", "artist": "Waves", "duration_sec": 275})
with open("sample_files/file_ops/playlist.json", "w") as f:
json.dump(playlist, f, indent=4)
Since playlist["tracks"] is a list, we can .append() a new track dictionary to it. Then we write the whole thing back. This is the same read-modify-write pattern — it works with nested data too.
P44. Create a dictionary of your 3 favorite movies, each mapping to a dictionary with "year" and "genre". Write it to sample_files/file_ops/my_movies.json, then read it back and print it to verify.
1import json
2# your code here
Answer:
import json
movies = {
"The Matrix": {"year": 1999, "genre": "sci-fi"},
"Spirited Away": {"year": 2001, "genre": "animation"},
"The Dark Knight": {"year": 2008, "genre": "action"}
}
with open("sample_files/file_ops/my_movies.json", "w") as f:
json.dump(movies, f, indent=4)
# verify
with open("sample_files/file_ops/my_movies.json", "r") as f:
loaded = json.load(f)
print(loaded)
Common errors with files#
Can’t find the file: FileNotFoundError#
1fhand = open("mbox-email-receipts.txt", 'r')
2print(fhand.read())
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
Cell In[61], line 1
----> 1 fhand = open("mbox-email-receipts.txt", 'r')
2 print(fhand.read())
FileNotFoundError: [Errno 2] No such file or directory: 'mbox-email-receipts.txt'
In basically all cases, the issue/mismatch is between your understanding of where the thing is (path) or what its name is (fname) and what you actually told Python.
This could be a:
Misspelling (remember how literal Python is?)
Wrong/missing directions (e.g., missing a folder, or an operation)
Wrong connection type/permission: UnsupportedOperation#
1# opened in read mode, and reading works fine
2fhand = open("assets/mbox-email-receipts.txt", 'r')
3print(fhand.read())
4fhand.close()
From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008
From louis@media.berkeley.edu Fri Jan 4 18:10:48 2008
From zqian@umich.edu Fri Jan 4 16:10:39 2008
From rjlowe@iupui.edu Fri Jan 4 15:46:24 2008
From zqian@umich.edu Fri Jan 4 15:03:18 2008
From rjlowe@iupui.edu Fri Jan 4 14:50:18 2008
From cwen@iupui.edu Fri Jan 4 11:37:30 2008
From cwen@iupui.edu Fri Jan 4 11:35:08 2008
From gsilver@umich.edu Fri Jan 4 11:12:37 2008
From gsilver@umich.edu Fri Jan 4 11:11:52 2008
From zqian@umich.edu Fri Jan 4 11:11:03 2008
From gsilver@umich.edu Fri Jan 4 11:10:22 2008
From wagnermr@iupui.edu Fri Jan 4 10:38:42 2008
From zqian@umich.edu Fri Jan 4 10:17:43 2008
From antranig@caret.cam.ac.uk Fri Jan 4 10:04:14 2008
From gopal.ramasammycook@gmail.com Fri Jan 4 09:05:31 2008
From david.horwitz@uct.ac.za Fri Jan 4 07:02:32 2008
From david.horwitz@uct.ac.za Fri Jan 4 06:08:27 2008
From david.horwitz@uct.ac.za Fri Jan 4 04:49:08 2008
From david.horwitz@uct.ac.za Fri Jan 4 04:33:44 2008
From stephen.marquard@uct.ac.za Fri Jan 4 04:07:34 2008
From louis@media.berkeley.edu Thu Jan 3 19:51:21 2008
From louis@media.berkeley.edu Thu Jan 3 17:18:23 2008
From ray@media.berkeley.edu Thu Jan 3 17:07:00 2008
From cwen@iupui.edu Thu Jan 3 16:34:40 2008
From cwen@iupui.edu Thu Jan 3 16:29:07 2008
From cwen@iupui.edu Thu Jan 3 16:23:48 2008
1# opened in read mode
2# but tried to write to it
3fhand = open("assets/mbox-email-receipts.txt", 'r')
4print(fhand.write("Hello world"))
---------------------------------------------------------------------------
UnsupportedOperation Traceback (most recent call last)
Cell In[63], line 4
1 # opened in read mode
2 # but tried to write to it
3 fhand = open("assets/mbox-email-receipts.txt", 'r')
----> 4 print(fhand.write("Hello world"))
UnsupportedOperation: not writable
1import os
2os.listdir()
['.DS_Store',
'Practice - Defining Functions.ipynb',
'6_Iteration.ipynb.bak',
'README.md',
'Practice_Module-2_Lists.ipynb',
'Practice - Module 2 Review.ipynb',
'myfile.txt',
'.ipynb_checkpoints',
'.github',
'2b_Variables.md',
'Practice_Module-1-Projects.ipynb',
'Problem-Formulation.md',
'assets',
'5_lists.md',
'9_Files.ipynb.bak',
'2a_Expressions.md',
'4_Conditionals.md',
'sample_files.zip',
'requirements.txt',
'10_Pandas-1.ipynb',
'sample_files_updated.zip',
'Practice_Module-2-Projects.md',
'Help-Seeking-Template.md',
'intro.md',
'_config.yml',
'laptop-weights-by-company.csv',
'Practice_Dictionaries_Scenarios.md',
'what-is-programming.md',
'Practice_Module-2-Projects.ipynb.bak',
'.jupyter',
'LICENSE',
'complex_dictionary.json',
'Practice_Strings_Integrative.md',
'Practice_Conditionals.ipynb',
'marvel-movies.csv',
'7_Strings.md',
'Practice_Debugging_examples-Solutions.ipynb',
'Practice_Warmup_Tracing.md',
'laptop-report.txt',
'.git',
'9_Files.md',
'11_Pandas-2.ipynb',
'CleanShot 2026-04-29 at 11.58.27.png',
'_toc.yml',
'4_Conditionals.qmd',
'Practice - Defining Functions (Errors).ipynb',
'.gitignore',
'dictionary.json',
'Practice_Debugging_examples.ipynb',
'module-4-review-scratchpad.ipynb',
'3_Functions.md',
'_static',
'ncaa-team-data.csv',
'6_Iteration.md',
'sample_files_updated',
'8_Dictionaries.ipynb.bak',
'exam_draft_module2.md',
'slides_7_Strings.html',
'8_Dictionaries.md',
'Practice_Indexing_FITB.md',
'notes.md',
'Debugging-Helpseeking.md',
'sample_files',
'7_Strings.ipynb.bak',
'INST courses.csv',
'_build',
'dictionary.txt',
'data',
'example-course-copier.ipynb']
Writing different kinds of things to files#
Often we just pass strings directly into .write() to write data to a file. But sometimes we want to write specific kinds of data structures to a file and preserve its structure in some way. One example is saving a dictionary to a file.
We could write the dictionary to the file like this:
1d = {"a": 1, "b": 2, "c": 3}
2with open("dictionary.txt", "w") as f:
3 f.write(str(d)) # write the string representation of the dictionary to the file
But sometimes you want to write the dictionary in a more structured way, like JSON (which stands for JavaScript Object Notation; a standard way to represent data structures in string form to pass data between programs in a way that makes it easy to export/import with consistent structure and make parsing easy, often on the Internet).
To do this, we can use the json library to neatly package up a dictionary to be able to write it to a file and have confidence that it retains its essential structure and can easily be read back into a dictionary from a file.
The code for doing so might look like this:
1import json
2# write the dictionary to a file in JSON format
3with open("dictionary.json", "w") as f:
4 str_d = json.dumps(d, indent=4) # convert the dictionary to a JSON string, with an indentation of 4 spaces
5 f.write(str_d) # write the JSON string to the file
Then the contents of the file will look like this:
{
"a": 1,
"b": 2,
"c": 3
}
This is nice and easy to read for humans, and especially pays off for longer and more complex dictionaries. For instance:
1complex_d = {
2 "a": 1,
3 "b": 2,
4 "c": {
5 "d": 3,
6 "e": 4
7 },
8 "f": [5, 6, 7],
9 "g": {
10 "h": 8,
11 "i": {
12 "j": 9,
13 "k": 10
14 }
15 }
16}
17
18with open("complex_dictionary.json", "w") as f:
19 str_d = json.dumps(complex_d, indent=4) # convert the dictionary to a JSON string, with an indentation of 4 spaces
20 f.write(str_d) # write the JSON string to the file
The .json file contents will look like this:
{
"a": 1,
"b": 2,
"c": {
"d": 3,
"e": 4
},
"f": [
5,
6,
7
],
"g": {
"h": 8,
"i": {
"j": 9,
"k": 10
}
}
}
Instead of
{'a': 1, 'b': 2, 'c': {'d': 3, 'e': 4}, 'f': [5, 6, 7], 'g': {'h': 8, 'i': {'j': 9, 'k': 10}}}
As a bonus, you can use the json.loads() function to read the contents of a .json file directly into a dictionary in a Python program, like this:
1with open("dictionary.json", "r") as f:
2 str_d = f.read() # read the contents of the file into a string
3 d = json.loads(str_d) # convert the JSON string back to a dictionary
4 print(d)
{'a': 1, 'b': 2, 'c': 3}
The dump() and load() functions from the json library make this work even simpler!
1# write the dictionary to a file in JSON format
2with open("dictionary.json", "w") as f:
3 json.dump(d, f, indent=4) # convert the dictionary to a JSON string, with an indentation of 4 spaces, and write to f
1with open("dictionary.json", "r") as f:
2 d = json.load(f) # read the contents of the JSON file into a dictionary
3 print(d)
{'a': 1, 'b': 2, 'c': 3}
Aside: What is a library?#
You can think of a library (also called a module) as a collection of functions and data structures. You import a library (or subsets of it) into your program so you have access to special functions or data structures.
You are already using Python’s standard library, which includes built-in functions like print(), and built-in data structures like str and dict. Every time you fire up Python, these are “imported” into your program in the background.
As you advance in your programming career, you will often find that you want to solve some (sub)problems that others have tried to do, and wrote a collection of functions and/or data structures to solve those problems really well, and saved that collection into a library that others can use. Take advantage of this!
For instance, os is a library that has convenient methods for handling files:
1# import the os library
2import os
3# see the current working directory
4print(os.getcwd())
5# list the contents of the current working directory
6print(os.listdir())
7# check if a file exists
8print(os.path.exists("assets/mbox-email-receipts.txt"))
9# make a file path using os.path.join
10path = os.path.join("assets", "mbox-email-receipts.txt")
11print(path)
/home/runner/work/inst126-intro-programming-notes/inst126-intro-programming-notes
['.DS_Store', 'Practice - Defining Functions.ipynb', '6_Iteration.ipynb.bak', 'README.md', 'Practice_Module-2_Lists.ipynb', 'Practice - Module 2 Review.ipynb', 'myfile.txt', '.ipynb_checkpoints', '.github', '2b_Variables.md', 'Practice_Module-1-Projects.ipynb', 'Problem-Formulation.md', 'assets', '5_lists.md', '9_Files.ipynb.bak', '2a_Expressions.md', '4_Conditionals.md', 'sample_files.zip', 'requirements.txt', '10_Pandas-1.ipynb', 'sample_files_updated.zip', 'Practice_Module-2-Projects.md', 'Help-Seeking-Template.md', 'intro.md', '_config.yml', 'laptop-weights-by-company.csv', 'Practice_Dictionaries_Scenarios.md', 'what-is-programming.md', 'Practice_Module-2-Projects.ipynb.bak', '.jupyter', 'LICENSE', 'complex_dictionary.json', 'Practice_Strings_Integrative.md', 'Practice_Conditionals.ipynb', 'marvel-movies.csv', '7_Strings.md', 'Practice_Debugging_examples-Solutions.ipynb', 'Practice_Warmup_Tracing.md', 'laptop-report.txt', '.git', '9_Files.md', '11_Pandas-2.ipynb', 'CleanShot 2026-04-29 at 11.58.27.png', '_toc.yml', '4_Conditionals.qmd', 'Practice - Defining Functions (Errors).ipynb', '.gitignore', 'dictionary.json', 'Practice_Debugging_examples.ipynb', 'module-4-review-scratchpad.ipynb', '3_Functions.md', '_static', 'ncaa-team-data.csv', '6_Iteration.md', 'sample_files_updated', '8_Dictionaries.ipynb.bak', 'exam_draft_module2.md', 'slides_7_Strings.html', '8_Dictionaries.md', 'Practice_Indexing_FITB.md', 'notes.md', 'Debugging-Helpseeking.md', 'sample_files', '7_Strings.ipynb.bak', 'INST courses.csv', '_build', 'dictionary.txt', 'data', 'example-course-copier.ipynb']
True
assets/mbox-email-receipts.txt
The import keyword followed by the name of the library makes the functions and data structures in the library available to your running Python interpreter. This is analogous to how def makes a function available for your program to call/use.
Once you import a library, you can access the functions in that library by first declaring the name of the library (e.g., json), then a ., then the name of the method (e.g., dumps()): notice that the syntax is similar to calling methods for data structures (e.g., some_list.append()).