Need an easy way to produce or describe a range of numeric
values
The built-in range() function handles
this and produces the needed iterable object
Takes 1, 2, or 3 arguments:
Start (default 0): where to start the sequence at
Stop (mandatory): the sequence ends just below this value
(does not include it!)
Step (default 1): what value the sequence counts by
Be careful, the range function will stop
one step before the final stop value.
For ranging examples
Providing just a stop argument:
for n in range(5):
print(n)
Providing a start and stop:
for n in range(1,11):
print(n)
Providing a start, stop, and step:
for n in range(10,0,-1):
print(n)
Review
Question and Understanding Check
Which of the below blocks of code would print something different
from the others?
for n in range(10):
if n % 2 == 0:
print(n)
for i in range(0,10,2):
if i > 0:
print(i)
for j in '02468':
L = int(j)
print(L)
for k in range(0,10):
if not (k % 2 > 0):
print(k)
Iterating over sequences
We can also use a for loop to iterate
directly over a sequence of values
We can loop through a string or list to examine each individual
character or element
Example of looping through a word to count occurrences of a given
letter:
def count_letters(letter, string):
count = 0
for character in string:
if character == letter:
count += 1
return count
Test Yourself
It is easy to make mistakes when writing code
Syntax mistakes
Typos or operations mistakes
Overall conceptual mistakes
Given that making mistakes is entirely natural, it is also
imperative that looking for mistakes become entirely natural
You known what the desired goal is for basically every problem in
this class. Test your code!
Way of Testing
Run your code early and often
If your code is breaking at something early, it is
not going to fix itself if you continue
Test outputs directly
Problems will generally give several example
outputs of desired functions
For your own functions, you should know what you
want them to be outputting
Run a function as isolated as you can make it after
writing it, and ensure that it is performing as expected
Write code to test code
Wherever possible, I provide some automated tests
that can be run
You can (and should) write your own tests as well.
They need not be complicated!!
Assertions
You can use Python’s assert statement
to write test functions, which take the form:
assert condition
where condition is any operation that
returns a True or
False
Assert statements “expect” a condition to yield a
True, and if they do, nothing happens
No news is good news in this case
If an assert condition evaluates to
False, an error is raised
Naming your test functions starting with the word
test_ will make them automatically
discoverable by other tools
Testing Example
Suppose we wanted to write some checks of the
count_letters function from earlier
def test_count_letters():
""" Runs several tests on the function count_letters """
assert count_letters("z", "banana") == 0
assert count_letters("a", "strawberry") == 1
assert count_letters("A", "apple") == 0
assert count_letters("e", "eerie") == 3
Algorithms
Recall that when approaching a computation problem, you
must have an algorithm designed before you start
coding
An algorithm is a problem-solving strategy, and
should be:
Clear and unambiguous, in the sense that it is
understandable and leaves no gaps
Effective, in the sense that each step is actually
possible
Finite, in the sense that it ends at some point
You need to come up with an algorithm before you start coding!
Creating your own Algorithms
Some useful hints to keep in mind when constructing
your own algorithms:
Think about how you would solve the problem
without a computer. You can’t write code if you don’t
understand what you want the computer to do.
Computers are fast! Brute force methods are often
very viable, or at least a good starting point.
Try to use tools and programming patterns you have
already seen. It is often far easier to write programs by assembling
pieces from code you have already seen than writing each program
entirely from scratch.
Common patterns we have already seen include:
looping over sequences, and using variables to track/control a loop
Recognize that the program you write is highly
unlikely to work the first time
Errors can occasionally be in your algorithms
More often, early on, errors are in your
translating of the algorithm into Python (the implementation)
Example: Greatest Factor
Suppose we wanted to write a function to compute the greatest
factor of a provided number (not including the number itself)
Algorithm:
Brute force – check all smaller values to see if
factor
Start at top and work down, means first found is
the greatest
Check if factor by seeing if remainder 0
def greatest_factor(num):
"""Finds the greatest factor of a number."""
for i in range(num-1,0,-1):
if num % i == 0:
return i
Debugging
If debugging is the process of removing software bugs, then
programming must be the process of putting them in.
Edsger W. Dijkstra
Everyone makes mistakes when writing code
A core skill then is in efficiently finding the
bugs that you introduce
We’ll spend the rest of today looking at some good
practices
As always though, practice makes perfect
Strategy #1
Concentrate on what your program IS doing, instead of what it SHOULD
be doing.
It may be impossible to find code that is missing
Instead focus on determining what your program is doing, or
why it is behaving a certain way
Only once you understand what it is currently doing can you
entertain thinking about how to change it productively
Strategy #2
Let Python help you: print or log the state of different
variables.
Many errors are caused by you expecting a variable to have some
content that it doesn’t have
Get Python to help you by adding print statements to print those
variables out
Add print statements in blocks of code that you aren’t sure are
being accessed to see if you see a message
Strategy #3
Stop and read. The documentation. The error messages.
Parsing Error Messages
Start at the bottom! That is where the general type of error and a
short description will show up.
Want to know where it happened? Look one line up from that.
Will show a copy of the line where the error occurred
One line up from that will include the line number
Want nicer error messages?
The rich library offers some very
pretty error messages: install with
pip install rich
At the top of your code, then include:
from rich.traceback import install
install(show_locals=True)
Strategy #4
Use PythonTutor or a debugger to track EXACTLY what is happening in
your program.
Strategy #5
Don’t make random changes to your code in the hopes that it will
miraculously start working.
Making random changes is easy, fast, and doesn’t require a lot of
thought
Unfortunately it is, at best, a wildly inefficient method of
debugging, and at worst, actively detrimental
If you don’t know what you need to fix yet, you either
haven’t:
Defined what you are attempting to do clearly enough, or
Understood / tracked your program well enough to know what it is
currently doing
Strategy #6
Talk it out.
Explaining things verbally, in plain English, uncovers a shocking
amount of misconceptions or mistakes
Find someone to talk at about your programming issues
It isn’t even important that they understand how to code, or even
can talk back to you (though that might help in some cases)
Rubber
Duck Debugging is where a software developer explains an issue out
loud to an inanimate rubber duck
Strategy #7
Test your code as you go! Either manually or automatically.
Know that everyone makes mistakes. The longer you go
without testing that something in your program works, the harder it is
to find where the mistake eventually is.
Write code that you test in small pieces as you go
Decomposition into smaller functions is great for this: test each
function individually as you go
In the projects we try to construct the Milestones for this exact
same purpose