from testlight import *
# program tracing practice
def count_char(find_char: str, words: list) -> list:
"""count how many times a given char appears in a list of words"""
count = 0
for w in words:
for char in w:
# memory point 2 (when processing the "u" in "purple")
if char == find_char:
count = count + 1
return count
def count_tests():
"""tests to show expected results of function"""
test(count_char("p", ["happy", "purple"]), 4)
test(count_char("p", ["summer"]), 0)
# memory point 1
count_char("p", ["happy", "purple"])
# memroy point 3
Environment (Known Names)
count_char => function
count_tests => function
----------(temp area)--------
find_char => "p"
words => loc 1000
Python Heap
label | slot |
---|---|
1000 | ["happy" |
1001 | "purple"] |
1002 | " " |
Environment (Known Names)
count_char => function
count_tests => function
----------(temp area)--------
find_char => "p"
words => loc 1000
count => 0
w => "happy" # @ line 8 (first time through)
char => "h" # @ line 9 (first time through)
char => "a" # @ line 9 (second time through)
char => "p" # @ line 9 (third time through)
count => 1
char => "p" # @ line 9 (fourth time through)
count => 2
char => "y" # @ line 9 (fifth time through)
w => "purple" # @ line 8 (second time through)
char => "p" # @ line 9 (sixth time through)
count => 3
char => "u" # @ line 9 (seventh time through)
Python Heap
label | slot |
---|---|
1000 | ["happy" |
1001 | "purple"] |
1002 | " " |
Environment (Known Names)
count_char => function
count_tests => function
----------(temp area)--------
find_char => "p"
words => loc 1000
w => "purple"
char => "u"
count => 3
Python Heap
label | slot |
---|---|
1000 | ["happy" |
1001 | "purple"] |
1002 | " " |
Once the call is completed, the temporary area is erased.
Environment (Known Names)
count_char => function
count_tests => function
Python Heap
label | slot |
---|---|
1000 | ["happy" |
1001 | "purple"] |
1002 | " " |
Professional programmers don't hand trace their programs.
Okay... maybe some do, but you don't have to! Since we're working with PyCharm, there's a nice built-in feature for doing this with your programs.
Under the Run menu, there is an option to "Step-through" the program. Click this to open the debugger panel.
There are a set of icons under the debugger panel (at the bottom of the PyCharm window).
from testlight import *
line is highlighted.The below code was copied from last class. Would the result produce the same value for each different update_duedate
function?
from typing import List
from testlight import *
from dataclasses import dataclass
from datetime import date
@dataclass
class ToDoItem:
descr: str
due: date
tags: List[str]
milkTD = ToDoItem("buy milk", date(2019, 7, 27), ["shopping", "home"])
gradeTD = ToDoItem("grade hwk", date(2019, 7, 27), ["teaching"])
myTDL = [milkTD,
gradeTD,
ToDoItem("meet students", date(2019, 7, 26), ["research"])
]
spouseTDL = [milkTD]
# memory point 1
def find_items(term: str, TDL: List[ToDoItem]) -> List[ToDoItem]:
return list(filter(lambda item: term in item.descr, TDL))
def update_duedate1(des: str, new_date: date, TDL: List[ToDoItem]) -> List[ToDoItem]:
def update_item(item: ToDoItem) -> ToDoItem:
if des == item.descr:
return ToDoItem(des, new_date, item.tags)
else:
return item
return list(map(update_item, TDL))
def update_duedate2(des: str, new_date: date, TDL: List[ToDoItem]) -> List[ToDoItem]:
"""change the due date on the item with given descr"""
item = find_items(des, TDL)[0]
TDL.remove(item)
TDL.append(ToDoItem(des, new_date, item.tags))
return TDL
def update_duedate3(des: str, new_date: date, TDL: List[ToDoItem]) -> List[ToDoItem]:
"""change the due date on the item with given descr"""
item = find_items(des, TDL)[0]
item.due = new_date
return TDL
print(myTDL)
print("\n")
update_duedate1("buy milk", date(2018, 8, 5), myTDL)
print(myTDL)
print("\n")
print("milkTD due date is " + str(milkTD.due))
print("spouseTD is " + str(spouseTDL))
print(myTDL)
print("\n")
update_duedate2("buy milk", date(2018, 8, 5), myTDL)
print(myTDL)
print("\n")
print("milkTD due date is " + str(milkTD.due))
print("spouseTD is " + str(spouseTDL))
print(myTDL)
print("\n")
update_duedate3("buy milk", date(2018, 8, 5), myTDL)
print(myTDL)
print("\n")
print("milkTD due date is " + str(milkTD.due))
print("spouseTD is " + str(spouseTDL))
update_duedate
function¶Looks like update_duedate1
didn't update the due date, update_duedate2
updated and moved the milk todo item to the end of the list, and update_duedate3
updated the milk in place. These are three very different behaviors!
update_duedate3
¶Before running this function, we will have Environment (Known Names)
milkTD => loc 1004
gradeTD => loc 1008
myTDL => loc 1013
spoundsTDL => loc 1017
----------(temp area)--------
Python Heap
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1000, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [loc 1004, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | ] |
1017 | [loc 1004, |
1018 | ] |
As an exercise we asked, at the prompt, how could we get "buy milk"? Some ways to do this were:
>>> "buy milk" # the obvious answer
"buy milk"
>>> milkTD.descr
"buy milk"
>>> myTDL[0].descr
"buy milk"
>>> spouseTDL[0].descr
"buy milk"
Notice that all of these ended up at the same location in memory (loc 1004
).
However, if we were to write:
>>> milkTD.due
datetime.date(2019, 7, 27)
>>> gradeTD.due
datetime.date(2019, 7, 27)
While these return the same value, they don't point to the same place in our heap. gradeTD.due is in loc 1009
while milkTD.due is in loc 1005
!
This illustrates that, in programming, there are two differnt possible meanings of equality.
Now, if we call update_duedate3
.
Environment (Known Names)
milkTD => loc 1004
gradeTD => loc 1008
myTDL => loc 1013
spoundsTDL => loc 1017
----------(temp area)--------
des => "buy milk"
new_date => loc 1019
TDL => loc 1013
item => 1004
Python Heap
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1000, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [loc 1004, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | ] |
1017 | [loc 1004, |
1018 | ] |
1019 | date(2018, 8, 5) |
1020 | [loc 1004] # result of filter |
1011 |
|
1022 |
|
We then alter the information at loc 1004
(which, before the call, is ToDoItem("buy milk", loc 1000, loc 1001)
). We change this to ToDoItem("buy milk", loc 1019, loc 1001)
, so the heap will now look like:
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1020, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [loc 1004, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | ] |
1017 | [loc 1004, |
1018 | ] |
1019 | date(2018, 8, 5) |
1020 | [loc 1004] # result of filter |
1021 |
|
1022 |
|
update_duedate2
¶First, the reference to loc 1004
in loc 1013
is eliminated. A new ToDoItem is also created in loc 1021
, resulting in:
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1020, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | ] |
1017 | [loc 1004, |
1018 | ] |
1019 | date(2018, 8, 5) |
1020 | [loc 1004] # result of filter |
1021 | ToDoItem("buy milk", loc 1019, loc 1001 |
1022 |
|
A reference to this new item is created, which results in:
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1020, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | loc 1021] |
1017 | [loc 1004, |
1018 | ] |
1019 | date(2018, 8, 5) |
1020 | [loc 1004] # result of filter |
1021 | ToDoItem("buy milk", loc 1019, loc 1001 |
1022 |
|
Where loc 1016
now points to the new ToDoItem at loc 1021
.
update_duedate1
¶When we do map, we create a new list. We're not doing this in "gory" detail, so map is condensed to one location.
label | slot |
---|---|
1000 | date(2019, 7, 27) |
1001 | ["shopping", |
1002 | "home" |
1003 | ] |
1004 | ToDoItem("buy milk", loc 1020, loc 1001) |
1005 | date(2019, 7, 27) |
1006 | ["teaching", |
1007 | ] |
1008 | ToDoItem("grade hwk", loc 1005, loc 1006) |
1009 | date(2019, 7, 26) |
1010 | ["research, |
1011 | ] |
1012 | ToDoItem("meet students", loc 1009, loc 1010) |
1013 | [, |
1014 | loc 1008, |
1015 | loc 1012 |
1016 | loc 1021] |
1017 | [loc 1004, |
1018 | ] |
1019 | date(2018, 8, 5) |
1020 | [loc 1004] # result of filter |
1021 | ToDoItem("buy milk", loc 1019, loc 1001 |
1022 | [loc 1008, loc 1012, loc 1026] |
The assignment of loc 1026
is arbitrary, the point here is that we return loc 1022
from update_duedate1
. Nothing in the map call of update_duedate1
touches the original data at loc 1013
. Hence, when we run it we won't observe any difference.
However, we can fix this problem in a simple step.
def update_duedate1(des: str, new_date: date, TDL: List[ToDoItem]) -> List[ToDoItem]:
def update_item(item: ToDoItem) -> ToDoItem:
if des == item.descr:
return ToDoItem(des, new_date, item.tags)
else:
return item
TDL = list(map(update_item, TDL))
return TDL
update_duedate1("buy milk", date(2018, 8, 5), myTDL)