count = 0
total = 0
for line in file:
count += 1
date, temp = line.split()
temp = int(temp)
total += temp
average = round(total/count)
Notice that I had to convert temp from a string into
an integer
Looping Through a File More Than Once
When working with text files we can only go in one direction
From the beginning to the end
With a text file you can never go backwards
This means if you have to go back again through the file ...
you have to create a new file object
And there are some things that you cannot do by looping through a file once
To count the number of days with above average temperature ...
you have to loop through the file once to get the average ...
then loop through it again to count the days above that average
Attendance
New Material
More Data Processing in a Loop
In the last class we found the maximum and minimum temperature ...
from a file that looked like this
2017-06-01 67
2017-06-02 71
2017-06-03 69
...
But we can do more while we are looping through the file
We can also get the dates when the maximum and minimum occurred
We use an approach similar to the one we used for the maximum
set the variable max to the lowest possible value
for each temperature
if the temperature is greater than max
set max to this temperature
To get both the maximum temperature and the date on which it
happened
we will need two variables
max_date = ""
min_date = ""
Why did we
initialize
the variables with the empty string?
Because we do not have to compare the dates with any other values
We change these values ...
when either the max or min value is replaced
if temp > max:
max = temp
max_date = date
if temp < min:
min = temp
min_date = date
Here is the code with the new lines in red
#! /usr/bin/python3
file = open("temps.txt", "r")
max = -100
min = 200
max_date = ""
min_date = ""
for line in file:
date, temp = line.split()
temp = int(temp)
if temp > max:
max = temp
max_date = date
if temp < min:
min = temp
min_date = date
print("Maximum:", max_date, max)
print("Minimum:", min_date, min)
that violates the rules of the programming language
>>> for = 5
File "<stdin>", line 1
for = 5
^
SyntaxError: invalid syntax
Python does not let you use a
keyword
as a variable name
So this is not a legal Python statement
Misspelling the name of a function or variable is a common error
>>> name = "Glenn"
>>> print(nme)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'nme' is not defined
It is also a syntax error
Another common syntax error is leaving out a )
$ cat get_number_1.py
#! /usr/bin/python3
number = int(input("Number: ")
$ ./get_number_1.py
File "./get_number_1.py", line 6
^
SyntaxError: unexpected EOF while parsing
Indentation problems are also syntax errors
>>> for i in range(5):
... num = i * 2
... print(num)
File "<stdin>", line 3
print(num)
^
IndentationError: unindent does not match any outer indentation level
Syntax errors are the most common errors
But they are the easiest to fix
All you have to do is change some characters
As you write more code ...
you will find it easier to spot and fix syntax errors
Logic Errors
A logic error
occurs when the code does not give the correct results
Here is an example
average = num_1 + num_2 / 2
print ("Average:", average)
Can you spot the problem here?
Division, /, has higer precedence than
addition, +
So 2 will not divide the sum of num_1
and num_2 ...
but num_2 alone
The average should be calculated like this
average = (num_1 + num_2) / 2
When you have a syntax error the interpreter will tell you
The error message it gives specifies the kind of error ...
and prints the line where it occurs
But you get no warning with a logic error
The code runs with no sign of a problem
You will not know there is a problem ...
unless you check the results of running the code
This is why it is always important run tests on the scripts you write
Even if you know there is an error ...
it can be hard to find
The problem could be anywhere in the code
It is hard to spot your own errors
That is why smart coders ask others to proof read what they write
Giving a conversion function a value it can't handle ...
will result in another runtime error
$ ./int_request.py
Integer: five
Traceback (most recent call last):
File "./int_request.py", line 5, in <module>
number = int(input("Integer: "))
ValueError: invalid literal for int() with base 10: 'five'
Or you can give a function an argument with the wrong type
>>> round("five")
Traceback (most recent call last):
File "<stdin>, line 1, in <module>
TypeError: type str doesn't define __round__ method
Or divide by zero
$ ./divide_two_numbers.py
Numerator: 5
Denominator: 0
Traceback (most recent call last):
File "./divide_two_numbers.py", line 7, in <module>
result = numerator / denominator
ZeroDivisionError: division by zero
The problems that cause runtime errors are called
exceptions
Exceptions Objects
Errors like the ones above are frustrating
But the error message gives you some idea what went wrong
Decades ago my CS classes required me to write C programs
In C a runtime error would produce the following output
Segmentation fault: Core dumped
Thanks, that was really helpful
Many computer languages have a better way of dealing with this problem
They have a mechanism built into the language ...
that deals with runtime errors
It gathers information about what happened and where ...
and uses that information to create an error message
$ cat open_file.py
#! /usr/bin/python3
# demonstrates using a try/except statement
# to catch exceptions encountered while
# trying to open a file
filename = input("Filename: ")
try:
file = open(filename, "r")
for line in file:
print(line.strip())
except:
print("Could not open file", filename)
$ ./open_file.py
Filename: xxxx
Could not open file xxxx
Notice that the interpreter never ran the for loop
When the exception occurred ...
it stopped running the try code ...
and jumped to the except code block
Exceptions and Data Validation
We can use the input function to ask the user for a
number ...
and then try to convert it into an integer
But if the user enters a decimal we will get an exception
>>> number = int(input("Integer: "))
Integer: 5.0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '5.0'
Whenever we get data from the user we need to check it
To make sure it is it can be handled by the script
and it is best done with a function that uses a while loop
A few weeks ago I showed you an algorithm to validate data
ask the user for a value
while the value is not valid:
print an error message
ask the user for a value
return the value
We need a different algorithm here
If I ask the user for a number ...
and then try to convert it into an integer or float ...
I could get a runtime error
The conversion function must be run inside a
try/except statement
And the try/except statement needs to be inside the
while loop
But what boolean expression should the while loop use?
One way to deal with this is to set a
flag
to False
The while loop will keep running as long as the flag is
False
If the conversion does not cause an exception ...
the flag is set to True
This will cause the loop to exit ...
and we can return the value
Here is the algorithm
set a flag to false
while the flag is not true
ask the user for a value
try:
convert the value to an integer
set the flag to true
except:
print an error message
return the value
Here is a function that implements this algorithm
def get_integer():
done = False
while not done:
number = input("Integer: ")
try:
number = int(number)
done = True
except:
print(number, "cannot be converted into an integer")
return number
When we run the code we get
$ ./get_integer.py
Integer: 2.0
2.0 cannot be converted into an integer
Integer: 2
2
Improving the Validation Function
The above function works
But we can make it shorter
And shorter code is usually better code
Why?
Because every line you write could cause an error
The shorter your code ...
the less chance there is for a problem
We can use a trick to make sure the while loop runs ...
when we don't yet have a value
We can write
while True
But how do we ever get out of the loop?
Instead of giving a flag a different value ...
we use a return statement
If the conversion does not cause a problem ...
the return statement will be run ...
and a return statement always causes the function to stop
That's how we exit the loop
Here is the refactored code
def get_integer():
while True:
number = input("Integer: ")
try:
number = int(number)
return number
except:
print(number, "cannot be converted into an integer")