Coursera Python Notes
Coursera Python Notes
Python as a Calculator
Arithmetic Operators
A type is a set of values and operations that can be performed on those values.
int:integer
For example: 3, 4, 894, 0, -3, -18
float: floating point number (an approximation to a real number)
For example: 5.6, 7.342, 53452.0, 0.0, -89.34, -9.5
When multiple operators are combined in a single expression, the operations are evaluated in
order of precedence.
Operator Precedence
** highest
- (negation)
*, /, //, %
+ (addition), - (subtraction) lowest
Errors
A syntax error occurs when we an instruction with invalid syntax is executed. For example:
>>> 3) + 2 * 4
SyntaxError: invalid syntax
A semantic error occurs when an instruction with invalid semantics is executed. For example:
>>> 89.4 / 0
Traceback (most recent call last):
File "", line 1, in
89.4 / 0
ZeroDivisionError: float division by zero
For the purpose of this course, you may think of computer memory as a long list of storage
locations where each location is identified with a unique number and each location houses a
value. This unique number is called a memory address. Typically, we will write memory
addresses as a number with an "id" as a prefix to distinguish them from other numbers (for
example, id201 is memory address 201).
Variables are a way to keep track of values stored in computer memory. A variable is a named
location in computer memory. Python keeps variables in a separate list from values. A variable
will contain a memory address, and that memory address contains the value. The variable then
refers to the value. Python will pick the memory addresses for you.
Terminology
Variables
Assignment statements
Variable names
When we trace this in the visualizer and click button Forward twice, this is the result:
Here is a link to the Python Visualizer containing this code so that you can explore this
yourself. We strongly encourage you to step forward and backward through this program
until you understand every step of execution.
Built-in Functions
Function Call
Terminology:
Function dir
Python has a set of built-in functions. To see the list of built-in functions,
run dir(__builtins__):
>>> dir(__builtins__)< br/> ['ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError',
'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError',
'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'ResourceWarning',
'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning',
'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError',
'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_',
'__build_class__', '__debug__', '__doc__', '__import__', '__name__', '__package__',
'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir',
'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format',
'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input',
'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map',
'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print',
'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Function help
To get information about a particular function, call help and pass the function as the argument.
For example:
>>> help(abs)
Help on built-in function abs in module builtins:
abs(...)
abs(number) -> number
Optional arguments
In the description of function pow below, the square brackets around [, z] indicate that the third
argument is optional:
>>> help(pow)
Help on built-in function pow in module builtins:
pow(...)
pow(x, y[, z]) -> number
Built-in Functions
Function Call
Terminology:
Function dir
Python has a set of built-in functions. To see the list of built-in functions,
run dir(__builtins__):
>>> dir(__builtins__)< br/> ['ArithmeticError', 'AssertionError', 'AttributeError',
'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError',
'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError',
'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'ResourceWarning',
'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning',
'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError',
'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_',
'__build_class__', '__debug__', '__doc__', '__import__', '__name__', '__package__',
'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr',
'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir',
'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format',
'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input',
'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map',
'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print',
'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Function help
To get information about a particular function, call help and pass the function as the argument.
For example:
>>> help(abs)
Help on built-in function abs in module builtins:
abs(...)
abs(number) -> number
Optional arguments
In the description of function pow below, the square brackets around [, z] indicate that the third
argument is optional:
>>> help(pow)
Help on built-in function pow in module builtins:
pow(...)
pow(x, y[, z]) -> number
A string literal is a sequence of characters. In Python, this type is called str. Strings in Python
start and end with a single quotes (') or double quotes ("). A string can be made up of letters,
numbers, and special characters. For example:
>>> 'hello'
'hello'
>>> 'how are you?'
'how are you?'
>>> 'short- and long-term'
'short- and long-term'
If a string begins with a single quote, it must end with a single quote. The same applies to
double-quoted strings. You can not mix the type of quotes.
Escape Sequences
To include a quote within a string, use an escape character (\) before it. Otherwise Python
interprets that quote as the end of a string and an error occurs. For example, the following code
results in an error because Python does not expect anything to come after the second quote:
>>> storm_greeting = 'wow, you're dripping wet.'
SyntaxError: invalid syntax
The escape sequence \' indicates that the second quote is simply a quote, not the end of the
string:
>>> storm_greeting = 'Wow, you\'re dripping wet.'
"Wow, you're dripping wet."
An alternative approach is to use a double-quoted string when including a single-quote within it,
or vice-versa. Single- and double-quoted strings are equivalent. For example, when we used
double-quotes to indicate the beginning and end of the string, the single-quote in you're no
longer causes an error:
>>> storm_greeting = "Wow, you're dripping wet."
"Wow, you're dripping wet."
String Operators
The * and + operands obey by the standard precedence rules when used with strings.
Python has a built-in function named print that displays messages to the user. For example, the
following function call displays the string "hello":
>>> print("hello")
hello
In the output above, notice that hello is displayed without the quotation marks. The quotes are
only for Python's internal string formatting and are not seen by the user.
The print function may also be called with a mathematical expression for an argument. Python
evaluates the mathematical expression first and then displays the resulting value to the user. For
example:
>>> print(3 + 7 - 3)
7
Finally, print can take in more than one argument. Each pair of arguments is separated by a
comma and a space is inserted between them when they are displayed. For example:
>>> print("hello", "there")
hello there
When a return statement executes, the expression is evaluated to produce a memory address.
An example of return:
>>> def square_return(num):
return num ** 2
>>> answer_return = square_return(4)
>>> answer_return
16
When a print function call is executed, the argument(s) are evaluated to produce memory
address(es).
An example of print:
>>> def square_print(num):
print("The square of num is", num ** 2)
>>> answer_print = square_print(4)
The square num is 16
>>> answer_print
>>>
Function input
The function input is a built-in function that prompts the user to enter some input. The program
waits for the user to enter the input, before executing the subsequent instructions. The value
returned from this function is always a string. For example:
>>> input("What is your name? ")
What is your name? Jen
'Jen'
>>> name = input("What is your name? ")
What is your name? Jen
>>> name
'Jen'
>>> location = input("What is your location? ")
What is your location? Toronto
>>> location
'Toronto'
>>> print(name, "lives in", location)
Jen lives in Toronto
>>> num_coffee = input("How many cups of coffee? ")
How many cups of coffee? 2
>>> num_coffee
'2'
Operations on strings
Triple-quoted strings
We have used single- and double- quotes to represent strings. The third string format uses triple-
quotes and a triple-quoted string can span multiple lines. For example:
>>> print(''' How
are
you?''')
How
are
you?
Escape Sequences
Python has a special character called an escape character: \. When the escape character is used
in a string, the character following the escape character is treated differently from normal. The
escape character together with the character that follows it is an escape sequence. The table
below contains some of Python's commonly used escape sequences.
area(base, height)
(number, number) -> number
1. Examples
o What should your function do?
o Type a couple of example calls.
o Pick a name (often a verb or verb phrase): What is a short answer to "What does
your function do"?
2. Type Contract
o What are the parameter types?
o What type of value is returned?
3. Header
o Pick meaningful parameter names.
4. Description
o Mention every parameter in your description.
o Describe the return value.
5. Body
o Write the body of your function.
6. Test
o Run the examples.
The problem:
The United States measures temperature in Fahrenheit and Canada measures it in Celsius. When
travelling between the two countries it helps to have a conversion function. Write a function that
converts from Fahrenheit to Celsius.
1. Examples
2. >>> convert_to_celsius(32)
3. 0
4. >>> convert_to_celsius(212)
5. 100
6.
7. Type Contract
8. (number) -> number
9.
10.Header
11. def convert_to_celsius(fahrenheit):
12.
13.Description
14. Return the number of Celsius degrees equivalent to fahrenheit degrees.
15.
16.Body
17. return (fahrenheit - 32) * 5 / 9
18.
19.Test
20. Run the examples.
21.
Putting it all together:
def convert_to_celsius(fahrenheit):
''' (number) -> number
>>> convert_to_ccelsius(32)
0
>>> convert_to_celsius(212)
100
'''
Function Reuse
Calling functions within other function definitions
>>> perimeter(3, 4, 5)
12
>>> perimeter(10.5, 6, 9.3)
25.8
'''
return side1 + side2 + side3
>>> semiperimeter(3, 4, 5)
6.0
>>> semiperimeter(10.5, 6, 9.3)
12.9
'''
return perimeter(side1, side2, side3) / 2
The approach: Pass calls to function area as arguments to built-in function max.
def convert_to_seconds(num_hours):
"""(int) -> int
Return the number of seconds there are in num_hours hours.
>>> convert_to_seconds(2)
7200
"""
return convert_to_minutes(num_hours) * 60
seconds_2 = convert_to_seconds(4)
Here is what the memory model looks like just before the return statement inside
function convert_to_minutes looks like:
Note that there are three stack frames on the call stack: the main one, then underneath that a
frame for the call on function convert_to_seconds, and underneath that the frame for the call on
function convert_to_minutes.
Here is a link to the Python Visualizer at this stage of the execution so that you can explore this
yourself. We strongly encourage you to step backward and forward through this program
until you understand every step of execution.
When the return statement is executed, the call on convert_to_minutes exits. The bottom stack
frame is removed, and execution resumes using the stack frame for convert_to_seconds:
def convert_to_seconds(num_hours):
""" (int) -> int
Return the number of seconds there are in num_hours hours.
"""
minutes = convert_to_minutes(num_hours)
seconds = minutes * 60
return seconds
seconds = convert_to_seconds(2)
Python defines the first two functions, creates objects for them in the heap, and, in the stack
frame for the main program, creates variables that refer to those function objects.
After that, it executes the assignment statement on line 16. The right-hand side of the
assignment statement is a function call so we evaluate the argument, 2, first. The frame
for convert_to_seconds will appear on the call stack. The parameter, num_hours, will refer to the
value 2.
We now see that there are two variables called num_hours in the call stack; one is
in convert_to_minutes and the other is in convert_to_seconds.
The next line of code Python executes is minutes = num_hours * 60. However, which instance
of num_hours will be used? Python always uses the variable in the current stack frame. With an
assignment statement, if the variable does not exist in the current stack frame, Python creates it.
So, once num_hours * 60 is evaluated, variable minutes is created in the current stack frame.
The last line of the function is return minutes. Once this statement is complete, Python will
return to the frame just underneath the top of the call stack.
So, Python is going to produce the value 120, remove the current stack frame, create a new
variable called minutes in the stack frame for convert_to_seconds, and store the memory adress
of 120 in that variable.
Python then executes seconds = minutes * 60. Python evaluates the right-hand side, which
produces 7200, and stores the memory address of that value in variable seconds. Since this
variable does not exist yet, Python creates it in the current stack
frame.
Next is a return statement. Like we saw above, that is going to return control back to the the
main module.
Once the frame for convert_to_seconds is removed, the assignment statement on line 16 (which
has been paused a long time!) is completed, and a new variable seconds is created in the stack
frame for the main program.
The Python type bool has two values: True and False.
Comparison operators
The comparison operators take two values and produce a Boolean value.
equal to == 3 == 4 False
There are also three logical operators that produce Boolean values: and, or, and not.
and and (80 >= 50) and (70 <= 50) False
The and operator produces True if and only if both expressions are True.
As such, if the first operand is False, the second condition will not even be checked, because it
is already known that the expression will produce False.
The or operator evaluates to True if and only if at least one operand is True.
As such, if the first operand is True, the second condition will not even be checked, because it is
already known that the expression will produce True.
The not operator evaluates to True if and only if the operand is False.
expr1 not expr1
True False
False True
Double-negation can be simplified. For example, the expression not not (4 == 5) can be
simplified to 4 == 5.
The order of precedence for logical operators is: not, and, then or. We can override precedence
using parentheses and parentheses can also be added to make things easier to read and
understand.
For example, the not operator is applied before the or operator in the following code:
>>> grade = 80
>>> grade2 = 90
>>> not grade >= 50 or grade2 >= 50
True
Parentheses can be added to make this clearer: (not grade >= 50) or (grade2 >= 50)
Alternatively, parentheses can be added to change the order of operations: not ((grade >= 50)
or (grade2 >= 50))
Builtin function str takes any value and returns a string representation of that value.
>>> str(3)
'3'
>>> str(47.6)
'47.6'
int
Builtin function int takes a string containing only digits (possibly with a leading minus sign -)
and returns the int that represents. Function int also converts float values to integers by
throwing away the fractional part.
>>> int('12345')
12345
>>> int('-998')
-998
>>> int(-99.9)
-99
If function int is called with a string that contains anything other than digits,
a ValueError happens.
>>> int('-99.9')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '-99.9'
float
Builtin function float takes a string containing only digits and zero or one decimal points
(possibly with a leading minus sign -) and returns the float that represents. Function float also
converts int values to floats.
>>> float('-43.2')
-43.2
>>> float('432')
432.0
>>> float(4)
4.0
If function float is called with a string that can't be converted, a ValueError happens.
>>> float('-9.9.9')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: could not convert string to float: '-9.9.9'
Python contains many functions, but not all of them are immediately available as builtin
functions. Instead of being available as builtins, some functions are saved in different modules.
A module is a file containing function definitions and other statements.
We may also define our own modules with our own functions.
import
In order to gain access to the functions in a module, we must import that module.
For example, we can import the Python module math and call the function sqrt from it:
import math
The if statement
If statements can be used to control which instructions are executed. Here is the general form:
if expression1:
body1
[elif expression2: 0 or more clauses
body2]
[else: 0 or 1 clause
bodyN]
A note on None
When execution of a function body ends without having executed a return statement, the
function returns value None. The type of None is NoneType.
Return the flight status (on time, early, delayed) for a flight that was
scheduled to arrive at scheduled_time, but is now estimated to arrive
at estimated_time.
Pre-condition: 0.0 <= scheduled_time < 24.0 and 0.0 <= estimated_time < 24.0
if scheduled_time == estimated_time:
return 'on time'
In the shell:
>>> report_status(14,3, 14.3)
'on time'
>>> report_status(12.5, 11.5)
>>> print(report_status(12.5, 11.5))
None
Because the type of None is NoneType, not str, this breaks the Type Contract. To fix this, we
would need to complete the rest of the function.
No if Required
It is common for new programmers to write code like the following:
def is_even(num):
""" (int) -> bool
Return whether num is even.
"""
if num % 2 == 0:
return True
else:
return False
This works, but is stylistically questionable. It's also more typing and reading than is necessary!
num % 2 == 0 already produces True or False, so that expression can be used with
the return statement:
def is_even(num):
""" (int) -> bool
Return whether num is even.
"""
return num % 2 == 0
Structuring if Statements
if-elif vs. if-if
An if statement with an elif clause is a single statement. The expressions are evaluated from
top to bottom until one produces True or until there are no expressions left to evaluate. When an
expression produces True, the body associated with it is executed and then the if statement
exits. Any subsequent expressions are ignored. For example:
grade1 = 70
grade2 = 80
The if statement condition (grade1 >= 50) evaluates to True, so the body associated with
the if is executed and then the if exits. The elif condition is not even evaluated in this case.
It is possible for if statements to appear one after another in a program. Although they are be
adjacent to each other, they are completely independent of each other and it is possible for the
body of each if to be executed. For example:
grade1 = 70
grade2 = 80
In the program above, the condition associated with the first if statement (grade1 >= 50)
produces True, so the body associated with it is executed. The condition associated with the
second if statement (grade2 >= 50) also produces True, so the body associated with it is also
executed.
Nested ifs
It is possible to place an if statement within the body of another if statement. For example:
if precipitation:
if temperature > 0:
print('Bring your umbrella!')
else:
print('Wear your snow boots and winter coat!)
The statement above can be simplified by removing some of the nesting. The message 'Bring
your umbrella!' is printed only when both of the if statement conditions are True. The
message 'Wear your snow boots and winter coat!' is printed only when the outer if condition
is True, but the inner if condition is False. The following is equivalent to the code above:
if precipitation and temperature > 0:
print('Bring your umbrella')
elif precipitation:
print('Wear your snow boots and winter coat!')
More str Operators
String Comparisons
We can compare two strings for their dictionary order, comparing them letter by letter:
>>> 'abracadabra' < 'ace'
True
>>> 'abracadabra' > 'ace'
False
>>> 'a' <= 'a'
True
>>> 'A' < 'B'
True
Capitalization matters, and capital letters are less than lowercase letters:
>>> 'a' != 'A'
True
>>> 'a' < 'A'
False
Every letter can be compared:
>>> ',' < '3'
True
We can compare a string and an integer for equality:
>>> 's' == 3
False
We can't compare values of two different types for ordering:
>>> 's' <= 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>>
TypeError: unorderable types: str() <= int()
The operator in checks whether a string appears anywhere inside another one (that is, whether a
string is a substring of another).
>>> 'c' in 'aeiou'
False
>>> 'cad' in 'abracadabra'
True
>>> 'zoo' in 'ooze'
False
Summary
An index is a position within the string. Positive indices count from the left-hand side with the
first character at index 0, the second at index 1, and so on. Negative indices count from the
right-hand side with the last character at index -1, the second last at index -2, and so on. For the
string "Learn to Program", the indices are:
The first character of the string is at index 0 and can be accessed using this bracket notation:
>>> s[0]
'L'
>>> s[1]
'e'
Negative indices are used to count from the end (from the right-hand side):
>>> s[-1]
'm'
>>> s[-2]
'a'
Slicing
We can extract more than one character using slicing. A slice is a substring from the start index
up to but not including the end index. For example:
>>> s[0:5]
'Learn'
>>> s[6:8]
'to'
>>> s[9:16]
'Program'
More generally, the end of the string can be represented using its length:
>>> s[9:len(s)]
'Program'
The end index may be omitted entirely and the default is len(s):
>>> s[9:]
'Program'
Similarly, if the start index is omitted, the slice starts from index 0:
>>> s[:]
'Learn to Program'
>>> s[:8]
'Learn to'
Negative indices can be used for slicing too. The following three expressions are equivalent:
>>> s[1:8]
'earn to'
>>> s[1:-8]
'earn to'
>>> s[-15:-8]
'earn to'
Modifying Strings
The slicing and indexing operations do not modify the string that they act on, so the string
that s refers to is unchanged by the operations above. In fact, we cannot change a string.
Operations like the following result in errors:
>>> s[6] = 'd'
Traceback (most recent call last):
File <"pyshell#19", line 1, in <module>
s[6] = 'd'
TypeError: 'str' object does not support item assignment
Imagine that we want to change string s to refer to 'Learned to Program'. The following
expression evaluates to that 'Learned to Program': s[:5] + 'ed' + s[5:]
Notice that the string that s originally referred to was not modified: strings cannot be modified.
Instead a new string was created and s was changed to point to that string.
To get information about a method, such as the lower method, do the following:
>>> help(str.lower)
The variable refers to each character of the string in turn and executes the body of the loop for
each character. For example:
>>> s = 'yesterday'
>>> for char in s:
... print(char)
...
y
e
s
t
e
r
d
a
y
num_vowels = 0
for char in s:
if char in 'aeiouAEIOU':
num_vowels = num_vowels + 1
return num_vowels
The loop in the function above will loop over each character that s refers to, in turn. The body of
the loop is executed for each character, and when a character is a vowel, the if condition
is True and the value that num_vowels refers to is increased by one.
def collect_vowels(s):
""" (str) -> str
vowels = ''
for char in s:
if char in 'aeiouAEIOU':
vowels = vowels + char
return vowels
Variable vowels initially refers to the empty string, but over the course of the function it
accumulates the vowels from s.
IDLE's Debugger
Debug Control
The Python Visualizer has limitations: it does not allow import statements, and it stops tracing
after 300 steps. IDLE comes with a debugger, which is a tool that works a lot like the visualizer
but without the pretty pictures. To run a program in IDLE's debugger, the steps are:
1. Make sure the Python Shell window is on top and select Debug->Debugger. This opens a
window called "Debug Control".
2. Check the checkbox for Source.
3. Open the Python file where you have saved your program.
4. Select Run->Run Module. This will change the contents of Debug Control.
Step is like Forward in the visualizer: it executes the current instruction. We click Step to "step"
through the program. The Debug Control window will highlight the current line being executed.
It will also show the current variables in the "Locals" pane. The middle pane shows the current
stack frames and the current lines of code for each. We can switch back and forth between them
to see the variables.
while loops
The general form of a while loop:
while expression:
statements
The while condition, num < 100, is evaluated, and if it is True the statements in the loop body are
executed. The loop condition is rechecked and if found to be True, the body executes again. This
continues until the loop condition is checked and is False. For example:
>>> num = 2
>>> while num < 100:
num = num * 2
print(num)
4
8
16
32
64
128
In the example above, there are 6 iterations: the loop body executes 6 times.
The first attempt at solving this problem works nicely when s contains one or more vowel, but
results in an error if there are no vowels in s:
>>> i = 0
>>> s = 'xyz'
>>> while not (s[i] in 'aeiouAEIOU'):
print(s[i])
i = i + 1
x
y
z
Traceback (most recent call last):
File "<pyshell#73>", line 1, in <module>
while not (s[i] in 'aeiouAEIOU'):
IndexError: string index out of range
In the code above, the error occurs when s is indexed at i and i is outside of the range of valid
indices. To prevent this error, add an additional condition is added to ensure that i is within the
range of valid indices for s:
>>> i = 0
>>> s = 'xyz'
>>> while i < len(s) and not (s[i] in 'aeiouAEIOU'):
print(s[i])
i = i + 1
x
y
z
Because Python evaluates the and using lazy evaluation, if the first operand is False, then the
expression evaluates to False and the second operand is not even evaluated. That prevents
the IndexError from occurring.
answer = input(prompt)
return answer
def up_to_vowel(s):
''' (str) -> str
>>> up_to_vowel('hello')
'h'
>>> up_to_vowel('there')
'th'
>>> up_to_vowel('cs')
'cs'
'''
before_vowel = ''
i = 0
return before_vowel
Comments
The Why and How of Comments
As your programs get longer and more complicated, some additional English explanation can be
used to help you and other programmers read your code. These explanations
called comments document your code, much the way docstrings document your functions.
A comment begins with the number sign character (#) and goes until the end of the line. One
name for this character is the hash character. Python ignores any lines that start with this
character.
Comments are intended for programmers to read, and are usually there to explain the purpose of
a function, as well as to describe relationships between your variables. Comments are to help
you, and anyone else who is reading/using your code, to remember or understand the purpose of
a given variable or function in a program.
Type list
Overview
Our programs will often work with collections of data. One way to store these collections of
data is using Python's type list.
List Operations
Lists can also be sliced, using the same notation as for strings:
>>> grades[0:2]
[80, 90]
The in operator can also be applied to check whether a value is an item in a list.
>>> 90 in grades
True
>>> 60 in grades
False
Lists elements may be of any type. For example, here is a list of str:
Lists can also contain elements of more than one type. For example, a street address can be
represented by a list of [int, str]:
street_address = [10, 'Main Street']
for loops over list
Similar to looping over the characters of a string, it is possible to iterate over the elements of a
list. For example:
>>> for grade in grades:
print(grade)
80
90
70
80
90
70
>>> for item in subjects:
print(item)
bio
cs
math
history
>>> @
list Methods
Methods
A method is a function inside an object. You can find out the methods in type list by
typing dir(list).
Modifying Lists
>>> colours.extend(['pink',
'green'])
list.extend(list)
Append the items in the list parameter >>> print(colours)
['yellow', 'blue', 'red',
to the list. 'pink', 'green']
>>> colours.pop()
'green'
>>> print(colours)
['yellow', 'blue', 'red',
Remove the item at the end of the list; 'pink']
list.pop([index]) optional index to remove from >>> colours.pop(2)
anywhere. 'red'
>>> print(colours)
['yellow', 'blue', 'pink']
>>> colours.remove('green')
Traceback (most recent call
last):
File "<pyshell#10>", line
1, in <module>
list.remove(object)
Remove the first occurrence of the colours.remove('green')
ValueError: list.remove(x): x
object; error if not there. not in list
>>> colours.remove('pink')
>>> print(colours)
['yellow', 'blue']
list.reverse() Reverse the list. >>> grades = [95, 65, 75, 85]
>>> grades.reverse()
>>> print(grades)
[85, 75, 65, 95]
>>> grades.sort()
>>> print(grades)
list.sort() Sort the list from smallest to largest. [65, 75, 85, 95]
The table below contains methods that return information about lists.
>>> letters.index('a')
0
>>> letters.index('d')
Traceback (most recent call
list.index(object)
Return the index of the first occurrence last):
File "<pyshell#24>", line 1,
of object; error if not there. in <module>
letters.index('d')
ValueError: 'd' is not in list
Enter another one of your favourite colours (type return to end): yellow
Enter another one of your favourite colours (type return to end): brown
Enter another one of your favourite colours (type return to end):
>>> colours
['blue', 'yellow', 'brown']
>>> colours.extend(['hot pink', 'neon green'])
>>> colours
['blue', 'yellow', 'brown', 'hot pink', 'neon green']
>>> colours.pop()
'neon green'
>>> colours
['blue', 'yellow', 'brown', 'hot pink']
>>> colours.pop(2)
'brown'
>>> colours
['blue', 'yellow', 'hot pink']
>>> colours.remove('black')
Traceback (most recent call last):
File "<pyshell#18>", line 1, in <module>
colours.remove('black')
ValueError: list.remove(x): x not in list
>>> if colours.count('yellow') > 0:
colours.remove('yellow')
>>> colours
['blue', 'hot pink']
>>> if 'yellow' in colours:
colours.remove('yellow')
>>> colours
['blue', 'hot pink']
>>> colours.extend('auburn', 'taupe', 'magenta')
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
colours.extend('auburn', 'taupe', 'magenta')
TypeError: extend() takes exactly one argument (3 given)
>>> colours.extend(['auburn', 'taupe', 'magenta'])
>>> colours
['blue', 'hot pink', 'auburn', 'taupe', 'magenta']
>>> colours.sort()
>>> colours
['auburn', 'blue', 'hot pink', 'magenta', 'taupe']
>>> colours.reverse()
>>> colours
['taupe', 'magenta', 'hot pink', 'blue', 'auburn']
>>> colours.insert(-2, 'brown')
>>> colours
['taupe', 'magenta', 'hot pink', 'brown', 'blue', 'auburn']
>>> colours.index('neon green')
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
colours.index('neon green')
ValueError: 'neon green' is not in list
>>> if 'hot pink' in colours:
where = colours.index('hot pink')
colours.pop(where)
'hot pink'
>>> colours
['taupe', 'magenta', 'brown', 'blue', 'auburn']
>>>
We say that lists are mutable: they can be modified. All the other types we have seen so far
(str, int, float and bool) are immutable: they cannot be modified.
Aliasing
After the second statement executes, lst1 and lst2 both refer to the same list. When two
variables refer to the same objects, they are aliases. If that list is modified, both
of lst1 and lst2 will see the change.
range
The Built-in Function: range
Python has a built-in function called range that is useful to use when you want to generate a
sequence of numbers. You can type help(range) in IDLE if you ever need a reminder.
Applications of Range
There are other options you can specify to range. One option is to let range generate the numbers
corresponding to indices of a string or a list.
s = 'computer science'
for i in range(len(s)):
print(i)
You can also tell range what index to start at. For instance, the example below starts at index 1
(as opposed to the default which is 0).
for i in range(1, len(s)):
print(i)
You can even specify the "step" for range. The default stepping size is 1, which means that
numbers increment by 1. The example below starts at index 1 and its step size is there (goes to
every third index).
for i in range(1, len(s), 3):
print(i)
rangeis typically used in a for loop to iterate over a sequence of numbers. Here are some
examples:
# Iterate over the numbers 0, 1, 2, 3, and 4.
for i in range(5):
Because len returns the number of items in a list, it can be used with range to iterate over all the
indices. This loop prints all the values in a list:
for i in range(len(lst)):
print(lst[i])
This also gives us flexibility to process only part of a list. For example, We can print only the
first half of the list:
for i in range(len(lst) // 2):
print(lst[i])
Previously, we have written loops over characters in a string or items in a list. However,
sometimes there are problems where knowing the value of the items in a list or the characters in
a string is not enough; we need to know where it occurs (i.e. its index).
Example 1
>>> count_adjacent_repeats('abccdeffggh')
3
'''
repeats = 0
return repeats
We want to compare a character in the string with another character in the string beside it. This
is why we iterate over the indices because the location is important, and only knowing the value
of the character does not provide us with enough information. This is how we are able to count
repeated characters in a string. We can't execute the body of the loop if i is len(s) - 1 because
we compare to s[i + 1], and that would produce an IndexError.
Example 2
Shift each item in L one position to the left and shift the first item to
the last position.
first_item = L[0]
L[-1] = first_item
For the same reasons as above, merely knowing the value of the items in the list is not enough
since we need to know where the items are located; we need to know the index (position) of the
item in the list.
def shift_left(L):
''' (list) -> NoneType
first_item = L[0]
L[-1] = first_item
def count_adjacent_repeats(s):
''' (str) -> int
>>> count_adjacent_repeats('abccdeffggh')
3
'''
repeats = 0
return repeats
In these two lists, the corresponding element of list1[0] is list2[0], the corresponding element
of list2[1] is list1[1], and so on.
Return the number of characters in s1 that are the same as the character
at the corresponding position of s2.
num_matches = 0
for i in range(len(s1)):
if s1[i] == s2[i]:
num_matches = num_matches + 1
return num_matches
The function above counts the corresponding elements of the two strings that are the same
character. If a character of s1 at index i is the same as the character of s2 at the same index, then
we increment num_matches by 1 (since they match). Otherwise, we continue on to the next pair
of corresponding elements and compare them.
num_matches = 0
for i in range(len(s1)):
if s1[i] == s2[i]:
num_matches = num_matches + 1
return num_matches
Return a new list in which each item is the sum of the items at the
corresponding position of list1 and list2.
sum_list = []
for i in range(len(list1)):
sum_list.append(list1[i] + list2[i])
return sum_list
Nested Lists
Lists can contain items of any type, including other lists. These are called nested lists.
Here is an example.
>>> grades = [['Assignment 1', 80], ['Assignment 2', 90], ['Assignment 3', 70]]
>>> grades[0]
['Assignment 1', 80]
>>> grades[1]
['Assignment 2', 90]
>>> grades[2]
['Assignment 3', 70]
To access a nested item, first select the sublist, and then treat the result as a regular list.
For example, to access 'Assignment 1', we can first get the sublist and then use it as we would a
regular list:
>>> sublist = grades[0]
>>> sublist
['Assignment 1', 80]
>>> sublist[0]
'Assignment 1'
>>> sublist[1]
80
Both sublist and grades[0] contain the memory address of the ['Assignment 1', 80] nested
list.
We can access the items inside the nested lists like this:
>>> grades[0][0]
'Assignment 1'
>>> grades[0][1]
80
>>> grades[1][0]
'Assignment 2'
>>> grades[1][1]
90
>>> grades[2][0]
'Assignment 3'
>>> grades[2][1]
70
Nested Loops
Bodies of Loops
The bodies of loops can contain any statements, including other loops. When this occurs, this is
known as a nested loop.
Notice that when i is 10, the inner loop executes in its entirety, and only after j has ranged from
1 through 4 is i assigned the value 11.
Return a new list in which each item is the average of the grades in the
inner list at the corresponding position of grades.
>>> calculate_averages([[70, 75, 80], [70, 80, 90, 100], [80, 100]])
[75.0, 85.0, 90.0]
'''
averages = []
averages.append(total / len(grades_list))
return averages
In calculate_averages, the outer for loop iterates through each sublist in grades. We then
calculate the average of that sublist using a nested, or inner, loop, and add the average to the
accumulator (the new list, averages).
def averages(grades):
'''
(list of list of number) -> list of float
>>> averages([[70, 75, 80], [70, 80, 90, 100], [80, 100]])
[75.0, 85.0, 90.0]
'''
averages = []
total = 0
for mark in grades_list:
total = total + mark
averages.append(total / len(grades_list))
return averages
Reading Files
Information stored in files can be accessed by a Python program. To get access to the contents of
a file, you need to open the file in your program. When you are done using a file, you
should close it.
Python has a built-in function open that can open a file for reading.
The form of open is open(filename, mode), where mode is 'r' (to open for reading), 'w' (to
open for writing), or 'a' (to open for appending to what is already in the file).
There are four standard ways to read from a file. Some use these methods:
readline(): read and return the next line from the file, including the newline character (if it
exists). Return the empty string if there are no more lines in the file.
readlines(): read and return all lines in a file in a list. The lines include the newline character.
line = file.readline()
while we are not at the end
of the section:
process the line
line = file.readline()
flanders_file.close()
The readlines approach file = open(filename, 'r') When you want to examine
# Get the contents as a list
of strings.
contents_list =
file.readlines()
file.close()
Here are the code examples that appeared in the video. All of them read the entire file into a
string and print that string.
line = flanders_file.readline()
while line != "":
flanders_poem = flanders_poem + line
line = flanders_file.readline()
print(flanders_poem)
flanders_file.close()
print(flanders_poem)
flanders_file.close()
print(flanders_poem)
flanders_file.close()
flanders_list = flanders_file.readlines()
for line in flanders_list:
flanders_poem = flanders_poem + line
print(flanders_poem)
flanders_file.close()
In Flanders Fields
-John McCrae
>>>
Python 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "copyright", "credits" or "license()" for more information.
>>> flanders_filename = '/Users/ltp/Documents/ltp/ltp1/lectures/6readfiles/In Flanders
Fields.txt'
>>> flanders_file = open(flanders_filename, 'r')
>>> flanders_file.readline()
'In Flanders Fields\n'
>>> flanders_file.readline()
'\n'
>>> flanders_file.readline()
'In Flanders fields the poppies blow\n'
>>> flanders_file.readline()
'Between the crosses, row on row,\n'
>>> flanders_file.readline()
'That mark our place; and in the sky\n'
>>> flanders_file.readline()
'The larks, still bravely singing, fly\n'
>>> flanders_file.readline()
'Scarce heard amid the guns below.\n'
>>> flanders_file.readline()
'\n'
>>> flanders_file.readline()
'We are the Dead. Short days ago\n'
>>> flanders_file.readline()
'We lived, felt dawn, saw sunset glow,\n'
>>> flanders_file.readline()
'Loved and were loved, and now we lie\n'
>>> flanders_file.readline()
'In Flanders fields.\n'
>>> flanders_file.readline()
'\n'
>>> flanders_file.readline()
'Take up our quarrel with the foe:\n'
>>> flanders_file.readline()
'To you from failing hands we throw\n'
>>> flanders_file.readline()
'The torch; be yours to hold it high.\n'
>>> flanders_file.readline()
'If ye break faith with us who die\n'
>>> flanders_file.readline()
'We shall not sleep, though poppies grow\n'
>>> flanders_file.readline()
'In Flanders fields.\n'
>>> flanders_file.readline()
'\n'
>>> flanders_file.readline()
'-John McCrae\n'
>>> flanders_file.readline()
''
>>> flanders_file.close()
>>> flanders_file = open(flanders_filename, 'r')
>>> line = flanders_file.readline()
>>> while line != '':
print(line)
line = flanders_file.readline()
In Flanders Fields
In Flanders fields.
In Flanders fields.
-John McCrae
>>> flanders_file.close()
>>> flanders_file = open(flanders_filename, 'r')
>>> line = flanders_file.readline()
>>> while line != '':
print(line, end='')
line = flanders_file.readline()
In Flanders Fields
-John McCrae
>>> flanders_file.close()
>>> flanders_file = open(flanders_filename, 'r')
>>> line = flanders_file.readline()
>>> line = flanders_file.readline()
>>> while not line.startswith('We are the Dead'):
line = flanders_file.readline()
print(line)
>>>
Python 3.2.3 (v3.2.3:3d0686d90f55, Apr 10 2012, 11:25:50)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "copyright", "credits" or "license()" for more information.
>>> flanders_filename = '/Users/ltp/Documents/ltp/ltp1/lectures/6readfiles/In Flanders
Fields.txt'
>>> flanders_file = open(flanders_filename, 'r')
>>> print(flanders_file.read())
In Flanders Fields
-John McCrae
>>>
Write Files
Writing To A File Within A Python Program
In order to write to a file, we use file.write(str). This method writes a string to a file.
Method write works like Python's print function, except that it does not add a newline
character.
File dialogs
This function returns the full path to the file, so we can use that when we call function open to
open that file.
from_filename = tkinter.filedialog.askopenfilename()
Function asksaveasfilename asks the user to select a file to save to, and provides a warning if
the file already exists.
to_filename = tkinter.filedialog.asksaveasfilename()
Example
Below is a program that copies a file, but puts "Copy" as the first line of the copied file.
Now we can open the file we want to read from and get the contents:
from_file = open(from_filename, 'r')
contents = from_file.read()
from_file.close()
And we can open the file we want to write to and write the contents:
to_file = open(to_filename, 'w')
to_file.write('Copy\n') # We have to add the newline ourselves.
to_file.write(contents) # Now write the contents of the file.
to_file.close()
Tuples
Immutable Sequences
Tuples are immutable sequences: they cannot be modified. Tuples and lists have much in
common, but lists are mutable sequences: they can be modified.
a
3
-0.2
Type dict
Dictionary
Another way to store collections of data is using Python's dictionary type: dict.
Dictionaries are mutable: they can be modified. There are a series of operations and methods
you can apply to dictionaries which are outlined below.
del Removes a key and its associated value >>> asn_to_grade = {'A1': 80,
dict[key] 'A2': 90, 'A3': 90}
from dict. >>> del asn_to_grade['A1']
>>> asn_to_grade
{'A3': 90, 'A2': 90}
Dictionaries are unordered. That is, the order the key-value pairs are added to the dictionary has
no effect on the order in which they are accessed. For example:
>>> asn_to_grade = {'A1': 80, 'A2': 70, 'A3': 90}
>>> for assignment in asn_to_grade:
print(assignment)
A1
A3
A2
The for-loop above printed out the keys of the dictionary. It is also possible to print out the
values:
>>> asn_to_grade = {'A1': 80, 'A2': 70, 'A3': 90}
>>> for assignment in asn_to_grade:
print(asn_to_grade[assignment])
80
90
70
Finally, both the keys are values can be printed:
>>> asn_to_grade = {'A1': 80, 'A2': 70, 'A3': 90}
>>> for assignment in asn_to_grade:
print(assignment, asn_to_grade[assignment])
A1 80
A3 90
A2 70
Empty Dictionaries
Heterogeneous Dictionaries
A dictionary can have keys of different types. For example, one key can be of type int and
another of type str:
d = {'apple': 1, 3: 4}
Immutable Keys
The keys of a dictionary must be immutable. Therefore, lists, dictionary and other mutable types
cannot be used as keys. The following results in an error:
d[[1, 2]] = 'banana'
Since lists are mutable, they cannot be keys. Instead, to use a sequence as a key, type tuple can
be used:
d[(1, 2)] = 'banana'
Inverting a Dictionary
Switching Keys and Values
Dictionaries have keys that are unique and each key has a value associated with it. For example,
here is a dictionary mapping fruit to their colours:
fruit_to_colour = {'watermelon': 'green', 'pomegranate': 'red',
'peach': 'orange', 'cherry': 'red', 'pear': 'green',
'banana': 'yellow', 'plum': 'purple', 'orange': 'orange'}
To invert the dictionary, that is, switch the mapping to be colours to fruit, here is one approach:
>>> colour_to_fruit = {}
>>> for fruit in fruit_to_colour:
colour = fruit_to_colour[fruit]
colour_to_fruit[colour] = fruit
>>> colour_to_fruit
{'orange': 'orange', 'purple': 'plum', 'green': 'pear', 'yellow': 'banana', 'red':
'pomegranate'}
The resulting dictionary is missing some fruit. This happens since colours, which are keys, are
unique so later assignments using the same colour replace earlier entries. A way to remedy this
is to map colours to a list of fruit.
For the example above, we need to consider two cases when adding a colour and a fruit to the
dictionary:
1. If the colour is not a key in the dictionary, add it with its value being a single element a
list consisting of the fruit.
2. If the colour is already a key, append the fruit to the list of fruit associated with that key.
>>> colour_to_fruit = {}
>>> for fruit in fruit_to_colour:
# What colour is the fruit?
colour = fruit_to_colour[fruit]
if not (colour in colour_to_fruit):
colour_to_fruit[colour] = [fruit]
else:
colour_to_fruit[colour].append(fruit)
>>> colour_to_fruit
{'orange': ['peach', 'orange'], 'purple': ['plum'], 'green': ['watermelon', 'pear'],
'yellow': ['banana'], 'red': ['cherry', 'pomegranate']}