Accumulation tables are a powerful tool for understanding the behaviour of for loops, but they also can be a bit time-consuming to create when given large quantities of data, or for more complex loops. So to make it easier to use accumulation tables as part of your code-writing process, PythonTA provides a way of generating and displaying loop accumulation tables!
Let’s illustrate using PythonTA in this way by modifying our
product function from 5.4
Repeated Execution: For Loops.
from python_ta.debug import AccumulationTable # New import!
def product(numbers: list[int]) -> int:
"""Return the product of the given numbers.
>>> product([10, 20])
200
>>> product([-5, 4])
-20
"""
# ACCUMULATOR product_so_far: keep track of the product
# of the elements in numbers seen so far in the loop.
product_so_far = 1
with AccumulationTable(['product_so_far']): # New line!
for number in numbers:
product_so_far = product_so_far * number
return product_so_farNow, here’s what gets printed when we call
print(product([10, 20, 30, 40])) in the main block:
iteration number product_so_far
----------- -------- ----------------
0 N/A 1
1 10 10
2 20 200
3 30 6000
4 40 240000
240000
The very last line, containing just 240000, is the
function’s return value, as we would normally expect to see when
printing the result of a function call. The part above it is what’s
interesting: a text-based representation of an accumulation table,
displaying the loop iteration number (0 to 4), the value of the loop
variable number at each iteration, and the value of the
accumulator product_so_far.
Pretty cool! Now let’s take a few moments to discuss how this new code is structured.
This code imports AccumulationTable, a custom data
type defined by PythonTA. What does this data type do, exactly?
When we write AccumulationTable(['product_so_far']), we are
creating a new Python value that will expect to “wrap around” a for loop
and keep track of the accumulator variable product_so_far
at each loop iteration.
We achieve this “wrap around” behaviour through a new form of Python syntax called a with statement: Yet another form of compound statement, like if statements and for loops!
with <value>:
<statement1>
<statement2>
...For our purposes, with statements are similar to decorators:
In general, with statements offer programmers the ability to modify
the behaviour of a block of code. Using
with AccumulationTable(['product_so_far']): to wrap the for
loop, the Python interpreter does the following:
AccumulationTable(['product_so_far']),
producing a value of the AccumulationTable data type that
is intended to track the accumulator variable
product_so_far.AccumulationTable value
records the iteration number, value of the loop variable, and value of
the product_so_far accumulator.AccumulationTable prints a text-based table of the
values it recorded in Step 2.One subtlety with AccumulationTable is that even though
it records data in a tabular form, it prints the table to the
terminal, rather than making that data available in code. To see what we
mean by this, let’s change our code in the main block, now using an
assignment statement to store product’s return value:
result = product([10, 20, 30, 40]). Running the program now
produces the following in the terminal:
iteration number product_so_far
----------- -------- ----------------
0 N/A 1
1 10 10
2 20 200
3 30 6000
4 40 240000
Now, even though we are storing the result of the function call in
the result variable rather than printing it, the
accumulation table is still displayed immediately. The return value of
the function is the value 240000, but the accumulation
table isn’t part of that return value. Using
AccumulationTable, we can see the accumulation table for a
given for loop, but we cannot access or compute with the underlying
data, just look at
it. Okay, technically there is a way to access the data
recorded by the AccumulationTable, but that’s beyond the
scope of this course. Please feel free to ask about this during office
hours! This illustrates one of the key limitations of “printing”
data: we see that data immediately in the terminal, but can’t access it
directly. In CSC110/111, we’ll use printing techniques like
AccumulationTable as a way to gain information about our
code, which is especially helpful when debugging. However, printing
should never replace the usual data flow of storing the results of
computations in variables and returning values from functions! And one
final warning:
Warning:
python_ta.debug.AccumulationTable is a useful tool for
debugging purposes, and we hope you make use of it when completing your
work for CSC110/111. However, because the accumulation table prints
debugging text to the terminal, it violates the principle of function
correctness: a function’s body should do exactly what the function
specification says, no more and no less.
That means that before submitting any graded work for this course,
you should always remove the with statement containing
AccumulationTable (and unindent the for loop contained
within) from your final submission!