CIS 2020 · Python for Everyone · Module 4 · Study guide
This module closes out Chapter 2 with two short special topics on how text is really stored, then opens Chapter 6: lists, the container that holds many related values under one name, and the handful of algorithms you will reuse on every list you ever write.
Section 2.5
Getting a value in from the user, converting it to something you can compute with, and printing it back out in a shape a person can read.
The input function reads a line of text the user types and returns it. The program pauses at that statement until the user presses Enter. The prompt is the message displayed to say what to type, passed as input's argument; without one, the program looks frozen, since nothing tells the user anything is expected.
name = input("Enter your name: ")
print("Hello,", name)
input always returns a string, no matter what the user types. Digits typed at the prompt come back as a string of digits, not a number.
Because input returns a string, a value meant for arithmetic must be converted right where it is read: int(text) for a whole quantity, float(text) for one with a fractional part. If the user types something that is not a number, the conversion raises a ValueError at run time.
count = int(input("How many packs? "))
price = float(input("Price per pack: $"))
total = count * price
print("Total: $", total)
A format string mixes text with format specifiers: %d for an integer, %f for a float, %s for a string. In %10.2f, the 10 is the field width, the minimum character positions the value occupies (padded and right justified), and the .2 is the precision, the digits shown after the decimal point. Precision only changes the display; the stored value is untouched.
price = 4.5
print("Total: $%.2f" % price)
for p in (4.5, 129.0, 3.25):
print("$%10.2f" % p)
# output:
# $4.50
# $129.00
# $3.25
Special Topic 2.4 · p. 46
Underneath every letter on the screen is a plain number. This is that number.
A character is stored internally as an integer value: characters are not stored as shapes, but as numbers from a standard set of codes, and the letter you see on screen is only how that integer gets displayed. The textbook's Appendix D lists the values used for the characters common in Western European languages. Look up the character "H" there and you find it is encoded as the number 72.
ord takes a character and returns its numeric code; chr takes a numeric code and returns the character it stands for. They are inverses of each other.
print("The letter H has a code of", ord("H"))
print("Code 97 represents the character", chr(97))
# output:
# The letter H has a code of 72
# Code 97 represents the character a
print(ord("A")) # 65
print(chr(65)) # A
print(chr(ord("A") + 1)) # B, the next letter
The codes Python works with are Unicode code points: the values ord() returns form an ordered sequence of integers running from 0 up to 0x10FFFF, over a million possible code points in all, of which 159,801 are currently assigned to an actual character. Because that sequence has a fixed order, it defines a total ordering on characters — every character has a code, and every code can be compared to every other. That is exactly why strings can be compared lexicographically: 'a' < 'b' is true because 97 < 98, and it is this same character-by-character numeric comparison that makes sorting and searching on strings behave the way you expect.
print('a' < 'b') # True, because ord('a') is 97 and ord('b') is 98
print(ord('a') < ord('b')) # True -- the comparison you're really making
print(sorted(["banana", "Apple", "cherry"]))
# ['Apple', 'banana', 'cherry'] -- capital letters have lower codes, so they sort first
Working with the numeric code instead of the character itself is useful because numbers can be compared and calculated with. Codes for the letters run in alphabetical order, so comparing two codes puts characters in order, and adding to a code steps a letter forward.
letter = "m"
if letter < "s":
print(letter, "comes before s")
next_letter = chr(ord(letter) + 1) # "n"
Special Topic 2.5 · p. 47
A way to put a character in a string literal that the string's own punctuation would otherwise swallow.
The escape character is the backslash. An escape sequence is the backslash together with the character right after it, read as a single unit standing for something other than what those two characters normally mean.
| Sequence | Produces |
|---|---|
| \" | a quotation mark, as part of the string's contents |
| \\ | one literal backslash |
| \n | a newline — output moves to the start of the next line |
| \t | a tab — output advances to the next tab stop |
print("She said \"hello\" to me.")
print("C:\\Users\\student")
print("Line one\nLine two")
print("Name:\tGrade")
# output:
# She said "hello" to me.
# C:\Users\student
# Line one
# Line two
# Name: Grade
The quotation mark normally marks where a string literal ends, so writing one plainly inside a string would end the string early and leave a syntax error. The backslash tells Python to treat that quotation mark as ordinary content instead.
The backslash itself never appears in the output. It is an instruction about how to read the next character, and it is consumed in the process — which is exactly why one literal backslash takes two characters, \\, to write in source code.
Section 6.1
A single name that holds many values, each one findable by a position number.
A list is a container that holds a sequence of values under one name; each value it holds is an element. An index identifies one element by position, and indexes start at 0: the first element is at index 0, the last is at index len(list) - 1.
grades = [91, 84, 77, 95] print(grades[0]) # 91, the first element print(grades[3]) # 95, the last element print(len(grades)) # 4
A list is mutable: its contents can change after it is created. A string cannot be changed this way; any operation that looks like it edits a string actually builds a new one.
grades[0] = 100 # replaces the element in place, no new list created
Using an index that does not exist, equal to or past the length, raises an IndexError: an index out of range error. It is caught only while the program runs, at the line that uses the bad index, so it is a run-time error, not a syntax error.
grades[10] # IndexError: list index out of range
Section 6.2
Adding, removing, searching — and the one behavior of lists that causes more debugging sessions than anything else in this course.
| Operation | Identifies the element by | Effect |
|---|---|---|
| list.append(x) | — | adds x at the end |
| list.insert(i, x) | position i | adds x at position i, later elements shift right |
| list.remove(x) | value x | deletes the first element equal to x, later elements shift left |
| list.pop(i) | position i (default: last) | deletes and returns the element at that position |
names = ["Ann", "Ben", "Cal"]
names.append("Dee") # ["Ann", "Ben", "Cal", "Dee"]
names.insert(1, "Amy") # ["Ann", "Amy", "Ben", "Cal", "Dee"]
names.remove("Cal") # ["Ann", "Amy", "Ben", "Dee"]
last = names.pop() # last = "Dee", names is now ["Ann", "Amy", "Ben"]
remove is given a value and must search for it, deleting only the first match; pop is given a position and hands back the element it removed.
The in operator reports whether a value occurs anywhere in the list, true or false. The index method reports the position of the first match, and raises a ValueError if the value is not present. Checking with in first avoids that crash.
if "Ben" in names:
print(names.index("Ben")) # 2
else:
print("not found")
Assigning one list variable to another does not copy its contents; it copies a reference, so both names lead to the exact same list. A second name for the same list is an alias.
a = [1, 2, 3] b = a # b is an alias for a, not a copy b.append(4) print(a) # [1, 2, 3, 4] -- a changed too! c = a.copy() # or list(a), or a[:] -- a real, independent copy c.append(99) print(a) # [1, 2, 3, 4] -- unaffected this time
This only matters because lists are mutable. Aliasing a string variable is harmless — no operation can change a string in place, so there is nothing for a second name to accidentally affect.
Section 6.3
Four patterns that cover almost everything you will ever do with a single list. Learn the shape, not just the code.
Start a running total at zero, visit every element, and add each one in.
total = 0
for g in grades:
total = total + g
Start by assuming the first element is the largest, then check the rest, replacing the remembered value whenever something bigger turns up.
largest = grades[0]
for g in grades[1:]:
if g > largest:
largest = g
Starting from an actual element matters: if the list holds only negative numbers, starting from 0 would report 0 as the maximum even though it never appears in the list.
Visit elements one at a time, compare each to the target, and stop the moment a match is found. Unlike sum or count, a search can end early.
target = 77
found = False
for g in grades:
if g == target:
found = True
break
Start a counter at zero, visit every element, and add one whenever an element passes a test. This one always visits the whole list, since an early match does not rule out later ones.
passing = 0
for g in grades:
if g >= 70:
passing = passing + 1
| Algorithm | Starts from | Visits every element? |
|---|---|---|
| Sum | 0 | Yes, always |
| Largest / smallest | the first element | Yes, always |
| Linear search | — | No, stops at the first match |
| Counting matches | 0 | Yes, always |
input always returns a string; convert with int() or float() right where you read it.%10.2f, 10 is the field width and 2 is the precision. Precision changes only the display, never the stored value.ord turns a character into its numeric code; chr turns a code back into a character. They undo each other.\", \\, \n, \t. The backslash itself never appears in the output.list[-1] is the last element. Using an index that does not exist raises IndexError at run time.remove(value) searches for a value; pop(index) takes a position and returns what it removed..copy(), list(x), or x[:] for an independent copy.