7b. Prof. Rosenthal’s Additions#
Additions to Chapter 7: Iteration and Search: topics that the chapter uses, but does not explain. Read this page after the chapter. The practice questions at the end have answers: try each question first, then open its Answer.
Run the setup cell below first. In Colab, it downloads the file words.txt (the word list that
Chapter 7 uses), and thinkpython.py, which lets this page show the errors of some examples without
stopping the page.
from os.path import basename, exists
def download(url):
filename = basename(url)
if not exists(filename):
from urllib.request import urlretrieve
local, _ = urlretrieve(url, filename)
print("Downloaded " + str(local))
return filename
download('https://github.com/AllenDowney/ThinkPython/raw/v3/thinkpython.py');
download('https://raw.githubusercontent.com/AllenDowney/ThinkPython/v3/words.txt');
import thinkpython
Opening a file on your own computer#
Chapter 7 opens the word list with open('words.txt'). In Colab, this works because the chapter’s setup cell
downloads words.txt first. If you run Python on your own computer (for example, in Jupyter, VS Code or
Spyder), you must download the file yourself:
Click here to download words.txt
Most browsers save the file in your Downloads folder. If the browser shows the words instead of saving the file, right-click the link and choose Save Link As… (or Download Linked File).
After the download, open('words.txt') can still fail on your computer, with an error like this one:
file_object = open('a_file_that_is_not_here.txt')
FileNotFoundError: [Errno 2] No such file or directory: 'a_file_that_is_not_here.txt'
The reason: when you give open only a file name, Python looks for the file in one folder only, the
working directory (see “Diving deeper: relative paths and the working directory” below). On your computer, the working
directory is usually not your Downloads folder.
The sure way to open the file is to give open the full path of the file.
Full paths#
A full path (also called an absolute path) names the file and every folder that contains it, from the
top folder of the disk down to the file. In Python, separate the folders with forward slashes (/).
On Windows, a full path starts with a drive letter and a colon:
C:/Users/YOUR_NAME/Downloads/words.txtOn a Mac (and on Linux), a full path starts with a slash:
/Users/YOUR_NAME/Downloads/words.txt
YOUR_NAME is your user name on the computer. To open the file, put its full path in a string and give the
string to open. The sections below show how to find the full path of your Downloads folder.
Windows: the full path of the Downloads folder#
Find the path:
Open File Explorer (press the Windows key and E together).
In the list on the left, click Downloads.
Click an empty part of the address bar at the top of the window (to the right of the folder names). The address bar now shows the full path of the folder, for example
C:\Users\alice\Downloads.
Use the path in Python. Windows shows the path with backslashes (\). When you type the path in
Python, type a forward slash (/) for each backslash, and add the file name at the end:
file_object = open('C:/Users/alice/Downloads/words.txt')
Replace alice with your own user name. Python on Windows accepts forward slashes, so this opens the file
words.txt in your Downloads folder.
Diving deeper (for Windows users): backslashes in paths
Windows uses backslashes, and Python accepts forward slashes. Windows separates the folders of a path with
backslashes: C:\Users\alice\Downloads\words.txt. When a Python program on Windows opens a file, Python
accepts forward slashes too, and the operating system finds the same file. That is why this page uses forward
slashes: they work the same way on Windows, on a Mac and on Linux.
Why not type the backslashes? In a Python string, a backslash starts a special character. For example,
\n is a newline and \t is a tab. So this line does not display the path:
print('C:\new\table.txt')
It displays C:, then a new line (from \n), then ew, a tab (from \t) and able.txt. And a string that
has \U in it, like every path that starts with C:\Users, is a SyntaxError
((unicode error) 'unicodeescape' codec can't decode bytes ...):
file_object = open('C:\Users\alice\Downloads\words.txt') # SyntaxError
Two other ways to write backslashes. You can also keep the backslashes in one of these ways. Both open the
same file as 'C:/Users/alice/Downloads/words.txt':
# A "raw string": an r before the quote tells Python to keep each backslash as it is
file_object = open(r'C:\Users\alice\Downloads\words.txt')
# Two backslashes for each backslash (in a normal string, \\ is one backslash)
file_object = open('C:\\Users\\alice\\Downloads\\words.txt')
Copy as path. Right-click a file in File Explorer and choose Copy as path (on Windows 10, hold down
Shift while you right-click). This copies the full path of the file with backslashes and in double quotes,
for example "C:\Users\alice\Downloads\words.txt". To use it in Python, paste it and put an r before it:
open(r"C:\Users\alice\Downloads\words.txt"). Or change each backslash to a forward slash.
Python shows backslashes. Some functions return Windows paths with backslashes, for example
os.getcwd() (see “Diving deeper: relative paths and the working directory”). Python displays such a string
with two backslashes for each one, like 'C:\\Users\\alice', because that is how you type it in a normal
string.
Mac: the full path of the Downloads folder#
Find the path:
Open Finder, and click Downloads in the sidebar on the left.
Hold down the Control key and click the file
words.txt(or right-click it).While the menu is open, hold down the Option key. The menu item Copy “words.txt” changes to Copy “words.txt” as Pathname. Click it.
This copies the full path of the file, for example /Users/alice/Downloads/words.txt.
The Downloads folder is always /Users/YOUR_NAME/Downloads. To see YOUR_NAME, open Terminal
(in Applications > Utilities), type whoami and press Return.
Use the path in Python. A Mac path has forward slashes only, so you can paste it into a string as it is:
file_object = open('/Users/alice/Downloads/words.txt')
Replace alice with your own user name.
Reading the file with its full path#
When open has the full path, the rest of the code is the same as in Chapter 7. For example, this code
counts the words in the file. Use the path for your computer:
total = 0
for line in open('C:/Users/alice/Downloads/words.txt'): # Windows
# for line in open('/Users/alice/Downloads/words.txt'): # Mac
total += 1
print(total)
Diving deeper: relative paths and the working directory
The working directory. Every running Python program has one folder that is its working directory
(also called the current directory). A file name or a path that does not start at the top of the disk is
a relative path: Python looks for it starting from the working directory. So open('words.txt') opens
the file words.txt in the working directory.
See the working directory. The function getcwd (“get current working directory”) of the module os
returns the working directory as a string:
import os
print(os.getcwd())
In Colab, this displays /content. On your computer, it depends on how you started Python. Jupyter usually
uses the folder of the notebook; a script that runs from a terminal uses the terminal’s current folder.
See the files in the working directory. os.listdir() returns the names of the files and folders in the
working directory. If words.txt is not in it, open('words.txt') causes a FileNotFoundError.
Set the working directory. os.chdir (“change directory”) makes another folder the working directory.
After that, relative paths start from the new folder:
import os
os.chdir('C:/Users/alice/Downloads') # Windows
# os.chdir('/Users/alice/Downloads') # Mac
file_object = open('words.txt') # opens C:/Users/alice/Downloads/words.txt
In Jupyter and Colab, you can also type %pwd (show the working directory) and %cd FOLDER (change it)
in a cell.
Relative paths with folders. A relative path can name folders too. If the working directory is
C:/Users/alice, then:
'Downloads/words.txt'is the fileC:/Users/alice/Downloads/words.txt;'..'is the folder above the working directory, so'../bob/notes.txt'is the fileC:/Users/bob/notes.txt.
Your home folder, on any computer. os.path.expanduser('~') returns the full path of your home folder
(for example /Users/alice on a Mac). os.path.join joins folder names and a file name
with the correct separator. So this code opens words.txt in the Downloads folder on Windows and on a Mac,
without typing your user name:
import os
path = os.path.join(os.path.expanduser('~'), 'Downloads', 'words.txt')
file_object = open(path)
os.path.exists(path) returns True if a file or folder with that path exists, and False if not.
Use it to test a path before you open it.
for loops and iterables#
Chapter 7 uses a for loop with a range, with a string and with a file. These are three examples of the
same idea. A for loop works with any iterable: a value that can give its items one at a time.
The general form of a for loop is:
for VARIABLE in ITERABLE:
SOME_CODE
The loop assigns the first item of ITERABLE to VARIABLE and runs the body, then assigns the next item and runs the body again, until there are no more items. What an “item” is depends on the type of the iterable:
Iterable |
Its items |
Example |
|---|---|---|
a range |
integers |
|
a string |
the characters of the string, one at a time |
|
a file object |
the lines of the file, one at a time (as strings, each with a newline at the end) |
|
A range is an iterable of integers:
for i in range(1, 4):
print(i)
1
2
3
A string is an iterable of characters. Spaces and punctuation are characters too:
for letter in 'Hi, Bob':
print(letter)
H
i
,
B
o
b
A file object is an iterable of lines. This loop counts the lines of words.txt, and displays
the first 3 of them (without the newline at the end of each line):
count = 0
for line in open('words.txt'):
count += 1
if count <= 3:
print(line.strip())
aa
aah
aahed
reversed makes an iterable that gives the items of another iterable in the opposite order.
It works with strings and ranges:
for letter in reversed('stop'):
print(letter, end=' ')
print()
for i in reversed(range(1, 4)):
print(i, end=' ')
p o t s
3 2 1
Later chapters show more iterables. For example, a list (Chapter 9) is an iterable of the values in the list.
Not every value is an iterable. An integer or a float has no items, so a for loop over it causes a
TypeError. To repeat something 5 times, loop over range(5), not over 5:
for i in 5:
print(i)
TypeError: 'int' object is not iterable
A file object gives its lines only once. A file object remembers how far it has read. When a loop gets to the end of the file, the file object has no more lines, so a second loop over the same file object does not run its body. Here, the second loop counts nothing:
file_object = open('words.txt')
count = 0
for line in file_object:
count += 1
print('first loop:', count)
count = 0
for line in file_object:
count += 1
print('second loop:', count)
first loop: 113783
second loop: 0
To read the file again, call open again: it makes a new file object that starts at the beginning
of the file. A range and a string do not have this problem: you can loop over them again and again.
Practice questions#
Question 1 (easy): what is displayed?#
Predict what this code displays. Then run the cell to check (on the website, click Show code cell output).
for ch in 'Yes!':
print(ch, end='-')
Answer
The loop displays Y-e-s-!-. A string is an iterable of its characters, so the loop runs once for each
of the 4 characters. Because of end='-', print puts a dash after each character instead of a newline,
also after the last one.
Question 2 (easy): open a file on Windows#
On a Windows computer, the address bar of File Explorer shows that the file scores.txt is in the folder
C:\Users\maria\Documents\class. Write the call to open that opens the file. Use forward slashes.
Answer
open('C:/Users/maria/Documents/class/scores.txt')
Type a forward slash for each backslash in the path that Windows shows, and add a slash and the file name at the end. (The box “Diving deeper (for Windows users)” shows other ways to write a Windows path.)
Question 3 (medium): what happens?#
Predict what happens when this code runs:
for digit in 2024:
print(digit)
Answer
A TypeError: 'int' object is not iterable. An integer has no items, so a for loop cannot loop over it.
To loop over the digits, loop over the string '2024', which is an iterable of 4 characters:
for digit in '2024':
print(digit)
To loop over the integers from 0 to 2023, loop over range(2024).
Question 4 (medium): relative paths#
The working directory is C:/Users/alice/Documents. Give the full path of the file that each call opens.
Part a:
open('words.txt')Part b:
open('python/words.txt')Part c:
open('../Downloads/words.txt')Part d:
open('C:/Users/alice/Downloads/words.txt')
Answer
Part a:
C:/Users/alice/Documents/words.txt. A file name alone is in the working directory.Part b:
C:/Users/alice/Documents/python/words.txt. The relative path starts at the working directory.Part c:
C:/Users/alice/Downloads/words.txt...is the folder above the working directory,C:/Users/alice.Part d:
C:/Users/alice/Downloads/words.txt. A full path does not depend on the working directory.
Question 5 (medium): write a function#
Write a function called count_lines that takes the path of a file (a string) and returns the number of
lines in the file. For example, count_lines('words.txt') returns the number of words in the word list.
Start from this header (replace ... with the body):
def count_lines(path):
...
Answer
def count_lines(path):
count = 0
for line in open(path):
count += 1
return count
The parameter can be a file name, a relative path or a full path: open accepts all of them. For example,
count_lines('C:/Users/alice/Downloads/words.txt') counts the lines of the file in alice’s Downloads folder.
Question 6 (hard): what is displayed?#
Predict what this code displays. Then run the cell to check (on the website, click Show code cell output).
file_object = open('words.txt')
count = 0
for line in file_object:
count += 1
for line in file_object:
count += 1
print(count)
Answer
113783, the number of lines in the file, and not two times that number. The first loop counts all the lines. When the first loop ends, the
file object is at the end of the file, so the second loop does not run its body, and count stays
113783. To count the lines twice, call open again before the second loop.
Question 7 (hard): write a function#
Write a function called count_letter that takes a string text and a one-character string letter, and
returns the number of times that letter is in text. Upper and lower case count as the same letter.
For example, count_letter('Banana', 'a') returns 3, and count_letter('Banana', 'B') returns 1.
Start from this header (replace ... with the body):
def count_letter(text, letter):
...
Answer
def count_letter(text, letter):
count = 0
for ch in text.lower():
if ch == letter.lower():
count += 1
return count
The loop goes over the characters of text (a string is an iterable). lower makes both strings lower
case, so that 'B' and 'b' count as the same letter.
Credits#
Additions by Prof. Y. Rosenthal, for Think Python: 3rd Edition by Allen B. Downey. Text license: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International