What are the unnecessary tests in Version 2? This time it's not unnecessary tests being evaluated, it's unnecessary comparisons being performed inside the tests.
Consider. The first if statement checks to see if
grade is between 95 and 100, so if the first elif
statement is reached we know the value is less than or equal to 94, or we
wouldn't have arrived here (unless the value is over 100, a condition we'll
deal with in a moment). Likewise, when we reach the second elif,
we no longer have to test that the value is less than or equal to 85, because
if it wasn't, it would have passed one of the previous tests. The
qualification in each case is: as long as the value isn't over 100.
This suggests that we can eliminate more than half the relational tests, as
long as we first test to make sure the value is between 0 and 100. The result
would be code like this:
# Version 3.
print " ... " # Instructions to user
grade = input("...")
if grade > 100 or grade < 0:
print "Error: The grade must be between 0 and 100."
elif grade >= 95:
print "A+"
elif grade >= 86:
print "A"
elif grade >= 80:
print "A-"
# ...
# and so on down to
# ...
elif grade >= 50:
print "D"
elif grade >= 0:
print "F"
(Again verify that the code works by "running it' in your mind with a couple of typical values.)
This code represents a significant improvement over the second version on two counts: it is more efficient, and it is easier to read. It is easier to read because the relational expressions are shorter. It is more efficient because where the previous version had 33 relational operators (3 for each of 11 letter grades) this one has 3 + 10 = 13 (2 for the initial range check followed by one for each possible letter grade except the last). This is less than half as many, a significant improvement in any field (imagine a runner completing a race in half the previous time, or a car whose mileage suddenly doubled!).