Write code to assign to the variable map_testing all the elements in lst_check while adding the string Fruit: to the beginning of each element using mapping.
+
+
+
+
+lst_check = ['plums', 'watermelon', 'kiwi', 'strawberries', 'blueberries', 'peaches', 'apples', 'mangos', 'papaya']
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(map_testing, ['Fruit: plums', 'Fruit: watermelon', 'Fruit: kiwi', 'Fruit: strawberries', 'Fruit: blueberries', 'Fruit: peaches', 'Fruit: apples', 'Fruit: mangos', 'Fruit: papaya'], "Testing that map_testing has the correct values.")
+ self.assertIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
+
+
+
Below, we have provided a list of strings called countries. Use filter to produce a list called b_countries that only contains the strings from countries that begin with B.
+
+
+
+
+countries = ['Canada', 'Mexico', 'Brazil', 'Chile', 'Denmark', 'Botswana', 'Spain', 'Britain', 'Portugal', 'Russia', 'Thailand', 'Bangladesh', 'Nigeria', 'Argentina', 'Belarus', 'Laos', 'Australia', 'Panama', 'Egypt', 'Morocco', 'Switzerland', 'Belgium']
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(b_countries, ['Brazil', 'Botswana', 'Britain', 'Bangladesh', 'Belarus', 'Belgium'], "Testing that b_countries is correct.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
+
+
+
Below, we have provided a list of tuples that contain the names of Game of Thrones characters. Using list comprehension, create a list of strings called first_names that contains only the first names of everyone in the original list.
+
+
+
+
+people = [('Snow', 'Jon'), ('Lannister', 'Cersei'), ('Stark', 'Arya'), ('Stark', 'Robb'), ('Lannister', 'Jamie'), ('Targaryen', 'Daenerys'), ('Stark', 'Sansa'), ('Tyrell', 'Margaery'), ('Stark', 'Eddard'), ('Lannister', 'Tyrion'), ('Baratheon', 'Joffrey'), ('Bolton', 'Ramsey'), ('Baelish', 'Peter')]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(first_names, ['Jon', 'Cersei', 'Arya', 'Robb', 'Jamie', 'Daenerys', 'Sansa', 'Margaery', 'Eddard', 'Tyrion', 'Joffrey', 'Ramsey', 'Peter'], "Testing that first_names is correct.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
+
Use list comprehension to create a list called lst2 that doubles each element in the list, lst.
+
+
+
+
+lst = [["hi", "bye"], "hello", "goodbye", [9, 2], 4]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testFiveA(self):
+ self.assertEqual(lst2, [['hi', 'bye', 'hi', 'bye'], 'hellohello', 'goodbyegoodbye', [9, 2, 9, 2], 8], "Testing that lst2 is assigned to correct values")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
+
Below, we have provided a list of tuples that contain students' names and their final grades in PYTHON 101. Using list comprehension, create a new list passed that contains the names of students who passed the class (had a final grade of 70 or greater).
+
+
+
+
+students = [('Tommy', 95), ('Linda', 63), ('Carl', 70), ('Bob', 100), ('Raymond', 50), ('Sue', 75)]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(passed, ['Tommy', 'Carl', 'Bob', 'Sue'], "Testing that passed is correct.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
+
Write code using zip and filter so that these lists (l1 and l2) are combined into one big list and assigned to the variable opposites if they are both longer than 3 characters each.
+
+
+
+
+
+l1 = ['left', 'up', 'front']
+l2 = ['right', 'down', 'back']
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(opposites, [('left','right'), ('front','back')], "Testing that opposites has the correct list of tuples.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
+
+
+
Below, we have provided a species list and a population list. Use zip to combine these lists into one list of tuples called pop_info. From this list, create a new list called endangered that contains the names of species whose populations are below 2500.
Now consider another common pattern: going through a list and keeping only those items that meet certain criteria.
+ This is called a filter.
+
+
+def keep_evens(nums):
+ new_list = []
+ for num in nums:
+ if num % 2 == 0:
+ new_list.append(num)
+ return new_list
+
+print(keep_evens([3, 4, 6, 7, 0, 1]))
+
+
+
Again, this pattern of computation is so common that Python offers a more compact and general way to do it, the filter
+ function. filter takes two arguments, a function and a sequence. The function takes one item and return True if the
+ item should. It is automatically called for each item in the sequence. You don't have to initialize an accumulator or
+ iterate with a for loop.
1. Write code to assign to the variable filter_testing all the elements in lst_check that have a w in them using filter.
+
+
+
+lst_check = ['plums', 'watermelon', 'kiwi', 'strawberries', 'blueberries', 'peaches', 'apples', 'mangos', 'papaya']
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(filter_testing, ['watermelon', 'kiwi', 'strawberries'], "Testing that filter_testing has the correct values.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
+
+
2. Using filter, filter lst so that it only contains words containing the letter o. Assign to variable lst2. Do not hardcode this.
+
+
+
+lst = ["witch", "halloween", "pumpkin", "cat", "candy", "wagon", "moon"]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testTwo(self):
+ self.assertEqual(lst2, ['halloween', 'wagon', 'moon'], "Testing that lst is assigned to correct values.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
diff --git a/pretext/AdvancedAccumulation/intro.ptx b/pretext/AdvancedAccumulation/intro.ptx
new file mode 100644
index 00000000..4e707073
--- /dev/null
+++ b/pretext/AdvancedAccumulation/intro.ptx
@@ -0,0 +1,18 @@
+
+
+ Introduction: Map, Filter, List Comprehensions, and Zip
+
Let's revisit the . We have frequently taken a list and produced another list
+ from it that contains either a subset of the items or a transformed version of each item. When each item is transformed we
+ say that the operation is a mapping, or just a map of the original list. When some items are omitted, we call it a
+ filter.
+
Python provides built-in functions map and filter. Python also provides a new syntax, called
+ list comprehensions, that lets you express a mapping and/or filtering operation. Just as with named functions and
+ lambda expressions, some students seem to find it easier to think in terms of the map and filter functions, while other
+ students find it easier to read and write list comprehensions. You'll learn both ways; one may even help you understand
+ the other. Most python programmers use list comprehensions, so make sure you learn to read those. In this course, you can
+ choose to learn to write list comprehensions or to use map and filter, whichever you prefer. You should learn to read both
+ list comprehensions and map/filter.
+
Other common accumulator patterns on lists aggregate all the values into a single value.
+
Map, and filter are commands that you would use in high-performance computing on big datasets.
+ See MapReduce on Wikipedia.
+
diff --git a/pretext/AdvancedAccumulation/listcomp.ptx b/pretext/AdvancedAccumulation/listcomp.ptx
new file mode 100644
index 00000000..f339b7d5
--- /dev/null
+++ b/pretext/AdvancedAccumulation/listcomp.ptx
@@ -0,0 +1,158 @@
+
+
+ List Comprehensions
+
Python provides an alternative way to do map and filter operations, called a list comprehension.
+ Many programmers find them easier to understand and write. List comprehensions are concise ways to create lists from other
+ lists. The general syntax is:
+
[<transformer_expression> for <loop_var> in <sequence> if <filtration_expression>]
+
where the if clause is optional. For example,
+
+
+things = [2, 5, 9]
+
+yourlist = [value * 2 for value in things]
+
+print(yourlist)
+
+
+
The transformer expression is value * 2. The item variable is value and the sequence is things. This is an alternative way
+ to perform a mapping operation. As with map, each item in the sequence is transformed into an item in the new list.
+ Instead of the iteration happening automatically, however, we have adopted the syntax of the for loop which may make it
+ easier to understand.
+
Just as in a regular for loop, the part of the statement for value in things says to execute some code once for each
+ item in things. Each time that code is executed, value is bound to one item from things. The code that is executed
+ each time is the transformer expression, value * 2, rather than a block of code indented underneath the for
+ statement. The other difference from a regular for loop is that each time the expression is evaluated, the resulting value
+ is appended to a list. That happens automatically, without the programmer explicitly initializing an empty list or
+ appending each item.
+
The if clause of a list comprehension can be used to do a filter operation. To perform a pure filter operation, the
+ expression can be simply the variable that is bound to each item. For example, the following list comprehension will keep
+ only the even numbers from the original list.
+
+
+def keep_evens(nums):
+ new_list = [num for num in nums if num % 2 == 0]
+ return new_list
+
+print(keep_evens([3, 4, 6, 7, 0, 1]))
+
+
+
You can also combine map and filter operations by chaining them together, or with a single list comprehension.
+
+
+things = [3, 4, 6, 7, 0, 1]
+#chaining together filter and map:
+# first, filter to keep only the even numbers
+# double each of them
+print(map(lambda x: x*2, filter(lambda y: y % 2 == 0, things)))
+
+# equivalent version using list comprehension
+print([x*2 for x in things if x % 2 == 0])
+
+
+
+ Check your understanding
+
+
+
+
What is printed by the following statements?
+
+
+alist = [4,2,8,6,5]
+blist = [num*2 for num in alist if num%2==1]
+print(blist)
+
+
+
+
+
+
+
[4,2,8,6,5]
+
+
+
Items from alist are doubled before being placed in blist.
+
+
+
+
+
[8,4,16,12,10]
+
+
+
Not all the items in alist are to be included in blist. Look at the if clause.
+
+
+
+
+
10
+
+
+
The result needs to be a list.
+
+
+
+
+
[10]
+
+
+
Yes, 5 is the only odd number in alist. It is doubled before being placed in blist.
+
+
+
+
+
+
+
2. The for loop below produces a list of numbers greater than 10. Below the given code, use list comprehension to accomplish the same thing. Assign it the the variable lst2. Only one line of code is needed.
+
+
+
+L = [12, 34, 21, 4, 6, 9, 42]
+lst = []
+for x in L:
+ if x > 10:
+ lst.append(x)
+print(lst)
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testFourA(self):
+ self.assertEqual(lst2, [12, 34, 21, 42], "Testing that lst2 is assigned to correct values")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
3. Write code to assign to the variable compri all the values of the key name in any of the sub-dictionaries in the dictionary tester. Do this using a list comprehension.
+
+
+
+tester = {'info': [{"name": "Lauren", 'class standing': 'Junior', 'major': "Information Science"},{'name': 'Ayo', 'class standing': "Bachelor's", 'major': 'Information Science'}, {'name': 'Kathryn', 'class standing': 'Senior', 'major': 'Sociology'}, {'name': 'Nick', 'class standing': 'Junior', 'major': 'Computer Science'}, {'name': 'Gladys', 'class standing': 'Sophomore', 'major': 'History'}, {'name': 'Adam', 'major': 'Violin Performance', 'class standing': 'Senior'}]}
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sorted(compri), sorted(['Lauren', 'Ayo', 'Kathryn', 'Nick', 'Gladys', 'Adam']), "Testing that compri has the correct values.")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/AdvancedAccumulation/map.ptx b/pretext/AdvancedAccumulation/map.ptx
new file mode 100644
index 00000000..31287b4f
--- /dev/null
+++ b/pretext/AdvancedAccumulation/map.ptx
@@ -0,0 +1,141 @@
+
+
+ Map
+
You previously were introduced to accumulating a list by transforming each of the elements. Here we revisit that pattern.
+
The following function produces a new list with each item in the original list doubled. It is an example of a mapping,
+ from the original list to a new list of the same length, where each element is doubled.
+
+
+def doubleStuff(a_list):
+ """ Return a new list in which contains doubles of the elements in a_list. """
+ new_list = []
+ for value in a_list:
+ new_elem = 2 * value
+ new_list.append(new_elem)
+ return new_list
+
+things = [2, 5, 9]
+print(things)
+things = doubleStuff(things)
+print(things)
+
+
+
The doubleStuff function is an example of the accumulator pattern, in particular the mapping pattern. On line 3,
+ new_list is initialized. On line 5, the doubled value for the current item is produced and on line 6 it is appended to
+ the list we're accumulating. Line 7 executes after we've processed all the items in the original list: it returns the
+ new_list. Once again, codelens helps us to see the actual references and objects as they are passed and returned.
+
+
+def doubleStuff(a_list):
+ """ Return a new list in which contains doubles of the elements in a_list. """
+ new_list = []
+ for value in a_list:
+ new_elem = 2 * value
+ new_list.append(new_elem)
+ return new_list
+
+things = [2, 5, 9]
+things = doubleStuff(things)
+
+
+
This pattern of computation is so common that python offers a more general way to do mappings, the map function, that
+ makes it more clear what the overall structure of the computation is. map takes two arguments, a function and a
+ sequence. The function is the mapper that transforms items. It is automatically applied to each item in the sequence. You
+ don't have to initialize an accumulator or iterate with a for loop at all.
+
+
Technically, in a proper Python 3 interpreter, the map function produces an iterator, which is like a list but
+ produces the items as they are needed. Most places in Python where you can use a list (e.g., in a for loop) you can
+ use an iterator as if it was actually a list. So you probably won't ever notice the difference. If you ever really
+ need a list, you can explicitly turn the output of map into a list: list(map(...)). In the runestone environment, map actually returns a real list, but to make this code compatible with a full python environment, we always convert it to a list.
+
+
As we did when passing a function as a parameter to the sorted function, we can specify a function to pass to map
+ either by referring to a function by name, or by providing a lambda expression.
Of course, once we get used to using the map function, it's no longer necessary to define functions like
+ tripleStuff and quadrupleStuff.
+
+
+things = [2, 5, 9]
+
+things4 = map((lambda value: 4*value), things)
+print(list(things4))
+
+# or all on one line
+print(list(map((lambda value: 5*value), [1, 2, 3])))
+
+
+
+ Check Your Understanding
+
+
+
+
1. Using map, create a list assigned to the variable greeting_doubled that doubles each element in the list lst.
+
+
+
+lst = [["hi", "bye"], "hello", "goodbye", [9, 2], 4]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOneA(self):
+ self.assertEqual(greeting_doubled, [['hi', 'bye', 'hi', 'bye'], 'hellohello', 'goodbyegoodbye', [9, 2, 9, 2], 8], "Testing that greeting_doubled is assigned to correct values")
+ self.assertIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
+
+
2. Below, we have provided a list of strings called abbrevs. Use map to produce a new list called abbrevs_upper that contains all the same strings in upper case.
+
+
+
+abbrevs = ["usa", "esp", "chn", "jpn", "mex", "can", "rus", "rsa", "jam"]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(abbrevs_upper, ["USA", "ESP", "CHN", "JPN", "MEX", "CAN", "RUS", "RSA", "JAM"], "Testing that abbrevs_upper is correct.")
+ self.assertIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+
+myTests().main()
+
+
+
+
diff --git a/pretext/AdvancedAccumulation/toctree.ptx b/pretext/AdvancedAccumulation/toctree.ptx
new file mode 100644
index 00000000..444f129e
--- /dev/null
+++ b/pretext/AdvancedAccumulation/toctree.ptx
@@ -0,0 +1,11 @@
+
+
+ More on Accumulation: Map, Filter, List Comprehension, and Zip
+
+
+
+
+
+
+
+
diff --git a/pretext/AdvancedAccumulation/zip.ptx b/pretext/AdvancedAccumulation/zip.ptx
new file mode 100644
index 00000000..e5d17812
--- /dev/null
+++ b/pretext/AdvancedAccumulation/zip.ptx
@@ -0,0 +1,161 @@
+
+
+ Zip
+
One more common pattern with lists, besides accumulation, is to step through a pair of lists (or several lists), doing
+ something with all of the first items, then something with all of the second items, and so on. For example, given two
+ lists of numbers, you might like to add them up pairwise, taking [3, 4, 5] and [1, 2, 3] to yield [4, 6, 8].
+
One way to do that with a for loop is to loop through the possible index values.
You have seen this idea previously for iterating through the items in a single list. In many other programming languages
+ that's really the only way to iterate through the items in a list. In Python, however, we have gotten used to the for loop
+ where the iteration variable is bound successively to each item in the list, rather than just to a number that's used as a
+ position or index into the list.
+
Can't we do something similar with pairs of lists? It turns out we can.
+
The zip function takes multiple lists and turns them into a list of tuples (actually, an iterator, but they work like
+ lists for most practical purposes), pairing up all the first items as one tuple, all the second items as a tuple, and so
+ on. Then we can iterate through those tuples, and perform some operation on all the first items, all the second items, and
+ so on.
Consider a function called possible, which determines whether a word is still possible to play in a game of hangman, given the guesses that have been made and the current state of the blanked word.
+
Below we provide function that fulfills that purpose.
+
+
+def possible(word, blanked, guesses_made):
+ if len(word) != len(blanked):
+ return False
+ for i in range(len(word)):
+ bc = blanked[i]
+ wc = word[i]
+ if bc == '_' and wc in guesses_made:
+ return False
+ elif bc != '_' and bc != wc:
+ return False
+ return True
+
+print(possible("wonderwall", "_on__r__ll", "otnqurl"))
+print(possible("wonderwall", "_on__r__ll", "wotnqurl"))
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(possible("HELLO", "_ELL_", "ELJ"), True, "Testing whether possible has been correctly defined.")
+ self.assertEqual(possible("HELLO", "_ELL_", "ELJH"), False, "Testing whether possible has been correctly defined.")
+ self.assertEqual(possible("HELLO", "_E___", "ELJ"), False, "Testing whether possible has been correctly defined.")
+
+myTests().main()
+
+
+
However, we can rewrite that using zip, to be a little more comprehensible.
+
+
+def possible(word, blanked, guesses_made):
+ if len(word) != len(blanked):
+ return False
+ for (bc, wc) in zip(blanked, word):
+ if bc == '_' and wc in guesses_made:
+ return False
+ elif bc != '_' and bc != wc:
+ return False
+ return True
+
+print(possible("wonderwall", "_on__r__ll", "otnqurl"))
+print(possible("wonderwall", "_on__r__ll", "wotnqurl"))
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(possible("HELLO", "_ELL_", "ELJ"), True, "Testing whether possible has been correctly defined.")
+ self.assertEqual(possible("HELLO", "_ELL_", "ELJH"), False, "Testing whether possible has been correctly defined.")
+ self.assertEqual(possible("HELLO", "_E___", "ELJ"), False, "Testing whether possible has been correctly defined.")
+
+myTests().main()
+
+
+
+ Check Your Understanding
+
+
+
+
1. Below we have provided two lists of numbers, L1 and L2. Using zip and list comprehension, create a new list, L3, that sums the two numbers if the number from L1 is greater than 10 and the number from L2 is less than 5. This can be accomplished in one line of code.
+
+
+
+L1 = [1, 5, 2, 16, 32, 3, 54, 8, 100]
+L2 = [1, 3, 10, 2, 42, 2, 3, 4, 3]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testSix(self):
+ self.assertEqual(L3, [18, 57, 103], "Testing that L3 is assigned to correct values")
+ self.assertNotIn('map(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('filter(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertNotIn('sum(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+ self.assertIn('zip(', self.getEditorText(), "Testing your code (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/AdvancedFunctions/Anonymousfunctionswithlambdaexpressions.ptx b/pretext/AdvancedFunctions/Anonymousfunctionswithlambdaexpressions.ptx
new file mode 100644
index 00000000..7eb3e331
--- /dev/null
+++ b/pretext/AdvancedFunctions/Anonymousfunctionswithlambdaexpressions.ptx
@@ -0,0 +1,100 @@
+
+
+ Anonymous functions with lambda expressions
+
To further drive home the idea that we are passing a function object as a parameter to the sorted object,
+ let's see an alternative notation for creating a function, a lambda expression. The syntax of a lambda
+ expression is the word lambda followed by parameter names, separated by commas but not inside (parentheses),
+ followed by a colon and then an expression. lambda arguments: expression yields a function object. This
+ unnamed object behaves just like the function object constructed below.
Note the paralells between the two. At line 4, f is bound to a function object. Its printed representation
+ is <function f>. At line 8, the lambda expression produces a function object. Because it is unnamed (anonymous),
+ its printed representation doesn't include a name for it, <function <lambda>>. Both are of type ‘function'.
+
A function, whether named or anonymous, can be called by placing parentheses () after it.
+ In this case, because there is one parameter, there is one value in parentheses. This
+ works the same way for the named function and the anonymous function produced by the lambda
+ expression. The lambda expression had to go in parentheses just for the purposes
+ of grouping all its contents together. Without the extra parentheses around it on line 10,
+ the interpreter would group things differently and make a function of x that returns x - 2(6).
+
Some students find it more natural to work with lambda expressions than to refer to a function
+ by name. Others find the syntax of lambda expressions confusing. It's up to you which version you want to
+ use though you will need to be able to read and understand lambda expressions that are written by others.
+ In all the examples below, both ways of doing it will be illustrated.
+
Say we want to create a function that takes a string and returns the last character in that string. What might this look
+ like with the functions you've used before?
+
+
+def last_char(s):
+ return s[-1]
+
+
+
To re-write this using lambda notation, we can do the following:
+
+
+last_char = (lambda s: s[-1])
+
+
+
Note that neither function is actually invoked. Look at the parallels between the two structures. The parameters are
+ defined in both functions with the variable s. In the typical function, we have to use the keyword return to send
+ back the value. In a lambda function, that is not necessary - whatever is placed after the colon is what will be returned.
+
+ Check Your Understanding
+
+
+
+
If the input to this lambda function is a number, what is returned?
+
+
+(lambda x: -x)
+
+
+
+
+
+
+
A string with a - in front of the number.
+
+
+
The number would be assigned to the variable x and there is no type conversion used here, so the number would stay a number.
+
+
+
+
+
A number of the opposite sign (positive number becomes negative, negative becomes positive).
+
+
+
Correct!
+
+
+
+
+
Nothing is returned because there is no return statement.
+
+
+
Remember, lambda functions do not use return statements.
Create a function called mult that has two parameters, the first is required and should be an integer, the second is an optional parameter that can either be a number or a string but whose default is 6. The function should return the first parameter multiplied by the second.
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(mult(2), 12, "Testing that mult returns the correct value on input (2)")
+ self.assertEqual(mult(5,3), 15, "Testing that mult returns the correct value on input (3,5)")
+ self.assertEqual(mult(4,"hello"), "hellohellohellohello", "testing that mult returns the correct value on input (4, 'hello'")
+
+myTests().main()
+
+
+
+
+
+
The following function, greeting, does not work. Please fix the code so that it runs without error. This only requires one change in the definition of the function.
Below is a function, sum, that does not work. Change the function definition so the code works. The function should still have a required parameter, intx, and an optional parameter, intz with a defualt value of 5.
+
+
+
+def sum(intz=5, intx):
+ return intz + intx
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sum(8, intz = 2), 10, "Testing the function sum on inputs 8, 2.")
+ self.assertEqual(sum(12), 17, "Testing the function sum on input 12.")
+
+myTests().main()
+
+
+
+
+
+
Write a function, test, that takes in three parameters: a required integer, an optional boolean whose default value is True, and an optional dictionary, called dict1, whose default value is {2:3, 4:5, 6:8}. If the boolean parameter is True, the function should test to see if the integer is a key in the dictionary. The value of that key should then be returned. If the boolean parameter is False, return the boolean value False.
Write a function called checkingIfIn that takes three parameters. The first is a required parameter, which should be a string. The second is an optional parameter called direction with a default value of True. The third is an optional parameter called d that has a default value of {'apple': 2, 'pear': 1, 'fruit': 19, 'orange': 5, 'banana': 3, 'grapes': 2, 'watermelon': 7}. Write the function checkingIfIn so that when the second parameter is True, it checks to see if the first parameter is a key in the third parameter; if it is, return True, otherwise return False.
+
But if the second paramter is False, then the function should check to see if the first parameter is not a key of the third. If it's not, the function should return True in this case, and if it is, it should return False.
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(checkingIfIn('grapes'), True, "Testing that checkingIfIn returns the correct boolean on input 'grapes'")
+ self.assertEqual(checkingIfIn('carrots'), False, "Testing that checkingIfIn returns the correct boolean on input 'carrots'")
+ self.assertEqual(checkingIfIn('grapes', False), False, "Testing that checkingIfIn returns the correct boolean on input ('grapes', False)")
+ self.assertEqual(checkingIfIn('carrots', False), True, "Testing that checkingIfIn returns the correct boolean on input ('carrots', False)")
+ self.assertEqual(checkingIfIn('grapes', d = {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1}), False, "Testing that checkingIfIn returns the correct boolean on input ('grapes', d = {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1})")
+ self.assertEqual(checkingIfIn('peas', d = {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1}), True, "Testing that checkingIfIn returns the correct boolean on input ('peas', d = {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1})")
+ self.assertEqual(checkingIfIn('peas', False, {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1}), False, "Testing that checkingIfIn returns the correct boolean on input ('peas', False, {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1})")
+ self.assertEqual(checkingIfIn('apples', False, {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1}), True, "Testing that checkingIfIn returns the correct boolean on input ('apples', False, {'carrots': 1, 'peas': 9, 'potatos': 8, 'corn': 32, 'beans': 1})")
+
+myTests().main()
+
+
+
+
+
+
We have provided a function below and the skeleton of three invocations of the function. Fill in the parameters of the invocations to produce the specified outputs
+
+
+
+def f(x, y = 3, z = 7):
+ return ("{} {} {}".format(x, y, z))
+
+# fill in just one parameter value to make val1 have the value "Hi 3 7"
+val1 = f()
+# fill in two parameter values to make val2 have the value "Hi Hi 7"
+val2 = f()
+# fill in two parameters to make vale have the value "Hi 3 Hi"
+val3 = f()
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(val1, "Hi 3 7", "Testing that val1 has the correct value")
+ def testTwo(self):
+ self.assertEqual(val2, "Hi Hi 7", "Testing that val2 has the correct value")
+ def testThree(self):
+ self.assertEqual(val3, "Hi 3 Hi", "Testing that val3 has the correct value")
+ ### would be good to define additional tests that check to make sure student is only suppplying minimum number of parameter values
+
+
+myTests().main()
+
+
+
+
diff --git a/pretext/AdvancedFunctions/Exercises.ptx b/pretext/AdvancedFunctions/Exercises.ptx
new file mode 100644
index 00000000..7506b4b2
--- /dev/null
+++ b/pretext/AdvancedFunctions/Exercises.ptx
@@ -0,0 +1,2 @@
+
+
diff --git a/pretext/AdvancedFunctions/KeywordParameters.ptx b/pretext/AdvancedFunctions/KeywordParameters.ptx
new file mode 100644
index 00000000..ae251f04
--- /dev/null
+++ b/pretext/AdvancedFunctions/KeywordParameters.ptx
@@ -0,0 +1,2 @@
+
+
diff --git a/pretext/AdvancedFunctions/MethodInvocations.ptx b/pretext/AdvancedFunctions/MethodInvocations.ptx
new file mode 100644
index 00000000..87ecf737
--- /dev/null
+++ b/pretext/AdvancedFunctions/MethodInvocations.ptx
@@ -0,0 +1,49 @@
+
+
+ Method Invocations
+
+
This section is a review of material you have already seen, but it may be helpful to look at it again now that you're focusing on functions and function calls.
+
+
There is one other special type of function called a method, which is invoked slightly differently. Some
+ object types have methods defined for them. You have already seen some methods that operate on strings (e.g.,
+ find, index, split, join) and on lists (e.g., append, pop).
+
We will not learn about how to define methods until later in the course, when we cover Classes. But it's worth getting a
+ basic understanding now of how methods are invoked. To invoke a method, the syntax is
+ <expr>.<methodname>(<additional parameter values>).
+
The expression to the left of the dot should evaluate to an object of the correct type, an object for which <methodname>
+ is defined. The method will be applied to that object (that object will be a parameter value passed to the
+ function/method.) If the method takes additional parameters (some do, some don't), additional expressions that evaluate
+ to values are included inside the parentheses.
+
For example, let's look at an invocation of the split method.
+
+
+y = "This is a sentence"
+z = y.split()
+print(type(z))
+print(len(z))
+print(z)
+for w in z:
+ print(w)
+
+
+
The split method operates on a string. Because it is a method rather than a regular function, the string it operates on
+ appears to the left of the period, rather than inside the parentheses. The split method always returns a list. On line
+ 2, that returned value is assigned to the variable z.
+
The split method actually takes an optional extra parameter. If no value is provided inside the parentheses, the split
+ method chops up the list whenever it encounters a whitespace (a space, a tab, or a newline). But you can specifying a
+ character or character string to split on. Try putting s inside the parentheses on line 2 above, make a prediction
+ about what the output will be, and then check it. Try some other things inside the parentheses.
+
Note that the thing to the left of the period can be any expression, not just a variable name. It can even be a return
+ value from some other function call or method invocation. For example, if we want to remove the s and t characters from
+ a string, we can do it all on one line as show below.
+
+
+print("This is a sentence".replace("s", "").replace("t", ""))
+
+
+
What's going on there? Start reading left to right. This is a sentence is a string, and the replace method is invoked
+ on it. Two additional parameter values are provided, s, and an empty string. So, in the sentence, all occurrences of
+ s are replaced with the empty string. A new string is returned, Thi i a entence. There is another period followed
+ by the word replace, so the replace method is called again on that string, returning the shorter string, which is
+ printed.
In the treatment of functions so far, each function definition specifies zero or more formal parameters
+ and each function invocation provides exactly that many values. Sometimes it is convenient to have
+ optional parameters that can be specified or omitted. When an optional parameter is omitted from a function invocation, the formal parameter is bound to a default value. When the optional parameter is included, then
+ the formal parameter is bound to the value provided. Optional parameters are convenient when a function is
+ almost always used in a simple way, but it's nice to allow it to be used in a more complex way, with non-default
+ values specified for the optional parameters.
+
Consider, for example, the int function, which you have used previously. Its first parameter,
+ which is required, specifies the object that you wish to convert to an integer. For example, if you
+ call in on a string, int("100"), the return value will be the integer 100.
+
That's the most common way programmers want to convert strings to integers. Sometimes, however, they
+ are working with numbers in some other base rather than base 10. For example, in base 8, the rightmost
+ digit says how many ones, the next digit to the left says how many 8s, and the one to the left of that says how many 64s (64 is 8 squared).
+
+
New Math
+
Some math educators believe that elementary school students will get a much deeper understanding
+ of the place-value system, and set a foundation for learning algebra later, if they learn to do
+ arithmetic not only in base-10 but also in base-8 and other bases. This was part of a movement
+ called New Math, though it's not so new now. It was popular in the 1960s and 1970s in the USA. One of the authors of this textbook (Resnick) had some version of it in elementary school and credits it with ruining his mind, in a good way. Tom
+ Lehrer wrote a really funny song about it in 1965, and it's now set with visuals in several YouTube renditions. Try this very nice lip-synched version.
+
+
The int function provides an optional parameter for the base. When it is not specified, the number is
+ converted to an integer assuming the original number was in base 10. We say that 10 is the default value.
+ So int("100") is the same as invoking int("100", 10). We can override the default of 10 by
+ supplying a different value.
+
+
+print(int("100"))
+print(int("100", 10)) # same thing, 10 is the default value for the base
+print(int("100", 8)) # now the base is 8, so the result is 1*64 = 64
+
+
+
When defining a function, you can specify a default value for a parameter. That parameter then becomes an
+ optional parameter when the function is called. The way to specify a default value is with an assignment
+ statement inside the parameter list. Consider the following code, for example.
Notice the different bindings of x, y, and z on the three invocations of f. The first time, y and z have
+ their default values, 3 and 7. The second time, y gets the value 5 that is passed in, but z still gets the
+ default value of 7. The last time, z gets the value 8 that is passed in.
+
If you want to provide a non-default value for the third parameter (z), you also need to provide a value
+ for the second item (y). We will see in the next section a mechanism called keyword parameters that lets you
+ specify a value for z without specifying a value for y.
+
+
This is a second, related but slightly different use of = than we have seen previously. In a stand-alone assignment statement, not part of a function definition, y=3 assigns 3 to the variable y. As part of specifying the parameters in a function definition, y=3 says that 3 is the default value for y, used only when no value is provided during the function invocation.
+
+
There are two tricky things that can confuse you with default values. The first is that the default
+ value is determined at the time that the function is defined, not at the time that it is invoked. So
+ in the example above, if we wanted to invoke the function f with a value of 10 for z, we cannot simply
+ set initial = 10 right before invoking f. See what happens in the code below, where z still gets the
+ value 7 when f is invoked without specifying a value for z.
The second tricky thing is that if the default value is set to a mutable object, such as a list or a dictionary,
+ that object will be shared in all invocations of the function. This can get very confusing, so I suggest that you
+ never set a default value that is a mutable object. For example, follow the execution of this one carefully.
When the default value is used, the same list is shared. But on lines 8 and 9 two different copies of the list
+ [Hello] are provided, so the 4 that is appended is not present in the list that is printed on line 9.
+
+ Check your understanding
+
+
+
+
What will the following code print?
+
+
+def f(x = 0, y = 1):
+ return x * y
+
+print(f())
+
+
+
+
+
+
+
0
+
+
+
Since no parameters are specified, x is 0 and y is 1, so 0 is returned.
+
+
+
+
+
1
+
+
+
0 * 1 is 0.
+
+
+
+
+
None
+
+
+
The function does return a value.
+
+
+
+
+
Runtime error since no parameters are passed in the call to f.
+
+
+
Because both parameters have default values specified in the definition, they are both optional.
+
+
+
+
+
+
+
What will the following code print?
+
+
+def f(x = 0, y = 1):
+ return x * y
+
+print(f(1))
+
+
+
+
+
+
+
0
+
+
+
Since one parameter value is specified, it is bound to x; y gets the default value of 1.
+
+
+
+
+
1
+
+
+
Since one parameter value is specified, it is bound to x; y gets the default value of 1.
+
+
+
+
+
None
+
+
+
The function does return a value.
+
+
+
+
+
Runtime error since the second parameter value is missing.
+
+
+
Because both parameters have default values specified in the definition, they are both optional.
+
+
+
+
+
+
+
3. Write a function called str_mult that takes in a required string parameter and an optional integer parameter. The default value for the integer parameter should be 3. The function should return the string multiplied by the integer parameter.
+
+ Contributed Exercises
+ {% for q in questions: %}
+ <div class='oneq full-width'>
+ {{ q['htmlsrc']|safe }}
+ </div>
+{% endfor %}
+
+
diff --git a/pretext/BuildingPrograms/TheStrategy.ptx b/pretext/BuildingPrograms/TheStrategy.ptx
new file mode 100644
index 00000000..8160911a
--- /dev/null
+++ b/pretext/BuildingPrograms/TheStrategy.ptx
@@ -0,0 +1,78 @@
+
+
+ Building A Program: A Strategy
+
Building on lessons learned in the first debugging interlude, this chapter offers a strategy for writing a program to solve a problem such as those that appear in the exercises at the ends of the chapters in this book. (A similar approach is helpful for writing larger programs, but that will come later.)
+
+ Warning.
+
You may find it tempting to start an exercise by copying and pasting a snippet of code from somewhere in the textbook, and hoping that a small edit will lead to a solution to the current problem. Often this will lead to frustration and confusion; after trying a few code substitutions that feel vaguely familiar to you, you'll find the code looking kind of complicated and the outputs baffling.
+
Copying and editing snippets of code is actually a useful element of the strategy we outline below. But it comes a little later in the process, not as the first thing. And it requires a fair bit of work to make sure you understand the code snippet that you've copied. Only then will you be able to find the right small edits to the code snippet to make it do what you want.
+
+
There are three basic steps to the strategy we recommend: Outline; Code One Section at a Time; Clean Up.
+
+ Sketch an Outline
+
We are suggesting you first write down all the steps you want the program to do. You can do this in any manner you like. We are going to
+ show you how to outline using comments, but if you are more visual you might want to sketch on a piece of paper and if you are more
+ spatial try walking around the room. The big trick is to understand everything you want to do first in your own words, so then you are
+ translating them to the computer.
+
+
+ Code One Section at a Time
+
After you outline your program, you should write code one section at a time, and carefully test that section before you go on. The idea here is to make sure your program is doing what you think it's doing at each stage.
+
Translating your English description of a step into code may be the most challenging step for you early in your learning about programming. Later it will come more naturally. Here is a checklist of questions that you may find useful in trying to find the right python code to express your idea, based on what you've learned so far:
+
+
+
+
Is this operation pulling out an item from a list or string or dictionary? If so, use [] to pull out the item you want.
+
+
+
Is this operation transforming a string into another string? If so, look at the summary of string methods.
+
+
+
Is this operation modifying a list? If so, look at the material on lists.
+
+
+
Is the operation doing something multiple times? If so, you'll want a for loop. Start by making a skeleton version of a for loop, and then fill in the parts that are in <brackets>
+
+
+
+
for <varname> in <seq>:
+ <code block line 1>
+ <code block line 2>
+ ...
+
+
+
+
Is the operation something that should only occur in some circumstances and not in others? If so, you'll want an if statement. Start by making a skeleton version of an if/then/else code snippet, and then fill in the parts that are in <brackets>
Is this an accumulator pattern? If so, start by making a skeleton version of it, and then fill it in.
+
+
+
+
#initialize accumulator
+a = <initial value>
+
+for <varname> in <seq>:
+ <some code in for block>
+ a = <new_value>
+ <other code in for block>
+print(a)
+
Finally, you may be reminded of a snippet of code somewhere in the textbook that did something similar to what you want to do. Now is the time to copy and edit that code. But wait! Before you start editing that code snippet, make sure you understand it. See the section below on understanding code.
+
+
+ Clean Up
+
When you are done with outlining and testing your program, delete any diagnostic print statements from your program. No one really needs to see the test statements you wrote, and leaving test statements in the program might confuse you if you add more to the program.
+
Extra comments do help other people read your code, but try to leave in only the bits that you think are useful. There is an art to writing good informative comments, and you can only learn this art by reading other people's programs and having your peers read your programs. As a rule of thumb for comments, when in doubt, keep it; it you're worried it won't make sense to you or someone else later, add more detail to it.
+
In the next few pages, we'll go through this process using a question similar to something that you may have already seen before.
+
+
diff --git a/pretext/BuildingPrograms/WPCleanCode.ptx b/pretext/BuildingPrograms/WPCleanCode.ptx
new file mode 100644
index 00000000..cfde4240
--- /dev/null
+++ b/pretext/BuildingPrograms/WPCleanCode.ptx
@@ -0,0 +1,42 @@
+
+
+ 👩💻 Clean Up
+
Congrats! We've solved the problem now, but our code isn't very nice to read. We can clean it up now and remove the print statements.
+
+
+# initialize a dictionary
+user_dictionary = {}
+
+# write a for loop that will iterate five times. I can use the range function for this!
+for _ in range(5):
+ # in the for loop, I should ask for input from the user
+ response = input("Please enter two words to add to a dictionary. The first word is the definition, the second will be the word associated with it.")
+
+ # next, I should separate the words
+ separated_response = response.split()
+ response_key = separated_response[0]
+ response_value = separated_response[1]
+
+ # finally, I should add the key value pair to the dictionary
+ user_dictionary[response_key] = response_value
+
+
+
We can also fix the comments so that they aren't so obvious.
+
+
+user_dictionary = {}
+
+# asks a user for two words to add to the user dictionary - will do this five times.
+# the first word will be the key, the second word will be the value.
+for _ in range(5):
+ response = input("Please enter two words to add to a dictionary. The first word is the definition, the second will be the word associated with it.")
+
+ separated_response = response.split()
+ response_key = separated_response[0]
+ response_value = separated_response[1]
+
+ user_dictionary[response_key] = response_value
+
+
+
At this point, the code has been cleaned up fully - you could easily write the comments in a different way but this should be easy for other programmers to understand, and ourselves to understand if we come back to the code days, weeks, or months later!
+
diff --git a/pretext/BuildingPrograms/WPCodeSectionataTime.ptx b/pretext/BuildingPrograms/WPCodeSectionataTime.ptx
new file mode 100644
index 00000000..17b5c704
--- /dev/null
+++ b/pretext/BuildingPrograms/WPCodeSectionataTime.ptx
@@ -0,0 +1,127 @@
+
+
+ 👩💻 Code one section at a time
+
As a reminder, this is our prompt:
+
Build a program that replicates a physical dictionary that you might use. The program should that take five different input from the user. Each input will have two words and we will build a dictionary where the words are the keys and values.
+
We'll start to build up the sections one at a time now! First, we need to pick a name for the dictionary. We'll try to pick a clear name for each of these variables
+
+
+# initialize a dictionary
+user_dictionary = {}
+print("---------- keys in user_dictionary: " + str(list(user_dictionary.keys())) + " ----------")
+
+# write a for loop that will iterate five times. I can use the range function for this!
+
+# in the for loop, I should ask for input from the user
+
+# next, I should separate the words
+
+# finally, I should add the key value pair to the dictionary
+
+
+
We picked the variable name user_dictionary because it will be a dictionary that is created by a user. Other names could be
+ appropriate as well! Though it may seem unnecessary, we'll add a print statement to remind ourself that user_dictionary is empty.
+
Next we'll build up the for loop!
+
+
+# initialize a dictionary
+user_dictionary = {}
+print("---------- keys in user_dictionary: " + str(list(user_dictionary.keys())) + " ----------")
+
+# write a for loop that will iterate five times. I can use the range function for this!
+for _ in range(5):
+ print("---------- LOOP HAS STARTED ----------")
+ # in the for loop, I should ask for input from the user
+
+ # next, I should separate the words
+
+ # finally, I should add the key value pair to the dictionary
+ print("---------- LOOP HAS ENDED ----------")
+
+
+
If we want to make sure that the for loop is iterating five times then we can add these print statements to execute so that we
+ can track the progress of the program.
+
Next, we'lll get the input from the user!
+
+
+# initialize a dictionary
+user_dictionary = {}
+print("---------- keys in user_dictionary: " + str(list(user_dictionary.keys())) + " ----------")
+
+# write a for loop that will iterate five times. I can use the range function for this!
+for _ in range(5):
+ print("---------- LOOP HAS STARTED ----------")
+ # in the for loop, I should ask for input from the user
+ response = input("Please enter two words to add to a dictionary. The first word is the definition, the second will be the word associated with it.")
+ print("---------- The response: " + response + " ----------")
+
+ # next, I should separate the words
+
+ # finally, I should add the key value pair to the dictionary
+ print("---------- LOOP HAS ENDED ----------")
+
+
+
Now we'll want to print out the response. We're expecting that it should be as string, so we should be able to add it to the print
+ statement with other strings without any issue. If there is an issue, then something could be going wrong with how we are getting input
+ from the user.
+
Now, we can separate the words so that we have our key and value to add to the dictionary!
+
+
+# initialize a dictionary
+user_dictionary = {}
+print("---------- keys in user_dictionary: " + str(list(user_dictionary.keys())) + " ----------")
+
+# write a for loop that will iterate five times. I can use the range function for this!
+for _ in range(5):
+ print("---------- LOOP HAS STARTED ----------")
+ # in the for loop, I should ask for input from the user
+ response = input("Please enter two words to add to a dictionary. The first word is the definition, the second will be the word associated with it.")
+ print("---------- The response: " + response + " ----------")
+
+ # next, I should separate the words
+ separated_response = response.split()
+ print("---------- The separated response: " + str(separated_response) + " ----------")
+ response_key = separated_response[0]
+ print("---------- The response key: " + response_key + " ----------")
+ response_value = separated_response[1]
+ print("---------- The response value: " + response_value + " ----------")
+
+ # finally, I should add the key value pair to the dictionary
+ print("---------- LOOP HAS ENDED ----------")
+
+
+
Here we know that response is a string that contains two words. We can use the split method to separate the words, which will give us
+ a list. The first word will be the key and the second word will be the value, so we can use indexing to access that information.
+
+
+# initialize a dictionary
+user_dictionary = {}
+print("---------- keys in user_dictionary: " + str(list(user_dictionary.keys())) + " ----------")
+
+# write a for loop that will iterate five times. I can use the range function for this!
+for _ in range(5):
+ print("---------- LOOP HAS STARTED ----------")
+ # in the for loop, I should ask for input from the user
+ response = input("Please enter two words to add to a dictionary. The first word is the definition, the second will be the word associated with it.")
+ print("---------- The response: " + response + " ----------")
+
+ # next, I should separate the words
+ separated_response = response.split()
+ print("---------- The separated response: " + str(separated_response) + " ----------")
+ response_key = separated_response[0]
+ print("---------- The response key: " + response_key + " ----------")
+ response_value = separated_response[1]
+ print("---------- The response value: " + response_value + " ----------")
+
+ # finally, I should add the key value pair to the dictionary
+ user_dictionary[response_key] = response_value
+ print("---------- LOOP HAS ENDED ----------")
+
+print("---------- The user dictionary")
+print(user_dictionary)
+print("----------")
+
+
+
Finally, we add code to add the key and value pair into a dictionary. We can print out the final result of the dictionary once the for
+ loop is over so that we can determine if it has been done correctly.
+
diff --git a/pretext/BuildingPrograms/WPSketchanOutline.ptx b/pretext/BuildingPrograms/WPSketchanOutline.ptx
new file mode 100644
index 00000000..f780184d
--- /dev/null
+++ b/pretext/BuildingPrograms/WPSketchanOutline.ptx
@@ -0,0 +1,26 @@
+
+
+ 👩💻 Sketch an Outline
+
The prompt that we will be using is the following:
+
Build a program that replicates a physical dictionary that you might use. The program should that take five different input from the user. Each input will have two words and we will build a dictionary where the words are the keys and values.
+
Try writting out what you think the outline would be, and then check out our answer to see how we did it! Always remember that there can be multiple ways to solve a problem, so your solution may be different from ours but still solve the problem.
+
+
+
+
+
+
+# initialize a dictionary
+
+# write a for loop that will iterate five times. I can use the range function for this!
+
+# in the for loop, I should ask for input from the user
+
+# next, I should separate the words
+
+# finally, I should add the key value pair to the dictionary
+
+
+
+
+
diff --git a/pretext/BuildingPrograms/toctree.ptx b/pretext/BuildingPrograms/toctree.ptx
new file mode 100644
index 00000000..05ccf217
--- /dev/null
+++ b/pretext/BuildingPrograms/toctree.ptx
@@ -0,0 +1,9 @@
+
+
+ Building Programs
+
+
+
+
+
+
diff --git a/pretext/Classes/AddingOtherMethodstoourClass.ptx b/pretext/Classes/AddingOtherMethodstoourClass.ptx
new file mode 100644
index 00000000..2591191a
--- /dev/null
+++ b/pretext/Classes/AddingOtherMethodstoourClass.ptx
@@ -0,0 +1,96 @@
+
+
+ Adding Other Methods to a Class
+
The key advantage of using a class like Point rather than something like a simple
+ tuple (7, 6) now becomes apparent. We can add methods to
+ the Point class that are sensible operations for points. Had we chosen to use a
+ tuple to represent the point, we would not have this capability.
+ Creating a class like Point brings an exceptional
+ amount of organizational power to our programs, and to our thinking.
+ We can group together the sensible operations, and the kinds of data
+ they apply to, and each instance of the class can have its own state.
+
A method behaves like a function but it is invoked on a specific
+ instance. For example, with a list bound to variable L, L.append(7) calls the function append, with the list itself as the first parameter and 7 as the second parameter. Methods are accessed using dot notation. This is why L.append(7) has 2 parameters even though you may think it only has one: the list stored in the variable L is the first parameter value and 7 is the second.
+
Let's add two simple methods to allow a point to give us information about its state. The getX method, when invoked, will return the value of the x coordinate.
+
The implementation of this method is straight forward since we already know how
+ to write functions that return values. One thing to notice is that even though the getX method does not need any other parameter information to do its work, there is still one formal parameter, self. As we stated earlier, all methods defined in a class that operate on objects of that class will have self as their first parameter. Again, this serves as a reference to the object itself which in turn gives access to the state data inside the object.
Note that the getX method simply returns the value of the instance variable x from the object self. In other words, the implementation of the method is to go to the state of the object itself and get the value of x. Likewise, the getY method looks almost the same.
+
Let's add another method, distanceFromOrigin, to see better how methods
+ work. This method will again not need any additional information to do its work, beyond the data stored in the instance variables.
+ It will perform a more complex task.
Notice that the call of distanceFromOrigin does not explicitly
+ supply an argument to match the self parameter. This is true of all method calls. The definition will always seem to
+ have one additional parameter as compared to the invocation.
+
+ Check Your Understanding
+
+
+
+
+
Create a class called Animal that accepts two numbers as inputs and assigns them respectively to two instance variables: arms and legs. Create an instance method called limbs that, when called, returns the total number of limbs the animal has. To the variable name spider, assign an instance of Animal that has 4 arms and 4 legs. Call the limbs method on the spider instance and save the result to the variable name spidlimbs.
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(spider.arms, 4, "Testing that spider was assigned the correct number of arms.")
+ self.assertEqual(spider.legs, 4, "Testing that spider was assigned the correct number of legs.")
+ self.assertEqual(spidlimbs, 8, "Testing that spidlimbs was assigned correctly.")
+
+myTests().main()
+
+
+
diff --git a/pretext/Classes/ChapterAssessment.ptx b/pretext/Classes/ChapterAssessment.ptx
new file mode 100644
index 00000000..884ac401
--- /dev/null
+++ b/pretext/Classes/ChapterAssessment.ptx
@@ -0,0 +1,81 @@
+
+
+ Chapter Assessment
+
+
+
Define a class called Bike that accepts a string and a float as input, and assigns those inputs respectively to two instance variables, color and price. Assign to the variable testOne an instance of Bike whose color is blue and whose price is 89.99. Assign to the variable testTwo an instance of Bike whose color is purple and whose price is 25.0.
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(testOne.color, "blue", "Testing that testOne has the correct color assigned.")
+ self.assertEqual(testOne.price, 89.99, "Testing that testOne has the correct price assigned.")
+
+ def testTwo(self):
+ self.assertEqual(testTwo.color, "purple", "Testing that testTwo has the correct color assigned.")
+ self.assertEqual(testTwo.price, 25.0, "Testing that testTwo has the correct color assigned.")
+
+myTests().main()
+
+
+
+
+
+
Create a class called AppleBasket whose constructor accepts two inputs: a string representing a color, and a number representing a quantity of apples. The constructor should initialize two instance variables: apple_color and apple_quantity. Write a class method called increase that increases the quantity by 1 each time it is invoked. You should also write a __str__ method for this class that returns a string of the format: "A basket of [quantity goes here] [color goes here] apples." e.g. "A basket of 4 red apples." or "A basket of 50 blue apples." (Writing some test code that creates instances and assigns values to variables may help you solve this problem!)
Define a class called BankAccount that accepts the name you want associated with your bank account in a string, and an integer that represents the amount of money in the account. The constructor should initialize two instance variables from those inputs: name and amt. Add a string method so that when you print an instance of BankAccount, you see "Your account, [name goes here], has [start_amt goes here] dollars." Create an instance of this class with "Bob" as the name and 100 as the amount. Save this to the variable t1.
+
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(t1.__str__(), "Your account, Bob, has 100 dollars.", "Testing that t1 is assigned to correct value")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Classes/ClassVariablesInstanceVariables.ptx b/pretext/Classes/ClassVariablesInstanceVariables.ptx
new file mode 100644
index 00000000..ee3a7f5c
--- /dev/null
+++ b/pretext/Classes/ClassVariablesInstanceVariables.ptx
@@ -0,0 +1,100 @@
+
+
+ Class Variables and Instance Variables
+
You have already seen that each instance of a class has its own namespace with its own instance variables. Two instances of the Point class each have their own instance variable x. Setting x in one instance doesn't affect the other instance.
+
A class can also have class variables. A class variable is set as part of the class definition.
+
For example, consider the following version of the Point class. Here we have added a graph method that generates a string representing a little text-based graph with the Point plotted on the graph. It's not a very pretty graph, in part because the y-axis is stretched like a rubber band, but you can get the idea from this.
+
Note that there is an assignment to the variable printed_rep on line 4. It is not inside any method. That makes it a class variable. It is accessed in the same way as instance variables. For example, on line 16, there is a reference to self.printed_rep. If you change line 4, you have it print a different character at the x,y coordinates of the Point in the graph.
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ printed_rep = "*"
+
+ def __init__(self, initX, initY):
+
+ self.x = initX
+ self.y = initY
+
+ def graph(self):
+ rows = []
+ size = max(int(self.x), int(self.y)) + 2
+ for j in range(size-1) :
+ if (j+1) == int(self.y):
+ special_row = str((j+1) % 10) + (" "*(int(self.x) -1)) + self.printed_rep
+ rows.append(special_row)
+ else:
+ rows.append(str((j+1) % 10))
+ rows.reverse() # put higher values of y first
+ x_axis = ""
+ for i in range(size):
+ x_axis += str(i % 10)
+ rows.append(x_axis)
+
+ return "\n".join(rows)
+
+
+p1 = Point(2, 3)
+p2 = Point(3, 12)
+print(p1.graph())
+print()
+print(p2.graph())
+
+
+
To be able to reason about class variables and instance variables, it is helpful to know the rules that the python interpreter uses. That way, you can mentally simulate what the interpreter does.
+
+
+ When the interpreter sees an expression of the form <obj>.<varname>, it:
+
+
+
+
Checks if the object has an instance variable set. If so, it uses that value.
+
+
+
If it doesn't find an instance variable, it checks whether the class has a class variable. If so it uses that value.
+
+
+
If it doesn't find an instance or a class variable, it creates a runtime error (actually, it does one other check first, which you will learn about in the next chapter).
+
+
+
+
+
+ When the interpreter sees an assignment statement of the form <obj>.<varname> = <expr>, it:
+
+
+
+
Evaluates the expression on the right-hand side to yield some python object;
+
+
+
Sets the instance variable <varname> of <obj> to be bound to that python object. Note that an assignment statement of this form never sets the class variable; it only sets the instance variable.
+
+
+
+
+
+
In order to set the class variable, you use an assignment statement of the form <varname> = <expr> at the top-level in a class definition, like on line 4 in the code above to set the class variable printed_rep.
+
+
+ In case you are curious, method definitions also create class variables. Thus, in the code above, graph becomes a class variable that is bound to a function/method object. p1.graph() is evaluated by:
+
+
+
+
looking up p1 and finding that it's an instance of Point
+
+
+
looking for an instance variable called graph in p1, but not finding one
+
+
+
looking for a class variable called graph in p1's class, the Point class; it finds a function/method object
+
+
+
Because of the () after the word graph, it invokes the function/method object, with the parameter self bound to the object p1 points to.
+
+
+
+
+
+
Try running it in codelens and see if you can follow how it all works.
+
diff --git a/pretext/Classes/ConvertinganObjecttoaString.ptx b/pretext/Classes/ConvertinganObjecttoaString.ptx
new file mode 100644
index 00000000..412b863f
--- /dev/null
+++ b/pretext/Classes/ConvertinganObjecttoaString.ptx
@@ -0,0 +1,101 @@
+
+
+ Converting an Object to a String
+
When we're working with classes and objects, it is often necessary to print an object (that is, to print the state of an object).
+ Consider the example below.
The print function shown above produces a string representation of the Point p. The default functionality provided by
+ Python tells you that p is an object of type Point. However, it does not tell you anything about the specific
+ state of the point.
+
We can improve on this representation if we include a special method call __str__. Notice that this method uses the same naming convention as the constructor, that is two underscores before and after the name. It is common that Python
+ uses this naming technique for special methods.
+
The __str__ method is responsible for returning a string representation as defined by the class creator. In other words, you as the programmer, get to choose what a Point should look like when it gets printed. In this case, we
+ have decided that the string representation will include the values of x and y as well as some identifying text. It
+ is required that the __str__ method create and return a string.
+
Whatever string the __str__ method for a class returns, that is the string that will print when you put any instance of that class in a print statement. For that reason, the string that a class's __str__ method returns should usually include values of instance variables. If a point has x value 3 and y value 4, but another point has x value 5 and y value 9, those two Point objects should probably look different when you print them, right?
When we run the program above you can see that the print function now shows the string that we chose.
+
Now, you ask, don't we already have a str type converter that can
+ turn our object into a string? Yes we do!
+
And doesn't print
+ automatically use this when printing things? Yes again!
+
However, as we saw earlier, these automatic mechanisms do not do exactly what we want. Python provides many default implementations for
+ methods that we as programmers will probably want to change. When a programmer changes the meaning of a method we
+ say that we override the method. Note also that the str type converter function uses whatever __str__ method we
+ provide.
+
+ Check Your Understanding
+
+
+
+
+
Create a class called Cereal that accepts three inputs: 2 strings and 1 integer, and assigns them to 3 instance variables in the constructor: name, brand, and fiber. When an instance of Cereal is printed, the user should see the following: [name] cereal is produced by [brand] and has [fiber integer] grams of fiber in every serving! To the variable name c1, assign an instance of Cereal whose name is "Corn Flakes", brand is "Kellogg's", and fiber is 2. To the variable name c2, assign an instance of Cereal whose name is "Honey Nut Cheerios", brand is "General Mills", and fiber is 3. Practice printing both!
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(c1.__str__(), "Corn Flakes cereal is produced by Kellogg's and has 2 grams of fiber in every serving!", "Testing that c1 prints correctly.")
+ def testTwo(self):
+ self.assertEqual(c2.__str__(), "Honey Nut Cheerios cereal is produced by General Mills and has 3 grams of fiber in every serving!", "Testing that c2 prints correctly.")
+
+myTests().main()
+
+
+
diff --git a/pretext/Classes/Exercises.ptx b/pretext/Classes/Exercises.ptx
new file mode 100644
index 00000000..7506b4b2
--- /dev/null
+++ b/pretext/Classes/Exercises.ptx
@@ -0,0 +1,2 @@
+
+
diff --git a/pretext/Classes/Glossary.ptx b/pretext/Classes/Glossary.ptx
new file mode 100644
index 00000000..0638b259
--- /dev/null
+++ b/pretext/Classes/Glossary.ptx
@@ -0,0 +1,62 @@
+
+
+ Glossary
+
+
+ attribute
+
One of the named data items that makes up an instance.
+
+
+ class
+
A user-defined compound type. A class can also be thought of as a
+ template for the objects that are instances of it.
+
+
+ constructor
+
Every class has a factory, called by the same name as the class, for
+ making new instances. If the class has an initializer method, this method
+ is used to get the attributes (i.e. the state) of the new object properly set up.
+
+
+ initializer method
+
A special method in Python (called __init__)
+ that is invoked automatically to set a newly created object's
+ attributes to their initial (factory-default) state.
+
+
+ instance
+
An object whose type is of some class. The words instance and object are used
+ interchangeably.
+
+
+ instance variable
+
A variable that stores a value associated with the instance. The instance variables together store the state of an instance.
+
+
+ instantiate
+
To create an instance of a class, and to run its initializer.
+
+
+ method
+
A function that is defined inside a class definition and is invoked on
+ instances of that class.
+
+
+ object
+
A compound data type that is often used to model a thing or concept in
+ the real world. It bundles together the data and the operations that
+ are relevant for that kind of data. Instance and object are used
+ interchangeably.
+
+
+ object-oriented programming
+
A powerful style of programming in which data and the operations
+ that manipulate it are organized into classes and methods.
+
+
+ object-oriented language
+
A language that provides features, such as user-defined classes and
+ inheritance, that facilitate object-oriented programming.
+
+
+
diff --git a/pretext/Classes/ImprovingourConstructor.ptx b/pretext/Classes/ImprovingourConstructor.ptx
new file mode 100644
index 00000000..e94c9d6b
--- /dev/null
+++ b/pretext/Classes/ImprovingourConstructor.ptx
@@ -0,0 +1,53 @@
+
+
+ Adding Parameters to the Constructor
+
Our constructor so far can only create points at location (0,0). To create a point at position (7, 6) requires that we
+ provide some additional capability for the user to pass information to the constructor. Since constructors are simply specially named functions, we can use parameters (as we've seen before) to provide the specific information.
+
We can make our class constructor more generally usable by putting extra parameters into
+ the __init__ method, as shown in this example.
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ def __init__(self, initX, initY):
+
+ self.x = initX
+ self.y = initY
+
+p = Point(7,6)
+
+
+
Now when we create new points, we supply the x and y coordinates as parameters. When the point is created, the values of initX and initY are assigned to the state of the object, in the instance variables x and y.
+
This is a common thing to do in the __init__ method for a class: take in some parameters and save them as instance variables. Why is this useful? Keep in mind that the parameter variables will go away when the method is finished executing. The instance variables, however, will still be accessible anywhere that you have a handle on the object instance. This is a way of saving those initial values that are provided when the class constructor is invoked.
+
+
Later on, you will see classes where the __init__ method does more than just save parameters as instance variables. For example, it might parse the contents of those variables and do some computation on them, storing the results in instance variables. It might even make an Internet connection, download some content, and store that in instance variables.
+
+
+
+ Check Your Understanding
+
+
+
+
+
Create a class called NumberSet that accepts 2 integers as input, and defines two instance variables: num1 and num2, which hold each of the input integers. Then, create an instance of NumberSet where its num1 is 6 and its num2 is 10. Save this instance to a variable t.
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOneA(self):
+ self.assertEqual(t.num1, 6, "Testing that t.num1 has correct number assigned.")
+ def testOneB(self):
+ self.assertEqual(t.num2, 10, "Testing that t.num2 has correct number assigned.")
+
+myTests().main()
+
+
+
diff --git a/pretext/Classes/InstancesasReturnValues.ptx b/pretext/Classes/InstancesasReturnValues.ptx
new file mode 100644
index 00000000..81818a22
--- /dev/null
+++ b/pretext/Classes/InstancesasReturnValues.ptx
@@ -0,0 +1,51 @@
+
+
+ Instances as Return Values
+
Functions and methods can return objects. This is actually nothing new since everything in Python is an object and we have
+ been returning values for quite some time. (You can also have lists or tuples of object instances, etc.) The difference here is that we want to have the method create an object using
+ the constructor and then return it as the value of the method.
+
Suppose you have a point object
+ and wish to find the midpoint halfway between it and some other target point. We would like to write a method, let's call
+ it halfway, which takes another Point as a parameter and returns the Point that is halfway between the point and
+ the target point it accepts as input.
+
+
+class Point:
+
+ def __init__(self, initX, initY):
+
+ self.x = initX
+ self.y = initY
+
+ def getX(self):
+ return self.x
+
+ def getY(self):
+ return self.y
+
+ def distanceFromOrigin(self):
+ return ((self.x ** 2) + (self.y ** 2)) ** 0.5
+
+ def __str__(self):
+ return "x = {}, y = {}".format(self.x, self.y)
+
+ def halfway(self, target):
+ mx = (self.x + target.x)/2
+ my = (self.y + target.y)/2
+ return Point(mx, my)
+
+p = Point(3,4)
+q = Point(5,12)
+mid = p.halfway(q)
+# note that you would have exactly the same result if you instead wrote
+# mid = q.halfway(p)
+# because they are both Point objects, and the middle is the same no matter what
+
+print(mid)
+print(mid.getX())
+print(mid.getY())
+
+
+
The resulting Point, mid, has an x value of 4 and a y value of 8. We can also use any other methods on mid since it is a
+ Point object.
In Python, every value is actually an object. Whether it be a dictionary, a list, or even an integer, they are all objects. Programs manipulate those objects either by performing
+ computation with them or by asking them to perform methods. To be more specific, we say that an object has
+ a state and a collection of methods that it can perform. (More about methods below.) The state of an object represents those things
+ that the object knows about itself. The state is stored in instance variables. For example, as we have seen with turtle objects, each turtle has a state consisting
+ of the turtle's position, its color, its heading and so on. Each turtle also has the ability to go forward, backward, or turn right or left. Individual turtles are different in that even though they are
+ all turtles, they differ in the specific values of the individual state attributes (maybe they are in a different location or have a different heading).
+
+
diff --git a/pretext/Classes/ObjectsasArgumentsandParameters.ptx b/pretext/Classes/ObjectsasArgumentsandParameters.ptx
new file mode 100644
index 00000000..f5584ac0
--- /dev/null
+++ b/pretext/Classes/ObjectsasArgumentsandParameters.ptx
@@ -0,0 +1,76 @@
+
+
+ Objects as Arguments and Parameters
+
You can pass an object as an argument to a function, in the usual way.
+
Here is a simple function called distance involving our new Point objects. The job of this function is to figure out the
+ distance between two points.
distance takes two points and returns the distance between them. Note that distance is not a method of the Point class. You can see this by looking at the indentation pattern. It is not inside the class definition. The other way we
+ can know that distance is not a method of Point is that self is not included as a formal parameter. In addition, we do not invoke distance using the dot notation.
+
We could have made distance be a method of the Point class. Then, we would have called the first parameter self, and would have invoked it using the dot notation, as in the following code. Which way to implement it is a matter of coding style. Both work correctly. Most programmers choose whether to make functions be stand-alone or methods of a class based on whether the function semantically seems to be an operation that is performed on instances of the class. In this case, because distance is really a property of a pair of points and is symmetric (the distance from a to b is the same as that from b to a) it makes more sense to have it be a standalone function and not a method. Many heated discussions have occurred between programmers about such style decisions.
There are also a lot of interesting ways to put user-defined classes to use that don't involve data from the internet. Let's pull all these mechanics together in a slightly more interesting way than we got with the Point class. Remember Tamagotchis, the little electronic pets? As time passed, they would get hungry or bored. You had to clean up after them or they would get sick. And you did it all with a few buttons on the device.
+
We are going to make a simplified, text-based version of that. In your problem set and in the chapter on we will extend this further.
+
+
+ First, let's start with a class Pet. Each instance of the class will be one electronic pet for the user to take care of. Each instance will have a current state, consisting of three instance variables:
+
+
+
+
hunger, an integer
+
+
+
boredom, an integer
+
+
+
sounds, a list of strings, each a word that the pet has been taught to say
+
+
+
+
+
+
In the __init__ method, hunger and boredom are initialized to random values between 0 and the threshold for being hungry or bored. The sounds instance variable is initialized to be a copy of the class variable with the same name. The reason we make a copy of the list is that we will perform destructive operations (appending new sounds to the list). If we didn't make a copy, then those destructive operations would affect the list that the class variable points to, and thus teaching a sound to any of the pets would teach it to all instances of the class!
+
There is a clock_tick method which just increments the boredom and hunger instance variables, simulating the idea that as time passes, the pet gets more bored and hungry.
+
The __str__ method produces a string representation of the pet's current state, notably whether it is bored or hungry or whether it is happy. It's bored if the boredom instance variable is larger than the threshold, which is set as a class variable.
+
To relieve boredom, the pet owner can either teach the pet a new word, using the teach() method, or interact with the pet, using the hi() method. In response to teach(), the pet adds the new word to its list of words. In response to the hi() method, it prints out one of the words it knows, randomly picking one from its list of known words. Both hi() and teach() cause an invocation of the reduce_boredom() method. It decrements the boredom state by an amount that it reads from the class variable boredom_decrement. The boredom state can never go below 0.
+
To relieve hunger, we call the feed() method.
+
+
+from random import randrange
+
+class Pet():
+ boredom_decrement = 4
+ hunger_decrement = 6
+ boredom_threshold = 5
+ hunger_threshold = 10
+ sounds = ['Mrrp']
+ def __init__(self, name = "Kitty"):
+ self.name = name
+ self.hunger = randrange(self.hunger_threshold)
+ self.boredom = randrange(self.boredom_threshold)
+ self.sounds = self.sounds[:] # copy the class attribute, so that when we make changes to it, we won't affect the other Pets in the class
+
+ def clock_tick(self):
+ self.boredom += 1
+ self.hunger += 1
+
+ def mood(self):
+ if self.hunger <= self.hunger_threshold and self.boredom <= self.boredom_threshold:
+ return "happy"
+ elif self.hunger > self.hunger_threshold:
+ return "hungry"
+ else:
+ return "bored"
+
+ def __str__(self):
+ state = " I'm " + self.name + ". "
+ state += " I feel " + self.mood() + ". "
+ # state += "Hunger {} Boredom {} Words {}".format(self.hunger, self.boredom, self.sounds)
+ return state
+
+ def hi(self):
+ print(self.sounds[randrange(len(self.sounds))])
+ self.reduce_boredom()
+
+ def teach(self, word):
+ self.sounds.append(word)
+ self.reduce_boredom()
+
+ def feed(self):
+ self.reduce_hunger()
+
+ def reduce_hunger(self):
+ self.hunger = max(0, self.hunger - self.hunger_decrement)
+
+ def reduce_boredom(self):
+ self.boredom = max(0, self.boredom - self.boredom_decrement)
+
+
+
Let's try making a pet and playing with it a little. Add some of your own commands, too, and keep printing p1 to see what the effects are. If you want to directly inspect the state, try printing p1.boredom or p1.hunger.
+
+
+p1 = Pet("Fido")
+print(p1)
+for i in range(10):
+ p1.clock_tick()
+ print(p1)
+p1.feed()
+p1.hi()
+p1.teach("Boo")
+for i in range(10):
+ p1.hi()
+print(p1)
+
+
+
That's all great if you want to interact with the pet by writing python code. Let's make a game that non-programmers can play.
+
We will use the pattern. At each iteration, we will display a text prompt reminding the user of what commands are available.
+
The user will have a list of pets, each with a name. The user can issue a command to adopt a new pet, which will create a new instance of Pet. Or the user can interact with an existing pet, with a Greet, Teach, or Feed command.
+
No matter what the user does, with each command entered, the clock ticks for all their pets. Watch out, if you have too many pets, you won't be able to keep them all satisfied!
+
+
+import sys
+sys.setExecutionLimit(60000)
+
+def whichone(petlist, name):
+ for pet in petlist:
+ if pet.name == name:
+ return pet
+ return None # no pet matched
+
+def play():
+ animals = []
+
+ option = ""
+ base_prompt = """
+ Quit
+ Adopt <petname_with_no_spaces_please>
+ Greet <petname>
+ Teach <petname> <word>
+ Feed <petname>
+
+ Choice: """
+ feedback = ""
+ while True:
+ action = input(feedback + "\n" + base_prompt)
+ feedback = ""
+ words = action.split()
+ if len(words) > 0:
+ command = words[0]
+ else:
+ command = None
+ if command == "Quit":
+ print("Exiting...")
+ return
+ elif command == "Adopt" and len(words) > 1:
+ if whichone(animals, words[1]):
+ feedback += "You already have a pet with that name\n"
+ else:
+ animals.append(Pet(words[1]))
+ elif command == "Greet" and len(words) > 1:
+ pet = whichone(animals, words[1])
+ if not pet:
+ feedback += "I didn't recognize that pet name. Please try again.\n"
+ print()
+ else:
+ pet.hi()
+ elif command == "Teach" and len(words) > 2:
+ pet = whichone(animals, words[1])
+ if not pet:
+ feedback += "I didn't recognize that pet name. Please try again."
+ else:
+ pet.teach(words[2])
+ elif command == "Feed" and len(words) > 1:
+ pet = whichone(animals, words[1])
+ if not pet:
+ feedback += "I didn't recognize that pet name. Please try again."
+ else:
+ pet.feed()
+ else:
+ feedback+= "I didn't understand that. Please try again."
+
+ for pet in animals:
+ pet.clock_tick()
+ feedback += "\n" + pet.__str__()
+
+
+
+play()
+
+
+
diff --git a/pretext/Classes/TestingClasses.ptx b/pretext/Classes/TestingClasses.ptx
new file mode 100644
index 00000000..767a7192
--- /dev/null
+++ b/pretext/Classes/TestingClasses.ptx
@@ -0,0 +1,155 @@
+
+
+ Testing classes
+
+
This page depends on the use of the test module, which is introduced in . If you haven't covered that chapter yet, you will want to delay reading this page until you do.
+
+
To test a user-defined class, you will create test cases that check whether instances are created properly, and you will
+ create test cases for each of the methods as functions, by invoking them on particular instances and seeing whether they
+ produce the correct return values and side effects, especially side effects that change data stored in the instance
+ variables. To illustrate, we will use the Point class that was used in the introduction to classes.
+
To test whether the class constructor (the __init__) method is working correctly, create an instance and then make
+ tests to see whether its instance variables are set correctly. Note that this is a side effect test: the constructor
+ method's job is to set instance variables, which is a side effect. Its return value doesn't matter.
+
A method like distanceFromOrigin in the Point class you saw does its work by computing a return value, so it
+ needs to be tested with a return value test. A method like move in the Turtle class does its work by changing the
+ contents of a mutable object (the point instance has its instance variable changed) so it needs to be tested with a side
+ effect test.
+
Try adding some more tests in the code below, once you understand what's there.
For each function, you should create exactly one test case.
+
+
+
+
+
True
+
+
+
Each test case checks whether the function works correctly on one input. It's a good idea to check several different inputs, including some extreme cases.
+
+
+
+
+
False
+
+
+
It's a good idea to check some extreme cases, as well as the typical cases.
+
+
+
+
+
+
+
To test a method that changes the value of an instance variable, which kind of test case should you write?
+
+
+
+
+
return value test
+
+
+
The method may return the correct value but not properly change the values of instance variables. See the move method of the Point class above.
+
+
+
+
+
side effect test
+
+
+
The move method of the Point class above is a good example.
+
+
+
+
+
+
+
To test the function maxabs, which kind of test case should you write?
+
+
+def maxabs(L):
+ """L should be a list of numbers (ints or floats). The return value should be the maximum absolute value of the numbers in L."""
+ return max(L, key=abs)
+
+
+
+
+
+
+
return value test
+
+
+
You want to check if maxabs returns the correct value for some input.
+
+
+
+
+
side effect test
+
+
+
The function has no side effects; even though it takes a list L as a parameter, it doesn't alter its contents.
+
+
+
+
+
+
+
We have usually used the sorted function, which takes a list as input and returns a new list containing the same items, possibly in a different order. There is also a method called sort for lists (e.g. [1,6,2,4].sort()). It changes the order of the items in the list itself, and it returns the value None. Which kind of test case would you use on the sort method?
+
+
+
+
+
return value test
+
+
+
The sort method always returns None, so there's nothing to check about whether it is returning the right value.
+
+
+
+
+
side effect test
+
+
+
You want to check whether it has the correct side effect, whether it correctly mutates the list.
+
+
+
+
+
diff --git a/pretext/Classes/ThinkingAboutClasses.ptx b/pretext/Classes/ThinkingAboutClasses.ptx
new file mode 100644
index 00000000..1c66b0ea
--- /dev/null
+++ b/pretext/Classes/ThinkingAboutClasses.ptx
@@ -0,0 +1,28 @@
+
+
+ Thinking About Classes and Instances
+
You can now imagine some reasons you may want to define a class. You have seen examples of creating types that are more complicated or specific than the ones built in to Python (like lists or strings). Turtle, with all the instance variables and methods you learned about using earlier in the semester, is a class that programmers defined which is now included in the Python language. In this chapter, we defined Point with some functionality that can make it easier to write programs that involve x,y coordinate Point instances. And shortly, you'll see how you can define classes to represent objects in a game.
+
You can also use self-defined classes to hold data – for example, data you get from making a request to a REST API.
+
Before you decide to define a new class, there are a few things to keep in mind, and questions you should ask yourself:
+
+
+
+
What is the data that you want to deal with? (Data about a bunch of songs from iTunes? Data about a bunch of tweets from Twitter? Data about a bunch of hashtag searches on Twitter? Two numbers that represent coordinates of a point on a 2-dimensional plane?)
+
+
+
What will one instance of your class represent? In other words, which sort of new thing in your program should have fancy functionality? One song? One hashtag? One tweet? One point? The answer to this question should help you decide what to call the class you define.
+
+
+
What information should each instance have as instance variables? This is related to what an instance represents. See if you can make it into a sentence. Each instance represents one < song > and each < song > has an < artist > and a < title > as instance variables. Or, Each instance represents a < Tweet > and each < Tweet > has a < user (who posted it) > and < a message content string > as instance variables.
+
+
+
What instance methods should each instance have? What should each instance be able to do? To continue using the same examples: Maybe each song has a method that uses a lyrics API to get a long string of its lyrics. Maybe each song has a method that returns a string of its artist's name. Or for a tweet, maybe each tweet has a method that returns the length of the tweet's message. (Go wild!)
+
+
+
What should the printed version of an instance look like? (This question will help you determine how to write the __str__ method.) Maybe, Each song printed out will show the song title and the artist's name. or Each Tweet printed out will show the username of the person who posted it and the message content of the tweet.
+
+
+
+
After considering those questions and making decisions about how you're going to get started with a class definition, you can begin to define your class.
+
Remember that a class definition, like a function definition, is a general description of what every instance of the class should have. (Every Point has an x and a y.) The class instances are specific: e.g. the Point with a specific x and y >. You might have a Point with an x value of 3 and a y value of 2, so for that particular instance of the classPoint, you'd pass in 3 and 2 to the constructor, the __init__ method, like so: new_point = Point(3,2), as you saw in the last sections.
+
diff --git a/pretext/Classes/UserDefinedClasses.ptx b/pretext/Classes/UserDefinedClasses.ptx
new file mode 100644
index 00000000..85e143a1
--- /dev/null
+++ b/pretext/Classes/UserDefinedClasses.ptx
@@ -0,0 +1,128 @@
+
+
+ User Defined Classes
+
We've already seen classes like str, int, float and list. These were defined by Python and
+ made available for us to use. However, in many cases when we are solving problems we need to create data objects
+ that are related to the problem we are trying to solve. We need to create our own classes.
+
As an example, consider the concept of a mathematical point. In two dimensions, a point is two
+ numbers (coordinates) that are treated collectively as a single object.
+ Points are often written in parentheses with a comma
+ separating the coordinates. For example, (0, 0) represents the origin, and
+ (x, y) represents the point x units to the right and y units up
+ from the origin. This (x,y) is the state of the point.
+
Thinking about our diagram above, we could draw a point object as shown here.
+
+
Some of the typical operations that one associates with points might be to ask
+ the point for its x coordinate, getX, or to ask for its y coordinate, getY. You would want these types of functions available to prevent accidental changes to these instance variables since doing so would allow you to view the values without accessing them directly. You may also
+ wish to calculate the distance of a point from the origin, or the distance of a point from another point,
+ or find the midpoint between two points, or answer the question as to whether a point falls within a
+ given rectangle or circle. We'll shortly see how we can organize these
+ together with the data.
+
+
Now that we understand what a point object might look like, we can define a new class.
+ We'll want our points to each have an x and a y attribute,
+ so our first class definition looks like this.
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ def __init__(self):
+ """ Create a new point at the origin """
+ self.x = 0
+ self.y = 0
+
+
+
Class definitions can appear anywhere in a program, but they are usually near
+ the beginning (after the import statements). The syntax rules for a class
+ definition are the same as for other compound statements. There is a header
+ which begins with the keyword, class, followed by the name of the class,
+ and ending with a colon.
+
If the first line after the class header is a string, it becomes
+ the docstring of the class, and will be recognized by various tools. (This
+ is also the way docstrings work in functions.)
+
Every class should have a method with the special name __init__.
+ This initializer method, often referred to as the constructor, is automatically called whenever a new
+ instance of Point is created. It gives the programmer the opportunity
+ to set up the attributes required within the new instance by giving them
+ their initial state values. The self parameter (you could choose any
+ other name, but nobody ever does!) is automatically set to reference
+ the newly created object that needs to be initialized.
+
So let's use our new Point class now. This next part should look a little familiar, if you remember some of the syntax for how we created instances of the Turtle class, in the .
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ def __init__(self):
+
+ self.x = 0
+ self.y = 0
+
+p = Point() # Instantiate an object of type Point
+q = Point() # and make a second point
+
+print("Nothing seems to have happened with the points")
+
+
+
During the initialization of the objects, we created two
+ attributes called x and y for each object, and gave them both the value 0. You will note that when you run the
+ program, nothing happens. It turns out that this is not quite the case. In fact, two Points have been created, each
+ having an x and y coordinate with value 0. However, because we have not asked the program to do anything with the points, we don't see any other result.
+
+
The following program adds a few print statements. You can see that the output suggests that each one is a Point object.
+ However, notice that the is operator returns False meaning that they are different objects (we will have more to say about this in a later section).
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ def __init__(self):
+
+ self.x = 0
+ self.y = 0
+
+p = Point() # Instantiate an object of type Point
+q = Point() # and make a second point
+
+print(p)
+print(q)
+
+print(p is q)
+
+
+
A function like Point that creates a new object instance
+ is called a constructor. Every class automatically uses the name of the class as the name of the constructor function.
+ The definition of the constructor function is done
+ when you write the __init__ function (method) inside the class definition.
+
It may be helpful to think of a class as a factory for making objects.
+ The class itself isn't an instance of a point, but it contains the machinery
+ to make point instances. Every time you call the constructor, you're asking
+ the factory to make you a new object. As the object comes off the
+ production line, its initialization method is executed to
+ get the object properly set up with it's factory default settings.
+
The combined process of make me a new object and get its settings initialized
+ to the factory default settings is called instantiation.
+
To get a clearer understanding of what happens when instantiating a new instance, examine the previous code using CodeLens.
+
+
+class Point:
+ """ Point class for representing and manipulating x,y coordinates. """
+
+ def __init__(self):
+
+ self.x = 0
+ self.y = 0
+
+p = Point() # Instantiate an object of type Point
+q = Point() # and make a second point
+
+print(p)
+print(q)
+
+print(p is q)
+
+
+
At Step 2 in the CodeLens execution, you can see that Point has been bound to an object representing the Point class, but there are not yet any instances. The execution of line 9, p = Point(), occurs at steps 3-5. First, at step 3, you can see that a blank instance of the class has been created, and is passed as the first (and only parameter) to the __init__ method. That method's code is executed, with the variable self bound to that instance. At steps 4 and 5, two instance variables are filled in: x and y are both set to 0. Nothing is returned from the __init__ method, but the point object itself is returned from the call to Point(). Thus, at step 7, p is bound to the new point that was created and initialized.
+
Skipping ahead, by the time we get to Step 14, p and q are each bound to different Point instances. Even though both have x and y instance variables set to 0, they are different objects. Thus p is q evaluates to False.
+
diff --git a/pretext/Classes/intro-ClassesandObjectstheBasics.ptx b/pretext/Classes/intro-ClassesandObjectstheBasics.ptx
new file mode 100644
index 00000000..8481ccf3
--- /dev/null
+++ b/pretext/Classes/intro-ClassesandObjectstheBasics.ptx
@@ -0,0 +1,21 @@
+
+
+ Introduction: Classes and Objects - the Basics
+
+ Object-oriented programming
+
Python is an object-oriented programming language. That means it
+ provides features that support object-oriented programming (OOP).
+
Object-oriented programming has its roots in the 1960s, but it wasn't until the
+ mid 1980s that it became the main programming paradigm used in the creation
+ of new software. It was developed as a way to handle the rapidly increasing
+ size and complexity of software systems and to make it easier to modify these
+ large and complex systems over time.
+
Up to now, some of the programs we have been writing use a procedural programming paradigm. In
+ procedural programming the focus is on writing functions or procedures which
+ operate on data. In object-oriented programming the focus is on the creation of
+ objects which contain both data and functionality together.
+ Usually, each object definition corresponds to some object or concept in the real
+ world and the functions that operate on that object correspond to the ways
+ real-world objects interact.
The Python type for storing true and false values is called bool, named
+ after the British mathematician, George Boole. George Boole created Boolean
+ Algebra, which is the basis of all modern computer arithmetic.
+
There are only two boolean values. They are True and False. Capitalization
+ is important, since true and false are not boolean values (remember Python is case
+ sensitive).
It is extremely important to realize that True and False are not strings. They are not
+ surrounded by quotes. They are the only two values in the data type bool. Take a close look at the
+ types shown below.
A boolean expression is an expression that evaluates to a boolean value.
+ The equality operator, ==, compares two values and produces a boolean value related to whether the
+ two values are equal to one another.
+
+
+print(5 == 5)
+print(5 == 6)
+
+
+
In the first statement, the two operands are equal, so the expression evaluates
+ to True. In the second statement, 5 is not equal to 6, so we get False.
+
The == operator is one of six common comparison operators; the others are:
+
+
+x != y # x is not equal to y
+x > y # x is greater than y
+x < y # x is less than y
+x >= y # x is greater than or equal to y
+x <= y # x is less than or equal to y
+
+
+
Although these operations are probably familiar to you, the Python symbols are
+ different from the mathematical symbols. A common error is to use a single
+ equal sign (=) instead of a double equal sign (==). Remember that =
+ is an assignment operator and == is a comparison operator. Also, there is
+ no such thing as =< or =>.
+
+
+
+
+
+
+
Note too that an equality test is symmetric, but assignment is not. For example,
+ if a == 7 then 7 == a. But in Python, the statement a = 7
+ is legal and 7 = a is not. (Can you explain why?)
+
+ Check your understanding
+
+
+
+
Which of the following is a Boolean expression? Select all that apply.
+
+
+
+
+
True
+
+
+
True and False are both Boolean literals.
+
+
+
+
+
3 == 4
+
+
+
The comparison between two numbers via == results in either True or False (in this case False), both Boolean values.
+
+
+
+
+
3 + 4
+
+
+
3+4 evaluates to 7, which is a number, not a Boolean value.
+
+
+
+
+
3 + 4 == 7
+
+
+
3+4 evaluates to 7. 7 == 7 then evaluates to True, which is a Boolean value.
+
+
+
+
+
"False"
+
+
+
With the double quotes surrounding it, False is interpreted as a string, not a Boolean value. If the quotes had not been included, False alone is in fact a Boolean value.
Python provides an alternative way to write nested selection such as the one shown in the previous section.
+ This is sometimes referred to as a chained conditional.
+
+
+if x < y:
+ print("x is less than y")
+elif x > y:
+ print("x is greater than y")
+else:
+ print("x and y must be equal")
+
+
+
The flow of control can be drawn in a different orientation but the resulting pattern is identical to the one shown above.
+
+
elif is an abbreviation of else if. Again, exactly one branch will be
+ executed. There is no limit of the number of elif statements but only a
+ single (and optional) final else statement is allowed and it must be the last
+ branch in the statement.
+
+
Each condition is checked in order. If the first is false, the next is checked,
+ and so on. If one of them is true, the corresponding branch executes, and the
+ statement ends. Even if more than one condition is true, only the first true
+ branch executes.
+
Here is the same program using elif.
+
+
+x = 10
+y = 10
+
+if x < y:
+ print("x is less than y")
+elif x > y:
+ print("x is greater than y")
+else:
+ print("x and y must be equal")
+
+
+
The following image highlights different kinds of valid conditionals that can be used. Though there are other
+ versions of conditionals that Python can understand (imagine an if statement with twenty elif statements), those
+ other versions must follow the same order as seen below.
+
+
+ Check your understanding
+
+
+
+
Which of I, II, and III below gives the same result as the following nested if?
+
+
+# nested if-else statement
+x = -10
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+else:
+ if x > 0:
+ print(x, " is a positive number")
+ else:
+ print(x, " is 0")
+
+
+
+
+I.
+
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+else (x > 0):
+ print(x, " is a positive number")
+else:
+ print(x, " is 0")
+
+
+
+
+II.
+
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+elif (x > 0):
+ print(x, " is a positive number")
+else:
+ print(x, " is 0")
+
+
+
+
+III.
+
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+if (x > 0):
+ print(x, " is a positive number")
+else:
+ print(x, " is 0")
+
+
+
+
+
+
+
I only
+
+
+
You can not use a Boolean expression after an else.
+
+
+
+
+
II only
+
+
+
Yes, II will give the same result.
+
+
+
+
+
III only
+
+
+
No, III will not give the same result. The first if statement will be true, but the second will be false, so the else part will execute.
+
+
+
+
+
II and III
+
+
+
No, Although II is correct III will not give the same result. Try it.
+
+
+
+
+
I, II, and III
+
+
+
No, in I you can not have a Boolean expression after an else.
+
+
+
+
+
+
+
What will the following code print if x = 3, y = 5, and z = 2?
+
+
+if x < y and x < z:
+ print("a")
+elif y < x and y < z:
+ print("b")
+else:
+ print("c")
+
+
+
+
+
+
+
a
+
+
+
While the value in x is less than the value in y (3 is less than 5) it is not less than the value in z (3 is not less than 2).
+
+
+
+
+
b
+
+
+
The value in y is not less than the value in x (5 is not less than 3).
+
+
+
+
+
c
+
+
+
Since the first two Boolean expressions are false the else will be executed.
+
+
+
+
+
+
+
Create one conditional to find whether false is in string str1. If so, assign variable output the string False. You aren't you?. Check to see if true is in string str1 and if it is then assign True! You are you! to the variable output. If neither are in str1, assign Neither true nor false! to output.
+
+
+
+str1 = "Today you are you! That is truer than true! There is no one alive who is you-er than you!"
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testThree(self):
+ self.assertEqual(output, "True! You are you!", "Testing that output has the correct value, given the str1 provided.")
+ self.assertIn("else", self.getEditorText(), "Testing output (Don't worry about actual and expected values).")
+ self.assertIn("elif", self.getEditorText(), "Testing output (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
Create an empty list called resps. Using the list percent_rain, for each percent, if it is above 90, add the string ‘Bring an umbrella.' to resps, otherwise if it is above 80, add the string ‘Good for the flowers?' to resps, otherwise if it is above 50, add the string ‘Watch out for clouds!' to resps, otherwise, add the string ‘Nice day!' to resps. Note: if you're sure you've got the problem right but it doesn't pass, then check that you've matched up the strings exactly.
+
+
+
+percent_rain = [94.3, 45, 100, 78, 16, 5.3, 79, 86]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(resps, ['Bring an umbrella.','Nice day!','Bring an umbrella.','Watch out for clouds!',"Nice day!",'Nice day!','Watch out for clouds!',"Good for the flowers?"], "Testing the value of resps")
+
+myTests().main()
+
+
+
+
+
+
We have created conditionals for you to use. Do not change the provided conditional statements. Find an integer value for x that will cause output to hold the values True and None. (Drawing diagrams or flow charts for yourself may help!)
+
+
+
+x =
+output = []
+
+if x > 63:
+ output.append(True)
+elif x > 55:
+ output.append(False)
+else:
+ output.append("Neither")
+
+if x > 67:
+ output.append(True)
+else:
+ output.append(None)
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testSixA(self):
+ self.assertEqual(output, [True, None], "Testing that value of output is correct.")
+
+ def testSixB(self):
+ self.assertEqual(x in [64, 65, 66, 67], True, "Testing that value of x is reasonable for this problem")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Conditionals/ConditionalExecutionBinarySelection.ptx b/pretext/Conditionals/ConditionalExecutionBinarySelection.ptx
new file mode 100644
index 00000000..c0c0e630
--- /dev/null
+++ b/pretext/Conditionals/ConditionalExecutionBinarySelection.ptx
@@ -0,0 +1,243 @@
+
+
+ Conditional Execution: Binary Selection
+
+
In order to write useful programs, we almost always need the ability to check
+ conditions and change the behavior of the program accordingly. Selection statements, sometimes
+ also referred to as conditional statements, give us this ability. The simplest form of selection is the if statement.
+ This is sometimes referred to as binary selection since there are two possible paths of execution.
+
+
+if BOOLEAN EXPRESSION:
+ STATEMENTS_1 # executed if condition evaluates to True
+else:
+ STATEMENTS_2 # executed if condition evaluates to False
+
+
+
The boolean expression after the if statement is called the condition.
+ If it is true, then the indented statements get executed. If not, then the statements
+ indented under the else clause get executed.
+
+ Flowchart of a if statement with an else
+
+
+
As with the function definition from the last chapter and other compound
+ statements like for, the if statement consists of a header line and a body. The header
+ line begins with the keyword if followed by a boolean expression and ends with
+ a colon (:).
+
The indented statements that follow are called a block. The first
+ unindented statement marks the end of the block.
+
Each of the statements inside the first block of statements is executed in order if the boolean
+ expression evaluates to True. The entire first block of statements
+ is skipped if the boolean expression evaluates to False, and instead
+ all the statements under the else clause are executed.
+
There is no limit on the number of statements that can appear under the two clauses of an
+ if statement, but there has to be at least one statement in each block.
+
+ Check your understanding
+
+
+
+
How many lines of code can appear in the indented code block below the if and else lines in a conditional?
+
+
+
+
+
Just one.
+
+
+
Each block may also contain more than one.
+
+
+
+
+
Zero or more.
+
+
+
Each block must contain at least one statement.
+
+
+
+
+
One or more.
+
+
+
Yes, a block must contain at least one statement and can have many statements.
+
+
+
+
+
One or more, and each must contain the same number.
+
+
+
The blocks may contain different numbers of statements.
+
+
+
+
+
+
+
What does the following code print? (choose from output a, b, c or nothing)
TRUE is printed by the if-block, which only executes if the conditional (in this case, 4+5 == 10) is true. In this case 5+4 is not equal to 10.
+
+
+
+
+
FALSE
+
+
+
Since 4+5==10 evaluates to False, Python will skip over the if block and execute the statement in the else block.
+
+
+
+
+
TRUE on one line and FALSE on the next
+
+
+
Python would never print both TRUE and FALSE because it will only execute one of the if-block or the else-block, but not both.
+
+
+
+
+
Nothing will be printed
+
+
+
Python will always execute either the if-block (if the condition is true) or the else-block (if the condition is false). It would never skip over both blocks.
Although TRUE is printed after the if-else statement completes, both blocks within the if-else statement print something too. In this case, Python would have had to have skipped both blocks in the if-else statement, which it never would do.
+
+
+
+
+
Output b
+
+
+
Because there is a TRUE printed after the if-else statement ends, Python will always print TRUE as the last statement.
+
+
+
+
+
Output c
+
+
+
Python will print FALSE from within the else-block (because 5+4 does not equal 10), and then print TRUE after the if-else statement completes.
+
+
+
+
+
Output d
+
+
+
To print these three lines, Python would have to execute both blocks in the if-else statement, which it can never do.
+
+
+
+
+
+
+
Write code to assign the string "You can apply to SI!" to outputif the string "SI 106" is in the list courses. If it is not in courses, assign the value "Take SI 106!" to the variable output.
+
+
+
+courses = ["ENGR 101", "SI 110", "ENG 125", "SI 106", "CHEM 130"]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(output, "You can apply to SI!", "Testing that output has the correct value, given the courses list provided")
+ self.assertIn("if", self.getEditorText(), "Testing output (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
+
Create a variable, b, and assign it the value of 15. Then, write code to see if the value b is greater than that of a. If it is, a's value should be multiplied by 2. If the value of b is less than or equal to a, nothing should happen. Finally, create variable c and assign it the value of the sum of a and b.
+
+
+
+a = 20
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testTwoA(self):
+ self.assertEqual(a, 20, "Testing that a has the correct value.")
+
+ def testTwoB(self):
+ self.assertEqual(c, 35, "Testing that c has the correct value.")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Conditionals/Exercises.ptx b/pretext/Conditionals/Exercises.ptx
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ b/pretext/Conditionals/Exercises.ptx
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git "a/pretext/Conditionals/Exercises.ptx\n" "b/pretext/Conditionals/Exercises.ptx\n"
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ "b/pretext/Conditionals/Exercises.ptx\n"
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git a/pretext/Conditionals/Glossary.ptx b/pretext/Conditionals/Glossary.ptx
new file mode 100644
index 00000000..2311f84e
--- /dev/null
+++ b/pretext/Conditionals/Glossary.ptx
@@ -0,0 +1,59 @@
+
+
+ Glossary
+
+
+ block
+
A group of consecutive statements with the same indentation.
+
+
+ body
+
The block of statements in a compound statement that follows the
+ header.
+
+
+ boolean values
+
A value that is either True or False. True and False must be capitalized to be considered Boolean.
+
+
+ branch
+
One of the possible paths of the flow of execution determined by
+ conditional execution.
+
+
+ chained conditional
+
A conditional branch with more than two possible flows of execution. In
+ Python chained conditionals are written with if ... elif ... else
+ statements.
+
+
+ comparison operator
+
One of the operators that compares two values: ==, !=, >,
+ <, >=, and <=.
+
+
+ condition
+
The boolean expression in a conditional statement that determines which
+ branch is executed.
+
+
+ conditional statement
+
A statement that controls the flow of execution depending on some
+ condition. In Python the keywords if, elif, and else are
+ used for conditional statements.
+
+
+ logical operators
+
and, or and not are logical operators used to evaluate expressions. Their semantic meaning is similar to their English meaning.
+
+
+ nesting
+
One program structure within another, such as a conditional statement
+ inside a branch of another conditional statement.
+
+
+ unary selection
+
A selection statement in which there is only an if statement and the else statement is omitted entirely. In an unary selection, the statements are only executed if the condition evaluates to true, otherwise the program continues to the body following the if statement.
There are three logical operators: and, or, and not.
+ All three operators take boolean operands and produce boolean values.
+ The semantics (meaning) of these operators is similar to their meaning in English:
+
+
+
+
x and y is True if both x and y are True. Otherwise, and produces False.
+
+
+
x or y yields True if either x or y is True. Only if both operands are False
+ does or yield False.
+
+
+
not x yields False if x is True, and vice versa.
+
+
+
+
Look at the following example. See if you can predict the output. Then, Run to see if your predictions were
+ correct:
+
+
+x = True
+y = False
+print(x or y)
+print(x and y)
+print(not x)
+
+
+
Although you can use boolean operators with simple boolean literals or variables as in the above
+ example, they are often combined with the comparison operators, as in this example. Again, before you
+ run this, see if you can predict the outcome:
+
+
+x = 5
+print(x > 0 and x < 10)
+
+n = 25
+print(n % 2 == 0 or n % 3 == 0)
+
+
+
The expression x > 0 and x < 10 is True only if x is greater than 0 and
+ at the same time, x is less than 10. In other words, this expression is True if
+ x is between 0 and 10, not including the endpoints.
+
+ Common Mistake!
+
There is a very common mistake that occurs when programmers try to write boolean expressions. For example, what if
+ we have a variable number and we want to check to see if its value is 5 or 6. In words we might say: number
+ equal to 5 or 6. However, if we translate this into Python, number == 5 or 6, it will not yield correct
+ results. The or operator must have a complete equality check on both sides. The correct way to write this is
+ number == 5 or number == 6. Remember that both operands of or must be booleans in order to yield proper results.
+
+
+ Smart Evaluation
+
Python is smart about the way it evaluates expressions using boolean operators. Consider the following example:
There are two operands for the or operator here: answer == 'Y' and 'answer == 'y'. Python evaluates from
+ left to right, and if the first operand for or evaluates to True, Python doesn't bother evaluating the second
+ operand, because it knows the result must be True (recall that if either operand for or is True, the
+ result is True). So, if the user enters Y, Python first evaluates answer ==
+'Y', determines that it is True, and doesn't bother to check to see if answer == 'y' is True; it just
+ concludes that the entire condition is True and executes the print statement.
+
In a similar fashion, with the and operator, if the first operand evaluates to False, Python doesn't check the
+ second operand's value, because it can conclude that the result must be False.
+
This behavior, in which Python in some cases skips the evaluation of the second operand to and and or, is called
+ short-circuit boolean evaluation. You don't have to do anything to make Python do this; it's the way Python works.
+ It saves a little processing time. And, as a special bonus, you can take advantage of Python's short-circuiting behavior
+ to shorten your code. Consider the following example:
+
+
+total_weight = int(input('Enter total weight of luggage:'))
+num_pieces = int(input('Number of pieces of luggage?'))
+
+if total_weight / num_pieces > 50:
+ print('Average weight is greater than 50 pounds -> $100 surcharge.')
+
+print('Luggage check complete.')
+
+
+
This code checks to see if the average weight of a given number of pieces of luggage is greater than 50 pounds. However,
+ there is a potential crash situation here. If the user enters 0 for num_pieces, the program will crash with a
+ divide by zero error. Try it out to see it happen.
+
To prevent the crash, you might add an extra if statement to check for zero:
+
if num_pieces != 0:
+ if total_weight / num_pieces > 50:
+ print('Average weight is greater than 50 pounds -> $100 surcharge.')
+
Now, the division will not occur if num_pieces is zero, and a potential runtime crash has been averted. Good job!
+
We can shorten this example to a single if statement if we do it carefully. Anytime you have two nested if
+ statements as in the example above, you can combine them into a single if statement by joining the conditions using
+ the and operator. Consider the version below, and think about why this if statement is equivalent in its behavior to
+ the previous version with two nested if statements:
+
+
+total_weight = int(input('Enter total weight of luggage:'))
+num_pieces = int(input('Number of pieces of luggage?'))
+
+if num_pieces != 0 and total_weight / num_pieces > 50:
+ print('Average weight is greater than 50 pounds -> $100 surcharge.')
+
+print('Luggage check complete.')
+
+
+
But wait a minute: is this code safe? Try running the program and entering the value 500 for total_weight and the value 5 for num_pieces.
+ Then, try it again using the value 0 for num_pieces. There should be no crash.
+
Next, try altering the code and reversing the order of the if conditions:
+
if total_weight / num_pieces > 50 and num_pieces != 0:
+ print('Average weight is greater than 50 pounds -> $100 surcharge.')
+
Run the program again, performing the same two tests. This time, you should observe a crash when you enter 0 for
+ num_pieces. Can you analyze why the first version did not crash, but the second one does?
+
In the second version, when evaluating left-to-right, the division by zero occurs before Python evaluates the comparison
+ num_pieces != 0. When joining two if statements into a single if statement, you must be sure to put the
+ condition from the first if statement on the left-hand side of the and operator, and the other condition on the
+ right-hand side, in order to get the same effect.
+
To summarize this discussion on smart evaluation, keep in mind that when you are performing potentially dangerous
+ operations in an if statement or while loop using boolean logic with and or or, order matters!
+
+ Check your understanding
+
+
+
+
What is the correct Python expression for checking to see if a number stored in a variable x is between 0 and 5.
+
+
+
+
+
x > 0 and < 5
+
+
+
Each comparison must be between exactly two values. In this case the right-hand expression < 5 lacks a value on its left.
+
+
+
+
+
0 < x < 5
+
+
+
Although most other programming languages do not allow this syntax, in Python, this syntax is allowed. Even though it is possible to use this format, you should not use it all the time. Instead, make multiple comparisons by using and or or.
+
+
+
+
+
x > 0 or x < 5
+
+
+
Although this is legal Python syntax, the expression is incorrect. It will evaluate to true for all numbers that are either greater than 0 or less than 5. Because all numbers are either greater than 0 or less than 5, this expression will always be True.
+
+
+
+
+
x > 0 and x < 5
+
+
+
Yes, with an ``and`` keyword both expressions must be true so the number must be greater than 0 an less than 5 for this expression to be true.
+
+
+
+
+
+
+
Which of the following may result in a crash at runtime if the user presses Enter without typing a response?
+
Option A)
+
yesno = input('Enter Yes or No:')
+if yesno[0] == 'Y' and len(yesno) > 0:
+ print('Yes!')
+
Option B)
+
yesno = input('Enter Yes or No:')
+if len(yesno) > 0 and yesno[0] == 'Y':
+ print('Yes!')
+
+
+
+
+
Option A
+
+
+
Correct! The comparison yesno[0] == 'Y' will crash if yesno is an empty string.
+
+
+
+
+
Option B
+
+
+
Incorrect. If len(yesno) > 0 is False, the potentially unsafe comparison yesno[0] == 'Y' will not be evaluated.
+
+
+
+
+
+
+
Consider the following fragment containing a nested if statement to prevent a crash in the event
+ the user enters an empty response for yesno:
+
yesno = input('Enter Yes or No:')
+if len(yesno) > 0:
+ if yesno[0] == 'Y':
+ print('Yes!')
+
Which of the following is the correct way to combine the nested if into a single if statement that executes
+ identically to the nested if statements?
+
Option A)
+
if yesno[0] == 'Y' and len(yesno) > 0:
+ print('Yes!')
+
Option B)
+
if len(yesno) > 0 and yesno[0] == 'Y':
+ print('Yes!')
+
Option C)
+
if yesno[0] == 'Y' or len(yesno) > 0:
+ print('Yes!')
+
Option D)
+
if len(yesno) > 0 or yesno[0] == 'Y':
+ print('Yes!')
+
+
+
+
+
Option A
+
+
+
Incorrect. The comparison yesno[0] == 'Y' will crash if yesno is an empty string.
+
+
+
+
+
Option B
+
+
+
Correct! Use the and operator to join nested if statements into a single statement, with the first if condition on the left-hand side.
+
+
+
+
+
Option C
+
+
+
Incorrect. The comparison yesno[0] == 'Y' will crash if yesno is an empty string.
+
+
+
+
+
Option D
+
+
+
Incorrect. The comparison yesno[0] == 'Y' will crash if yesno is an empty string.
One conditional can also be nested within another. For example, assume we have two integer variables, x and
+ y. The following pattern of selection shows how we might decide how they are related to each other.
+
+
+if x < y:
+ print("x is less than y")
+else:
+ if x > y:
+ print("x is greater than y")
+ else:
+ print("x and y must be equal")
+
+
+
The outer conditional contains two branches.
+ The second branch (the else from the outer) contains another if statement, which
+ has two branches of its own. Those two branches could contain
+ conditional statements as well.
+
The flow of control for this example can be seen in this flowchart illustration.
+
+
Here is a complete program that defines values for x and y. Run the program and see the result. Then change the values of the variables to change the flow of control.
+
+
+x = 10
+y = 10
+
+if x < y:
+ print("x is less than y")
+else:
+ if x > y:
+ print("x is greater than y")
+ else:
+ print("x and y must be equal")
+
+
+
+
In some programming languages, matching the if and the else is a problem. However, in Python this is not
+ the case. The indentation pattern tells us exactly which else belongs to which if.
+
+
If you are still a bit unsure, here is the same selection as part of a codelens example. Step through it to see how the correct print is chosen.
+
+
+x = 10
+y = 10
+
+if x < y:
+ print("x is less than y")
+else:
+ if x > y:
+ print("x is greater than y")
+ else:
+ print("x and y must be equal")
+
+
+
+ Check your understanding
+
+
+
+
Will the following code cause an error?
+
+
+x = -10
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+else:
+ if x > 0:
+ print(x, " is a positive number")
+ else:
+ print(x," is 0")
+
+
+
+
+
+
+
No
+
+
+
This is a legal nested if-else statement. The inner if-else statement is contained completely within the body of the outer else-block.
+
+
+
+
+
Yes
+
+
+
This is a legal nested if-else statement. The inner if-else statement is contained completely within the body of the outer else-block.
+
+
+
+
+
diff --git a/pretext/Conditionals/OmittingtheelseClauseUnarySelection.ptx b/pretext/Conditionals/OmittingtheelseClauseUnarySelection.ptx
new file mode 100644
index 00000000..56ec06d9
--- /dev/null
+++ b/pretext/Conditionals/OmittingtheelseClauseUnarySelection.ptx
@@ -0,0 +1,114 @@
+
+
+ Omitting the else Clause: Unary Selection
+
+
+ Flowchart of an if with no else
+
+
+
Another form of the if statement is one in which the else clause is omitted entirely. This creates what
+ is sometimes called unary selection. In this case, when the condition evaluates to True, the statements
+ are executed. Otherwise the flow of execution continues to the statement after the body of the if.
+
+
+x = 10
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+print("This is always printed")
+
+
+
What would be printed if the value of x is negative? Try it.
+
+ Check your understanding
+
+
+
+
What does the following code print?
+
+
+x = -10
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+print("This is always printed")
+
+
+
a.
+This is always printed
+
+b.
+The negative number -10 is not valid here
+This is always printed
+
+c.
+The negative number -10 is not valid here
+
+
+
+
+
Output a
+
+
+
Because -10 is less than 0, Python will execute the body of the if-statement here.
+
+
+
+
+
Output b
+
+
+
Python executes the body of the if-block as well as the statement that follows the if-block.
+
+
+
+
+
Output c
+
+
+
Python will also execute the statement that follows the if-block (because it is not enclosed in an else-block, but rather just a normal statement).
+
+
+
+
+
It will cause an error because every if must have an else clause.
+
+
+
It is valid to have an if-block without a corresponding else-block (though you cannot have an else-block without a corresponding if-block).
+
+
+
+
+
+
+
Will the following code cause an error?
+
+
+x = -10
+if x < 0:
+ print("The negative number ", x, " is not valid here.")
+else:
+ print(x, " is a positive number")
+else:
+ print("This is always printed")
+
+
+
+
+
+
+
No
+
+
+
Every else-block must have exactly one corresponding if-block. If you want to chain if-else statements together, you must use the else if construct, described in the chained conditionals section.
+
+
+
+
+
Yes
+
+
+
This will cause an error because the second else-block is not attached to a corresponding if-block.
+
+
+
+
+
diff --git a/pretext/Conditionals/PrecedenceofOperators.ptx b/pretext/Conditionals/PrecedenceofOperators.ptx
new file mode 100644
index 00000000..f68bd00b
--- /dev/null
+++ b/pretext/Conditionals/PrecedenceofOperators.ptx
@@ -0,0 +1,166 @@
+
+
+ Precedence of Operators
+
Arithmetic operators take precedence over logical operators. Python will always evaluate the arithmetic operators first (** is highest, then multiplication/division, then addition/subtraction). Next comes the relational operators. Finally, the logical operators are done last. This means that the expression x*5 >= 10 and y-6 <= 20 will be evaluated so as to first perform the arithmetic and then check the relationships. The and will be done last. Many programmers might place parentheses around the two relational expressions, (x*5 >= 10) and (y-6 <= 20). It is not necessary to do so, but causes no harm and may make it easier for people to read and understand the code.
+
The following table summarizes the operator precedence from highest to lowest. A complete table for the entire language can be found in the Python Documentation.
This workspace is provided for your convenience. You can use this activecode window to try out anything you like.
+
+
+
+
+
+
+
+
+ Common Mistake!
+
Students often incorrectly combine the in and or operators. For example, if they want to check
+ that the letter x is inside of either of two variables then they tend to write it the following
+ way: 'x' in y or z
+
Written this way, the code would not always do what the programmer intended. This is because the
+ in operator is only on the left side of the or statement. It doesn't get implemented on both
+ sides of the or statement. In order to properly check that x is inside of either variable, the in
+ operator must be used on both sides which looks like this:
+
+
+'x' in y or 'x' in z
+
+
+
+
+ Check your understanding
+
+
+
+
Which of the following properly expresses the precedence of operators (using parentheses) in the following expression: 5*3 > 10 and 4+6==11
+
+
+
+
+
((5*3) > 10) and ((4+6) == 11)
+
+
+
Yes, * and + have higher precedence, followed by > and ==, and then the keyword "and"
+
+
+
+
+
(5*(3 > 10)) and (4 + (6 == 11))
+
+
+
Arithmetic operators (*, +) have higher precedence than comparison operators (>, ==)
+
+
+
+
+
((((5*3) > 10) and 4)+6) == 11
+
+
+
This grouping assumes Python simply evaluates from left to right, which is incorrect. It follows the precedence listed in the table in this section.
+
+
+
+
+
((5*3) > (10 and (4+6))) == 11
+
+
+
This grouping assumes that "and" has a higher precedence than ==, which is not true.
+
+
+
+
+
Here is an animation for the above expression:
+
+
diff --git a/pretext/Conditionals/TheAccumulatorPatternwithConditionals.ptx b/pretext/Conditionals/TheAccumulatorPatternwithConditionals.ptx
new file mode 100644
index 00000000..57222f21
--- /dev/null
+++ b/pretext/Conditionals/TheAccumulatorPatternwithConditionals.ptx
@@ -0,0 +1,221 @@
+
+
+ The Accumulator Pattern with Conditionals
+
Sometimes when we're accumulating, we don't want to add to our accumulator every time we iterate.
+ Consider, for example, the following program which counts the number of letters in a phrase.
+
+
+phrase = "What a wonderful day to program"
+tot = 0
+for char in phrase:
+ if char != " ":
+ tot = tot + 1
+print(tot)
+
+
+
Here, we initialize the accumulator variable to be zero on line two.
+
We iterate through the sequence (line 3).
+
The update step happens in two parts. First, we check to see if the value of char is not a space. If
+ it is not a space, then we update the value of our accumulator variable tot (on line 6) by adding one to
+ it. If that conditional proves to be False, which means that char is a space, then we don't update tot
+ and continue the for loop. We could have written tot = tot + 1 or tot += 1, either is fine.
+
At the end, we have accumulated a the total number of letters in the phrase. Without using the conditional,
+ we would have only been able to count how many characters there are in the string and not been able to
+ differentiate between spaces and non-spaces.
+
We can use conditionals to also count if particular items are in a string or list. The following code finds all occurrences of vowels in the following string.
+
+
+s = "what if we went to the zoo"
+x = 0
+for i in s:
+ if i in ['a', 'e', 'i', 'o', 'u']:
+ x += 1
+print(x)
+
+
+
We can also use == to execute a similar operation. Here, we'll check to see if the character we are iterating over is
+ an o. If it is an o then we will update our counter.
+
+
+ Accumulating the Max Value
+
We can also use the accumulation pattern with conditionals to find the maximum or minimum value. Instead of
+ continuing to build up the accumulator value like we have when counting or finding a sum, we can reassign the
+ accumulator variable to a different value.
+
The following example shows how we can get the maximum value from a list of integers.
+
+
+nums = [9, 3, 8, 11, 5, 29, 2]
+best_num = 0
+for n in nums:
+ if n > best_num:
+ best_num = n
+print(best_num)
+
+
+
Here, we initialize best_num to zero, assuming that there are no negative numbers in the list.
+
In the for loop, we check to see if the current value of n is greater than the current value of best_num.
+ If it is, then we want to updatebest_num so that it now is assigned the higher number. Otherwise, we
+ do nothing and continue the for loop.
+
You may notice that the current structure could be a problem. If the numbers were all negative what would
+ happen to our code? What if we were looking for the smallest number but we initialized best_num with
+ zero? To get around this issue, we can initialize the accumulator variable using one of the numbers in the
+ list.
+
+
+nums = [9, 3, 8, 11, 5, 29, 2]
+best_num = nums[0]
+for n in nums:
+ if n > best_num:
+ best_num = n
+print(best_num)
+
+
+
The only thing we changed was the value of best_num on line 2 so that the value of best_num is the
+ first element in nums, but the result is still the same!
+
+ Check your understanding
+
+
+
+
What is printed by the following statements?
+
+
+s = "We are learning!"
+x = 0
+for i in s:
+ if i in ['a', 'b', 'c', 'd', 'e']:
+ x += 1
+print(x)
+
+
+
+
+
+
+
2
+
+
+
Though only two of the letters in the list are found, we count them each time they appear.
+
+
+
+
+
5
+
+
+
Yes, we add to x each time we come across a letter in the list.
+
+
+
+
+
0
+
+
+
Check again what the conditional is evaluating. The value of i will be a character in the string s, so what will happen in the if statement?
min_value was set to a number that was smaller than any of the numbers in the list, so it was never updated in the for loop.
+
+
+
+
+
0
+
+
+
Yes, min_value was set to a number that was smaller than any of the numbers in the list, so it was never updated in the for loop.
+
+
+
+
+
There is an error in the code so it cannot run.
+
+
+
The code does not have an error that would prevent it from running.
+
+
+
+
+
+
+
For each string in the list words, find the number of characters in the string. If the number of characters in the string is greater than 3, add 1 to the variable num_words so that num_words should end up with the total number of words with more than 3 characters.
Challenge For each word in words, add ‘d' to the end of the word if the word ends in e to make it past tense. Otherwise, add ‘ed' to make it past tense. Save these past tense words to a list called past_tense.
+
+
+
+words = ["adopt", "bake", "beam", "confide", "grill", "plant", "time", "wave", "wish"]
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testNine(self):
+ self.assertEqual(past_tense, ['adopted', 'baked', 'beamed', 'confided', 'grilled', 'planted', 'timed', 'waved', 'wished'], "Testing that the past_tense list is correct.")
+ self.assertIn("else", self.getEditorText(), "Testing output (Don't worry about actual and expected values).")
+ self.assertIn("for", self.getEditorText(), "Testing output (Don't worry about actual and expected values).")
+
+myTests().main()
+
+
+
+
+
diff --git a/pretext/Conditionals/Theinandnotinoperators.ptx b/pretext/Conditionals/Theinandnotinoperators.ptx
new file mode 100644
index 00000000..5bd802f0
--- /dev/null
+++ b/pretext/Conditionals/Theinandnotinoperators.ptx
@@ -0,0 +1,50 @@
+
+
+ The in and not in operators
+
The in operator tests if one string is a substring of another:
+
+
+print('p' in 'apple')
+print('i' in 'apple')
+print('ap' in 'apple')
+print('pa' in 'apple')
+
+
+
Note that a string is a substring of itself, and the empty string is a
+ substring of any other string. (Also note that computer scientists
+ like to think about these edge cases quite carefully!)
+
+
+print('a' in 'a')
+print('apple' in 'apple')
+print('' in 'a')
+print('' in 'apple')
+
+
+
The not in operator returns the logical opposite result of in.
+
+
+print('x' not in 'apple')
+
+
+
We can also use the in and not in operators on lists!
+
+
+print("a" in ["a", "b", "c", "d"])
+print(9 in [3, 2, 9, 10, 9.0])
+print('wow' not in ['gee wiz', 'gosh golly', 'wow', 'amazing'])
+
+
+
However, remember how you were able to check to see if an a was in apple?
+ Let's try that again to see if there's an a somewhere in the following list.
Clearly, we can tell that a is in the word apple, and absolutely, and application. For some reason
+ though, the Python interpreter returns False. Why is that? When we use the in and not in
+ operators on lists, Python checks to see if the item on the left side of the expression is equivalent
+ to an element in the item on the right side of the expression. In this case, Python is checking
+ whether or not an element of the list is the string a - nothing more or less than that.
+
diff --git a/pretext/Conditionals/WPSettingUpConditionals.ptx b/pretext/Conditionals/WPSettingUpConditionals.ptx
new file mode 100644
index 00000000..3efe4ad0
--- /dev/null
+++ b/pretext/Conditionals/WPSettingUpConditionals.ptx
@@ -0,0 +1,109 @@
+
+
+ 👩💻 Setting Up Conditionals
+
Before writing your conditionals, it can be helpful to make your own flowchart that will
+ plot out the flow of each condition. By writing out the flow, you can better determine how
+ complex the set of conditionals will be as well as check to see if any condition is not
+ taken care of before you begin writing it out.
+
To make sure that your code covers all of the conditions that you intend for it to cover, you
+ should add comments for each clause that explains what that clause is meant to do. Then, you
+ should add tests for each possible path that the program could go though. What leads
+ to certain conditional statements being executed? Is that what you intended?
+
+ Choosing your type of Conditional
+
When adding conditionals to your program, you should also consider the kinds of conditionals
+ that are at your disposal and what would fit best.
+
+
Though you'll use them often, remember that conditional statements don't always need an else clause.
+ When deciding the flow, ask yourself what you want to have happen under a certain condition.
+ For example, if you wanted to find all of the words that have the letter ‘n' in them. If there's nothing
+ that needs to happen when a word does not contain the letter ‘n' then you won't need an else
+ clause. The program should just continue onward!
+
+
+
What is the best set of conditonal statements provided based on the following prompt? You want to keep track of all the words that have the letter ‘t' and in a separate variable you want to keep track of all the words that have the letter ‘z' in them.
+
+
+
+
+
If statement - Else statement
+
+
+
Using if/else either uses an unnecessary else statement or would improperly keep track of one of the accumulator variables.
+
+
+
+
+
If statement - Elif statement
+
+
+
Using if/elif means that words that have both a "t" and a "z" would not be propperly counted by the two variables.
+
+
+
+
+
If statement - If statement
+
+
+
Yes, two if statements will keep track of - and properly update - the two different accumulator variables.
+
+
+
+
+
If statement - Elif statemenet - Else statement
+
+
+
Using if/elif/else here will provide an unnecessary else statement and improperly update one of the accumulator variables in the case where a word has both a "t" and a "z".
+
+
+
+
+
+
+
Select the most appropriate set of conditonal statements for the situation described: You want to keep track of all the words that contain both t and z.
+
+
+
+
+
If statement - Elif statemenet - Else statement
+
+
+
The elif and else statements are both unnecessary.
+
+
+
+
+
If statement - Else statement
+
+
+
The else statement is unnecessary.
+
+
+
+
+
If statement - Nested If statement
+
+
+
Though you could write a set of conditional statements like this and answer the prompt, there is a more concise way.
+
+
+
+
+
If statement
+
+
+
Yes, this is the most concise way of writing a conditional for that prompt.
+
+
+
+
+
If statement - Nested If statement - Else statement
+
+
+
The else statement is unnecessary.
+
+
+
+
+
+
diff --git a/pretext/Conditionals/intro-TurtlesandConditionals.ptx b/pretext/Conditionals/intro-TurtlesandConditionals.ptx
new file mode 100644
index 00000000..64153d79
--- /dev/null
+++ b/pretext/Conditionals/intro-TurtlesandConditionals.ptx
@@ -0,0 +1,112 @@
+
+
+ Intro: What we can do with Turtles and Conditionals
+
So far, our programs have either been a series of statements which always execute sequentially or operations that are applied to each item in an iterable. Yet programs frequently need to be more subtle with their behavior. For example, a messaging app might only set a message's title bold if it has not been read by the user. Or a video game needs to update the position of all the characters that are not asleep. This is done with something called a selection or a conditional statement.
+
In the context of turtle drawings, using this kind of statement will allow us to check conditions and change the behavior of the program accordingly
In the above code, we first set amy's pen color to be Pink and then move her forward. Next we want one of
+ two actions to happen, either amy should move right and then forward, or left and then forward. The direction
+ that we want her to go in depends on her pen color. If her pen color is set to pink - which is determined by
+ writing amy.pencolor() == "Pink" which checks to see if the value returned by amy.pencolor() is the
+ equivalent to the string Pink - then we should have her move right and forward. Else (or otherwise) she
+ should move left and forward. Both things can't happen though. She can't move right, forward and left,
+ forward. We then do the same thing for kenji, though in this case, we didn't change kenji's pen color.
+
It might seem a bit odd to add the conditionals in this example. Wouldn't we already know that we set up amy
+ and kenji's colors, so why would we need a conditional? While it's true that this isn't the best place to
+ use a conditional, we can combine conditional statements with for loops to make something pretty cool!
The above example combines a for loop with a set of conditional statements. Here, we loop through a list of
+ colors and each iteration checks to see what amy's pen color is. Depending on the pen color, the turtle will
+ move in a certain direction, for a certain distance. Before the for loop iterates, amy's pen color is changed
+ to whatever color is in the for loop and it continues. Note how the color doesn't change until the end,
+ so that we can start using whatever color amy is set to initally. This means that the last color in the list
+ colors will not be used, though you can see how the icon changes to the appropriate color.
+
This chapter will further detail how to use conditional statements.
+
+ Learning Goals
+
+
+
+
To understand boolean expressions and logical operators
+
+
+
To understand conditional execution
+
+
+
To be able to write a boolean function
+
+
+
To know when to use binary, unary, chained and nested conditional statements
+
+
+
+
+
+ Objectives
+
+
+
+
To properly evaluate a (compound) boolean expression
+
+
+
To use parenthesis to properly demonstrate operator precedence
+
+
+
To use conditional statements to properly branch code
+
+
+
+
+
diff --git a/pretext/Conditionals/toctree.ptx b/pretext/Conditionals/toctree.ptx
new file mode 100644
index 00000000..0c6de0bd
--- /dev/null
+++ b/pretext/Conditionals/toctree.ptx
@@ -0,0 +1,18 @@
+
+
+ Conditionals
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pretext/Conditionals/week3a1.ptx b/pretext/Conditionals/week3a1.ptx
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ b/pretext/Conditionals/week3a1.ptx
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git "a/pretext/Conditionals/week3a1.ptx\n" "b/pretext/Conditionals/week3a1.ptx\n"
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ "b/pretext/Conditionals/week3a1.ptx\n"
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git a/pretext/Debugging/BeginningtipsforDebugging.ptx b/pretext/Debugging/BeginningtipsforDebugging.ptx
new file mode 100644
index 00000000..c695596a
--- /dev/null
+++ b/pretext/Debugging/BeginningtipsforDebugging.ptx
@@ -0,0 +1,30 @@
+
+
+ 👩💻 Beginning tips for Debugging
+
Debugging a program is a different way of thinking than writing a program. The process of debugging is much more like being a detective. Here are a few rules to get you thinking about debugging.
+
+
+
+
Everyone is a suspect (Except Python)! It's common for beginner programmers to blame Python, but that should be your last resort. Remember that Python has been used to solve CS1 level problems millions of times by millions of other programmers. So, Python is probably not the problem.
+
+
+
Check your assumptions. At this point in your career you are still developing your mental model of how Python does its work. Its natural to think that your code is correct, but with debugging you need to make your code the primary suspect. Even if you think it is right, you should verify that it really is by liberally using print statements to verify that the values of variables really are what you think they should be. You'll be surprised how often they are not.
+
+
+
Find clues. This is the biggest job of the detective and right now there are two important kinds of clues for you to understand.
+
+
+
+
Error Messages
+
+
+
Print Statements
+
+
+
+
+
+
+
Three kinds of errors can occur in a program: syntax errors, runtime errors, and semantic errors. It is useful to distinguish
+ between them in order to track them down more quickly.
Many problems in your program will lead to an error message. For example as I was
+ writing and testing this chapter of the book I wrote the following version of the
+ example program in the previous section.
+
+
+current_time_str = input("What is the current time (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait")
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_int)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
+
Can you see what is wrong, just by looking at the code? Maybe, maybe not. Our brain
+ tends to see what we think is there, so sometimes it is very hard to find the problem
+ just by looking at the code. Especially when it is our own code and we are sure that
+ we have done everything right!
+
Let's try the program again, but this time in an activecode:
+
+
+current_time_str = input("What is the current time (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait")
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_int)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
+
Aha! Now we have an error message that might be useful. The name error tells us
+ that wait_time_int is not defined. It also tells us that the error is on line 5.
+ That's really useful information. Now look at line five and you will see that
+ wait_time_int is used on both the left and the right hand side of the assignment
+ statement.
+
+
The error descriptions you see in activecode may be different (and more understandable!) than in a regular
+ Python interpreter. The interpreter in activecode is limited in many ways, but it is intended for beginners,
+ including the wording chosen to describe errors.
+
+
+
+
Which of the following explains why wait_time_int = int(wait_time_int) is an error?
+
+
+
+
+
You cannot use a variable on both the left and right hand sides of an assignment statement.
+
+
+
No, You can, as long as all the variables on the right hand side already have values.
+
+
+
+
+
wait_time_int does not have a value so it cannot be used on the right hand side.
+
+
+
Yes. Variables must already have values in order to be used on the right hand side.
+
+
+
+
+
This is not really an error, Python is broken.
+
+
+
No, No, No!
+
+
+
+
+
In writing and using this book over the last few years we have collected a lot of
+ statistics about the programs in this book. Here are some statistics about error
+ messages for the exercises in this book.
+
+
Most of the error messages encountered are SyntaxError,
+ TypeError, NameError, or ValueError. We will look at these errors in three stages:
+
+
+
+
First we will define what these four error messages mean.
+
+
+
Then, we will look at some examples that cause these errors to occur.
+
+
+
Finally we will look at ways to help uncover the root cause of these messages.
+
+
+
+
+ SyntaxError
+
Syntax errors happen when you make an error in the syntax of your program. Syntax
+ errors are like making grammatical errors in writing. If you don't use periods and
+ commas in your writing then you are making it hard for other readers to figure out
+ what you are trying to say. Similarly Python has certain grammatical rules that must
+ be followed or else Python can't figure out what you are trying to say.
+
Usually SyntaxErrors can be traced back to missing punctuation characters, such as
+ parentheses, quotation marks, or commas. Remember that in Python commas are used to
+ separate parameters to functions. Paretheses must be balanced, or else Python thinks
+ that you are trying to include everything that follows as a parameter to some function.
+
Here are a couple examples of Syntax errors in the example program we have been using.
+ See if you can figure out what caused them.
+
+
+
Find and fix the error in the following code.
+
+
+
+current_time_str = input("What is the current time (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait"
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
+
+ current_time_str = input("What is the current time (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait"
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+ Since the error message points us to line 4 this might be a bit confusing. If
+ you look at line four carefully you will see that there is no problem with the
+ syntax. So, in this case the next step should be to back up and look at the
+ previous line. In this case if you look at line 2 carefully you will see that
+ there is a missing right parenthesis at the end of the line. Remember that
+ parentheses must be balanced. Since Python allows statements to continue over
+ multiple lines inside parentheses python will continue to scan subsequent
+ lines looking for the balancing right parenthesis. However in this case it
+ finds the name current_time_int and it will want to interpret that as
+ another parameter to the input function. But, there is not a comma to
+ separate the previous string from the variable so as far as Python is
+ concerned the error here is a missing comma. From your perspective its a
+ missing parenthesis.
+
+
+current_time_str = input("What is the current time (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait"
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
Since the error message points us to line 4 this might be a bit confusing. If
+ you look at line four carefully you will see that there is no problem with the
+ syntax. So, in this case the next step should be to back up and look at the
+ previous line. In this case if you look at line 2 carefully you will see that
+ there is a missing right parenthesis at the end of the line. Remember that
+ parentheses must be balanced. Since Python allows statements to continue over
+ multiple lines inside parentheses python will continue to scan subsequent
+ lines looking for the balancing right parenthesis. However in this case it
+ finds the name current_time_int and it will want to interpret that as
+ another parameter to the input function. But, there is not a comma to
+ separate the previous string from the variable so as far as Python is
+ concerned the error here is a missing comma. From your perspective its a
+ missing parenthesis.
+
+
+
Finding Clues How can you help yourself find these problems? One trick that can be
+ very valuable in this situation is to simply start by commenting out the line number
+ that is flagged as having the error. If you comment out line four, the error message
+ now changes to point to line 5. Now you ask yourself, am I really that bad that I
+ have two lines in a row that have errors on them? Maybe, so taken to the extreme, you
+ could comment out all of the remaining lines in the program. Now the error message
+ changes to TokenError: EOF in multi-line statement This is a very technical way
+ of saying that Python got to the end of file (EOF) while it was still looking for
+ something. In this case a right parenthesis.
+
+
+
Find and fix the error in the following code.
+
+
+
+current_time_str = input("What is the "current time" (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait")
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
+
+ current_time_str = input("What is the "current time" (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait")
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+ The error message points you to line 1 and in this case that is exactly where
+ the error occurs. In this case your biggest clue is to notice the difference
+ in highlighting on the line. Notice that the words current time are a
+ different color than those around them. Why is this? Because current time
+ is in double quotes inside another pair of double quotes Python thinks that
+ you are finishing off one string, then you have some other names and finally
+ another string. But you haven't separated these names or strings by commas,
+ and you haven't added them together with the concatenation operator (+). So,
+ there are several corrections you could make. First you could make the
+ argument to input be as follows: "What is the 'current time' (in hours 0-23)
+" Notice that here we have correctly used single quotes inside double quotes
+ . Another option is to simply remove the extra double quotes. Why were you
+ quoting current time anyway? "What is the current time (in hours 0-23)"
+
+
+current_time_str = input("What is the "current time" (in hours 0-23)?")
+wait_time_str = input("How many hours do you want to wait")
+
+current_time_int = int(current_time_str)
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
The error message points you to line 1 and in this case that is exactly where
+ the error occurs. In this case your biggest clue is to notice the difference
+ in highlighting on the line. Notice that the words current time are a
+ different color than those around them. Why is this? Because current time
+ is in double quotes inside another pair of double quotes Python thinks that
+ you are finishing off one string, then you have some other names and finally
+ another string. But you haven't separated these names or strings by commas,
+ and you haven't added them together with the concatenation operator (+). So,
+ there are several corrections you could make. First you could make the
+ argument to input be as follows: "What is the 'current time' (in hours 0-23)
+" Notice that here we have correctly used single quotes inside double quotes
+ . Another option is to simply remove the extra double quotes. Why were you
+ quoting current time anyway? "What is the current time (in hours 0-23)"
+
+
+
Finding Clues If you follow the same advice as for the last problem, comment out
+ line one, you will immediately get a different error message. Here's where you need
+ to be very careful and not panic. The error message you get now is: NameError: name
+'current_time_str' is not defined on line 4. You might be very tempted to think
+ that this is somehow related to the earlier problem and immediately conclude that
+ there is something wrong with the variable name current_time_str but if you
+ reflect for a minute you will see that by commenting out line one you have caused a
+ new and unrelated error. That is you have commented out the creation of the name
+ current_time_str. So of course when you want to convert it to an int you will
+ get the NameError. Yes, this can be confusing, but it will become much easier with
+ experience. It's also important to keep calm, and evaluate each new clue carefully so
+ you don't waste time chasing problems that are not really there.
+
Uncomment line 1 and you are back to the SyntaxError. Another track is to eliminate a
+ possible source of error. Rather than commenting out the entire line you might just
+ try to assign current_time_str to a constant value. For example you might make
+ line one look like this: current_time_str = "10" #input("What is the "current
+time" (in hours 0-23)?"). Now you have assigned current_time_str to the string
+ 10, and commented out the input statement. And now the program works! So you
+ conclude that the problem must have something to do with the input function.
+
+
+ TypeError
+
TypeErrors occur when you try to combine two objects that are not compatible. For
+ example you try to add together an integer and a string. Usually type errors can be
+ isolated to lines that are using mathematical operators, and usually the line number
+ given by the error message is an accurate indication of the line.
+
Here's an example of a type error created by a Polish learner. See if you can find
+ and fix the error.
+
+
+a = input('wpisz godzine')
+x = input('wpisz liczbe godzin')
+int(x)
+int(a)
+h = x // 24
+s = x % 24
+print (h, s)
+a = a + s
+print ('godzina teraz', a)
+
+
+
+
+ Solution
+
In finding this error there are few lessons to think about. First, you may
+ find it very disconcerting that you cannot understand the whole program.
+ Unless you speak Polish then this won't be an issue. But, learning what you
+ can ignore, and what you need to focus on is a very important part of the
+ debugging process. Second, types and good variable names are important and
+ can be very helpful. In this case a and x are not particularly helpful names,
+ and in particular they do not help you think about the types of your
+ variables, which as the error message implies is the root of the problem here.
+ The rest of the lessons we will get back to in a minute.
+
The error message provided to you gives you a pretty big hint.
+ TypeError: unsupported operand type(s) for FloorDiv: 'str' and 'number' on line: 5
+ On line five we are trying to use integer division on x and 24. The error
+ message tells you that you are tyring to divide a string by a number. In this
+ case you know that 24 is a number so x must be a string. But how? You can
+ see the function call on line 3 where you are converting x to an integer.
+ int(x) or so you think. This is lesson three and is one of the most
+ common errors we see in introductory programming. What is the difference
+ between int(x) and x = int(x)
+
+
+
+
The expression int(x) converts the string referenced by x to an integer but it does not store it anywhere. It is very common to assume that int(x) somehow changes x itself, as that is what you are intending! The thing that makes this very tricky is that int(x) is a valid expression, so it doesn't cause any kind of error, but rather the error happens later on in the program.
+
+
+
The assignment statement x = int(x) is very different. Again, the int(x) expression converts the string referenced by x to an integer, but this time it also changes what x references so that x now refers to the integer value returned by the int function.
+
+
+
+
So, the solution to this problem is to change lines 3 and 4 so they are
+ assignment statements.
+
+
+
Finding Clues One thing that can help you in this situation is to print out the
+ values and the types of the variables involved in the statement that is causing the
+ error. You might try adding a print statement after line 4 print(x, type(x)) You
+ will see that at least we have confirmed that x is of type string. Now you need to
+ start to work backward through the program. You need to ask yourself, where is x used
+ in the program? x is used on lines 2, 3, and of course 5 and 6 (where we are getting
+ an error). So maybe you move the print statement to be after line 2 and again after 3.
+ Line 3 is where you expect the value of x to be changed to an integer. Could line 4
+ be mysteriously changing x back to a string? Not very likely. So the value and type
+ of x is just what you would expect it to be after line 2, but not after line 3. This
+ helps you isolate the problem to line 3. In fact if you employ one of our earlier
+ techniques of commenting out line 3 you will see that this has no impact on the error,
+ and is a big clue that line 3 as it is currently written is useless.
+
+
+ NameError
+
Name errors almost always mean that you have used a variable before it has a value.
+ Often NameErrors are simply caused by typos in your code. They can be hard to spot if
+ you don't have a good eye for catching spelling mistakes. Other times you may simply
+ mis-remember the name of a variable or even a function you want to call. You have
+ seen one example of a NameError at the beginning of this section. Here is another one.
+ See if you can get this program to run successfully:
+
+
+str_time = input("What time is it now?")
+str_wait_time = input("What is the number of hours to wait?")
+time = int(str_time)
+wai_time = int(str_wait_time)
+
+time_when_alarm_go_off = time + wait_time
+print(time_when_alarm_go_off)
+
+
+
+
+ Solution
+
In this example, the student seems to be a fairly bad speller, as there are a
+ number of typos to fix. The first one is identified as wait_time is not
+ defined on line 6. Now in this example you can see that there is
+ str_wait_time on line 2, and wai_time on line 4 and wait_time on
+ line 6. If you do not have very sharp eyes its easy to miss that there is a
+ typo on line 4.
+
+
+
Finding Clues With name errors one of the best things you can do is use the
+ editor, or browser search function. Quite often if you search for the exact word in the
+ error message one of two things will happen:
+
1. The word you are searching for will appear only once in your code, it's also likely
+ that it will be on the right hand side of an assignment statement, or as a parameter to
+ a function. That should confirm for you that you have a typo somewhere. If the name in
+ question is what you thought it should be then you probably have a typo on the left
+ hand side of an assignment statement on a line before your error message occurs. Start
+ looking backward at your assignment statements. In some cases it's really nice to
+ leave all the highlighted strings from the search function visible as they will help
+ you very quickly find a line where you might have expected your variable to be
+ highlighted.
+
2. The second thing that may happen is that you will be looking directly at a line
+ where you expected the search to find the string in question, but it will not be
+ highlighted. Most often that will be the typo right there.
+
Here is another one for you to try:
+
+
+n = input("What time is it now (in hours)?")
+n = imt(n)
+m = input("How many hours do you want to wait?")
+m = int(m)
+sum_time = n + m
+time_of_day = sum_time % 12
+print("The time is now", time_of_day)
+
+
+
+
+ Solution
+
This one is once again a typo, but the typo is not in a variable name, but
+ rather, the name of a function. The search strategy would help you with this
+ one easily, but there is another clue for you as well. The editor in the
+ textbook, as well as almost all Python editors in the world provide you with
+ color clues. Notice that on line 2 the function imt is not highlighted
+ blue like the word int on line 4.
In this example the error message is about set_time not defined on line 3.
+ In this case the undefined name is not used in an assignment statement, but is
+ used as a parameter (incorrectly) to a function call. A search on set_time
+ reveals that in fact it is only used once in the program. Did the author mean
+ set_alarm? If we make that assumption we immediately get another error
+ NameError: name 'alarm_time' is not defined on line: 3. The variable
+ alarm_time is defined on line 4, but that does not help us on line 3.
+ Furthermore we now have to ask the question is this function call
+ int(present_time, set_alarm, alarm_time) even the correct use of the
+ int function? The answer to that is a resounding no. Let's list all of the
+ things wrong with line 3:
+
+
+
+
set_time is not defined and never used, the author probably meant set_alarm.
+
+
+
alarm_time cannot be used as a parameter before it is defined, even on the next line!
+
+
+
int can only convert one string to an integer at a time.
+
+
+
Finally, int should be used in an assignment statement. Even if int was called with the correct number of parameters it would have no real effect.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ValueError
+
Value errors occur when you pass a parameter to a function and the function is
+ expecting a certain limitations on the values, and the value passed is not compatible.
+ We can illustrate that with this particular program in two different ways.
+
+
+current_time_str = input("What is the current time (in hours 0-23)?")
+current_time_int = int(current_time_str)
+
+wait_time_str = input("How many hours do you want to wait")
+wait_time_int = int(wait_time_str)
+
+final_time_int = current_time_int + wait_time_int
+print(final_time_int)
+
+
+
Run the program but instead of typing in anything to the dialog box just click OK. You
+ should see the following error message: ValueError: invalid literal for int() with
+base 10: '' on line: 4 This error is not because you have made a mistake in your
+ program. Although sometimes we do want to check the user input to make sure its valid,
+ but we don't have all the tools we need for that yet. The error happens because the
+ user did not give us something we can convert to an integer, instead we gave it an
+ empty string. Try running the program again. Now this time enter ten instead of
+ the number 10. You will get a similar error message.
+
ValueErrors are not always caused by user input error, but in this program that is the
+ case. We'll look again at ValueErrors again when we get to more complicated programs.
+ For now it is worth repeating that you need to keep track of the restrictions needed
+ for your variables, and understand what your function is expecting. You can do this by
+ writing comments in your code, or by naming your variables in a way that reminds you of
+ their proper form.
The second type of error is a runtime error. A program with a runtime error
+ is one that passed the interpreter's syntax checks, and started to execute.
+ However, during the execution of one of the statements in the program, an error
+ occurred that caused the interpreter to stop executing the program and display
+ an error message. Runtime errors are also called exceptions because they usually
+ indicate that something exceptional (and bad) has happened.
+
Here are some examples of common runtime errors you are sure to encounter:
+
+
+
+
Misspelled or incorrectly capitalized variable and function names
+
+
+
Attempts to perform operations (such as math operations) on data of the wrong type (ex.
+ attempting to subtract two variables that hold string values)
+
+
+
Dividing by zero
+
+
+
Attempts to use a type conversion function such as int on a value that can't be converted to an int
+
+
+
+
The following program contains various runtime errors. Can you spot any of them?
+ After locating the error, run the program to see the error message.
Notice the following important differences between syntax errors and runtime errors that can help you as you try to diagnose
+ and repair the problem:
+
+
+
+
If the error message mentions SyntaxError, you know that the problem has to do with syntax: the structure of the code,
+ the punctuation, etc.
+
+
+
If the program runs partway and then crashes, you know the problem is a runtime error. Programs with syntax errors
+ don't execute even one line.
+
+
+
+
Stay tuned for more details on the various types of runtime errors. We have a whole section of this
+ chapter dedicated to that topic.
+
+ Check your understanding
+
+
+
+
Which of the following is a run-time error?
+
+
+
+
+
Attempting to divide by 0.
+
+
+
Python cannot reliably tell if you are trying to divide by 0 until it is executing your program (e.g., you might be asking the user for a value and then dividing by that value—you cannot know what value the user will enter before you run the program).
+
+
+
+
+
Forgetting a right-parenthesis ) when invoking a function.
+
+
+
This is a problem with the formal structure of the program. Python knows where colons are required and can detect when one is missing simply by looking at the code without running it.
+
+
+
+
+
Forgetting to divide by 100 when printing a percentage amount.
+
+
+
This will produce the wrong answer, but Python will not consider it an error at all. The programmer is the one who understands that the answer produced is wrong.
+
+
+
+
+
+
+
Who or what typically finds runtime errors?
+
+
+
+
+
The programmer.
+
+
+
Programmers rarely find all the runtime errors, there is a computer program that will do it for us.
+
+
+
+
+
The interpreter.
+
+
+
If an instruction is illegal to perform at that point in the execution, the interpreter will stop with a message describing the exception.
+
+
+
+
+
The computer.
+
+
+
Well, sort of. But it is a special thing in the computer that does it. The stand alone computer without this additional piece can not do it.
+
+
+
+
+
The teacher / instructor.
+
+
+
Your teacher and instructor may be able to find most of your runtime errors, but only because they have experience looking at code and possibly writing code. With experience runtime errors are easier to find. But we also have an automated way of finding these types of errors.
The third type of error is the semantic error, also called a logic error. If there is a semantic error
+ in your program, it will run successfully in the sense that the computer will
+ not generate any error messages. However, your program will not do the right thing. It will do
+ something else. Specifically, it will do what you told it to do, not what you wanted it to do.
+
The following program has a semantic error. Execute it to see what goes wrong:
+
+
+num1 = input('Enter a number:')
+num2 = input('Enter another number:')
+sum = num1 + num2
+
+print('The sum of', num1, 'and', num2, 'is', sum)
+
+
+
This program runs and produces a result. However, the result is not what the programmer intended. It contains
+ a semantic error. The error is that the program performs concatenation instead of addition, because the programmer
+ failed to write the code necessary to convert the inputs to integers.
+
With semantic errors, the problem is that the program you wrote is not the program you wanted to
+ write. The meaning of the program (its semantics) is wrong. The computer is
+ faithfully carrying out the instructions you wrote, and its results
+ are correct, given the instructions that you provided. However, because your instructions
+ have a flaw in their design, the program does not behave as desired.
+
Identifying semantic errors can be tricky because no error message appears to make it obvious that the results are
+ incorrect. The only way you can detect semantic errors is if you know in advance what the program should do for a given set
+ of input. Then, you run the program with that input data and compare the output of the program with what you expect. If
+ there is a discrepancy between the actual output and the expected output, you can conclude that there is either 1) a
+ semantic error or 2) an error in your expected results.
+
Once you've determined that you have a semantic error, locating it can be tricky because you must work
+ backward by looking at the output of the program and trying to figure out what it is doing.
+
+ Test Cases
+
To detect a semantic error in your program, you need the help of something called a test case.
+
+ Test Case
+
A test case is a set of input values for the program, together with the output that you expect the program should produce when it is run with those particular
+ inputs.
+
+
Here is an example of a test case for the program above:
+
Test Case
+---------
+Input: 2, 3
+Expected Output: 5
+
If you give this test case to someone and ask them to test the program, they can type in the inputs, observe the output,
+ check it against the expected output, and determine whether a semantic error exists based on whether the actual output
+ matches the expected output or not. The tester doesn't even have to know what the program is supposed to do. For this reason,
+ software companies often have separate quality assurance departments whose responsibility is to check that the programs written
+ by the programmers perform as expected. The testers don't have to be programmers; they just have to be able to operate the
+ program and compare its results with the test cases they're given.
+
In this case, the program is so simple that we don't need to write down a test case at all; we can compute the expected output
+ in our heads with very little effort. More complicated programs require effort to create the test case (since you shouldn't use
+ the program to compute the expected output; you have to do it with a calculator or by hand), but the effort pays off when
+ the test case helps you to identify a semantic error that you didn't know existed.
+
Semantic errors are the most dangerous of the three types of errors, because in some cases they are not noticed by either
+ the programmers or the users who use the program. Syntax errors cannot go undetected (the program won't run at all if
+ they exist), and runtime errors are usually also obvious and typically detected by developers before a program is
+ released for use (although it is possible for a runtime error to occur for some inputs and not for
+ others, so these can sometimes remain undetected for a while). However, programs often go for years with undetected
+ semantic errors; no one realizes that the program has been producing incorrect results. They just assume that because the
+ results seem reasonable, they are correct. Sometimes, these errors are relatively harmless. But if they involve
+ financial transactions or medical equipment, the results can be harmful or even deadly. For this reason, creating test
+ cases is an important part of the work that programmers perform in order to help them produce programs that work
+ correctly.
+
+ Check your understanding
+
+
+
+
Which of the following is a semantic error?
+
+
+
+
+
Attempting to divide by 0.
+
+
+
A semantic error is an error in logic. In this case the program does not produce the correct output because the problem is not solved correctly. This would be considered a run-time error.
+
+
+
+
+
Forgetting a right-parenthesis ) when invoking a function.
+
+
+
A semantic error is an error in logic. In this case the program does not produce the correct output because the code can not be processed by the compiler or interpreter. This would be considered a syntax error.
+
+
+
+
+
Forgetting to divide by 100 when printing a percentage amount.
+
+
+
This will produce the wrong answer because the programmer implemented the solution incorrectly. This is a semantic error.
+
+
+
+
+
+
+
Who or what typically finds semantic errors?
+
+
+
+
+
The programmer.
+
+
+
You must fully understand the problem so that you can tell if your program properly solves it.
+
+
+
+
+
The compiler / interpreter.
+
+
+
The compiler and / or interpreter will only do what you instruct it to do. It does not understand what the problem is that you want to solve.
+
+
+
+
+
The computer.
+
+
+
The computer does not understand your problem. It just executes the instructions that it is given.
+
+
+
+
+
The teacher / instructor.
+
+
+
Your teacher and instructor may be able to find most of your semantic errors, but only because they have experience solving problems. However it is your responsibility to understand the problem so you can develop a correct solution.
Python can only execute a program if the program is syntactically correct;
+ otherwise, the process fails and returns an error message. Syntax refers
+ to the structure of a program and the rules about that structure. For example,
+ in English, a sentence must begin with a capital letter and end with a period.
+ this sentence contains a syntax error. So does this one
+
In Python, rules of syntax include requirements like these: strings must be enclosed in quotes; statements must
+ generally be written one per line; the print statement must enclose the value to be displayed in parenthesis;
+ expressions must be correctly formed. The following lines contain syntax errors:
For most readers of English, a few syntax errors are not a significant problem, which is why we can read the poetry of
+ e. e. cummings without problems. Python is not so forgiving. When you run a Python program, the interpreter checks it
+ for syntax errors before beginning to execute the first statement. If there is a single syntax error anywhere in your
+ program, Python will display an error message and quit without executing any of the program.
+
To see a syntax error in action, look at the following program. Can you spot the error?
+ After locating the error, run the program to see the error message.
The error message clearly indicates that the problem is a SyntaxError. This lets you know the problem
+ is not one of the other two types of errors we'll discuss shortly.
+
+
+
The error is on line 2 of the program. However, even though there is nothing
+ wrong with line 1, the print statement does not execute — none of the program successfully executes
+ because of the presence of just one syntax error.
+
+
+
The error gives the line number where Python believes the error exists. In this case, the error message pinpoints the
+ location correctly. But in other cases, the line number can be inaccurate or entirely missing.
+
To see an example of the latter, try removing just the right parenthesis ) from line 2 and
+ running the program again. Notice how the error message gives no line number at all. With syntax errors, you need to be
+ prepared to hunt around a bit in order to locate the trouble.
+
+
+
+
One aspect of syntax you have to watch out for in Python involves indentation. Python requires you to begin all
+ statements at the beginning of the line, unless you are using a flow control statement like a for or an if statement
+ (we'll discuss these soon… stay tuned!). To see an example of this kind of problem, modify the program above by inserting a
+ couple of spaces at the beginning of one of the lines.
+
+ Check your understanding
+
+
+
+
Which of the following is a syntax error?
+
+
+
+
+
Attempting to divide by 0.
+
+
+
A syntax error is an error in the structure of the python code that can be detected before the program is executed. Python cannot usually tell if you are trying to divide by 0 until it is executing your program (e.g., you might be asking the user for a value and then dividing by that value—you cannot know what value the user will enter before you run the program).
+
+
+
+
+
Forgetting a right-parenthesis ) when invoking a function.
+
+
+
This is a problem with the formal structure of the program. Python knows where parentheses are required and can detect when one is missing simply by analyzing the code without running it.
+
+
+
+
+
Forgetting to divide by 100 when printing a percentage amount.
+
+
+
This will produce the wrong answer, but Python will not consider it an error at all. The programmer is the one who understands that the answer produced is wrong.
+
+
+
+
+
+
+
Who or what typically finds syntax errors?
+
+
+
+
+
The programmer.
+
+
+
Programmers rarely find all the syntax errors, there is a computer program that will do it for us.
+
+
+
+
+
The compiler / interpreter.
+
+
+
The compiler and / or interpreter is a computer program that determines if your program is written in a way that can be translated into machine language for execution.
+
+
+
+
+
The computer.
+
+
+
Well, sort of. But it is a special thing in the computer that does it. The stand alone computer without this additional piece can not do it.
+
+
+
+
+
The teacher / instructor.
+
+
+
Your teacher and instructor may be able to find most of your syntax errors, but only because they have experience looking at code and possibly writing code. With experience syntax errors are easier to find. But we also have an automated way of finding these types of errors.
+
+
+
+
+
diff --git a/pretext/Debugging/intro-DebuggingGeneral.ptx b/pretext/Debugging/intro-DebuggingGeneral.ptx
new file mode 100644
index 00000000..41c47ca4
--- /dev/null
+++ b/pretext/Debugging/intro-DebuggingGeneral.ptx
@@ -0,0 +1,38 @@
+
+
+ Introduction to Debugging
+
The art of debugging is figuring out what you really told your program to do rather than what you thought you told it to do. — Andrew Singer
+
This chapter will spend some time talking about what happens when errors occur as well as how to fix
+ the errors that you will inevitably come across.
+
Before computers became digital, debugging could mean looking for insects impeding the functioning of physical relays as in this somewhat apocryphal tale about Admiral Grace Hopper, a pioneer of computer programming.
+
Nowadays, debugging doesn't involve bug guts all over your computer but it can still be just as frustrating. To cope with this frustration, this chapter will present some strategies to help you understand why the program you wrote does not behave as intended.
+
Many people think debugging is some kind of punishment for not being smart enough to write code correctly the first time. But nobody does that, failure in programming is part of the deal. Here's a fun video to keep in mind as you learn to program.
+
+
CC BY–NC–ND 4.0 International Ted.com
+
+ Learning Goals
+
+
+
+
To understand good programming strategies to avoid errors
+
+
+
To understand common kinds of exceptions and their likely causes
+
+
+
+
+
+ Objectives
+
+
+
+
Given a piece of code identify the Syntax errors based on error messages
+
+
+
Given a piece of code find the (ValueError, TypeError, SyntaxError, ParseError, NameError)
+
+
+
+
+
diff --git a/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx b/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx
new file mode 100644
index 00000000..bf046903
--- /dev/null
+++ b/pretext/Debugging/intro-HowtobeaSuccessfulProgrammer.ptx
@@ -0,0 +1,6 @@
+
+
+ 👩💻 Programming in the Real World
+
Before we dive into the nitty gritty details of debugging, here is a video to give you a flavor for what its like to be a programmer in the real world.
+
+
diff --git a/pretext/Debugging/toctree.ptx b/pretext/Debugging/toctree.ptx
new file mode 100644
index 00000000..6d1f1144
--- /dev/null
+++ b/pretext/Debugging/toctree.ptx
@@ -0,0 +1,12 @@
+
+
+ Debugging
+
+
+
+
+
+
+
+
+
diff --git a/pretext/Dictionaries/AccumulatingResultsFromaDictionary.ptx b/pretext/Dictionaries/AccumulatingResultsFromaDictionary.ptx
new file mode 100644
index 00000000..3f340052
--- /dev/null
+++ b/pretext/Dictionaries/AccumulatingResultsFromaDictionary.ptx
@@ -0,0 +1,86 @@
+
+
+ Accumulating Results From a Dictionary
+
Just as we have iterated through the elements of a list to accumulate a result,
+ we can also iterate through the keys in a dictionary, accumulating a result that may
+ depend on the values associated with each of the keys.
+
For example, suppose that we wanted to compute a Scrabble score for the Study in Scarlet
+ text. Each occurrence of the letter ‘e' earns one point, but ‘q' earns 10. We have
+ a second dictionary, stored in the variable letter_values. Now, to compute the
+ total score, we start an accumulator at 0 and go through each of the letters in the
+ counts dictionary. For each of those letters that has a letter value (no points for spaces,
+ punctuation, capital letters, etc.), we add to the total score.
+
+
+f = open('scarlet2.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+letter_counts = {} # start with an empty dictionary
+for c in txt:
+ if c not in letter_counts:
+ # we have not seen this character before, so initialize a counter for it
+ letter_counts[c] = 0
+
+ #whether we've seen it before or not, increment its counter
+ letter_counts[c] = letter_counts[c] + 1
+
+letter_values = {'a': 1, 'b': 3, 'c': 3, 'd': 2, 'e': 1, 'f':4, 'g': 2, 'h':4, 'i':1, 'j':8, 'k':5, 'l':1, 'm':3, 'n':1, 'o':1, 'p':3, 'q':10, 'r':1, 's':1, 't':1, 'u':1, 'v':4, 'w':4, 'x':8, 'y':4, 'z':10}
+
+tot = 0
+for letter in letter_counts:
+ if letter in letter_values:
+ tot = tot + letter_values[letter] * letter_counts[letter]
+
+print(tot)
+
+
+
Line 18 is the tricky one. We are updating the variable tot to have its old number plus the score for the current letter times the number of occurrences of that letter.
+ Try changing some of the letter values and see how it affects the total. Try changing txt to be just a single word that you might play in Scrabble.
+
+ Check your Understanding
+
+
+
+
1. The dictionary travel contains the number of countries within each continent that Jackie has traveled to. Find the total number of countries that Jackie has been to, and save this number to the variable name total. Do not hard code this!
2.schedule is a dictionary where a class name is a key and its value is how many credits it was worth. Go through and accumulate the total number of credits that have been earned so far and assign that to the variable total_credits. Do not hardcode.
Now what if we want to find the key associated with the maximum value? It would be nice to just find
+ the maximum value as above, and then look up the key associated with it, but dictionaries don't work
+ that way. You can look up the value associated with a key, but not the key associated with a value. (The
+ reason for that is there may be more than one key that has the same value).
+
The trick is to have the accumulator keep track of the best key so far instead of the best value so far.
+ For simplicity, let's assume that there are at least two keys in the dictionary. Then, similar to our
+ first version of computing the max of a list, we can initialize the best-key-so-far to be the first key,
+ and loop through the keys, replacing the best-so-far whenever we find a better one.
+
In the exercise below, we have provided skeleton code. See if you can fill it in. An answer is provided,
+ but you'll learn more if you try to write it yourself first.
+
+
+
Write a program that finds the key in a dictionary that has the maximum value. If
+ two keys have the same maximum value, it's OK to print out either one. Fill
+ in the skeleton code
+
+
+
+d = {'a': 194, 'b': 54, 'c':34, 'd': 44, 'e': 312, 'full':31}
+
+ks = d.keys()
+# initialize variable best_key_so_far to be the first key in d
+for k in ks:
+ # check if the value associated with the current key is
+ # bigger than the value associated with the best_key_so_far
+ # if so, save the current key as the best so far
+
+print("key " + best_key_so_far + " has the highest value, " + str(d[best_key_so_far]))
+
+
+
+
+
+d = {'a': 194, 'b': 54, 'c':34, 'd': 44, 'e': 312, 'full':31}
+
+ks = d.keys()
+best_key_so_far = list(ks)[0] # Have to turn ks into a real list before using [] to select an item
+for k in ks:
+ if d[k] > d[best_key_so_far]:
+ best_key_so_far = k
+
+print("key " + best_key_so_far + " has the highest value, " + str(d[best_key_so_far]))
+
+
+
+
+
+ Check your Understanding
+
+
+
+
1. Create a dictionary called d that keeps track of all the characters in the string placement and notes how many times each character was seen. Then, find the key with the lowest value in this dictionary and assign that key to key_with_min_value.
+
+
+
+placement = "Beaches are cool places to visit in spring however the Mackinaw Bridge is near. Most people visit Mackinaw later since the island is a cool place to explore."
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sorted(d.keys()), sorted(['B', 'e', 'a', 'c', 'h', 's', ' ', 'r', 'o', 'l', 'p', 't', 'v', 'i', 'n', 'g', 'w', 'M', 'k', 'd', '.', 'x']), "Testing the keys were created correctly")
+ self.assertEqual(sorted(d.values()), sorted([2, 17, 12, 8, 4, 10, 27, 7, 10, 8, 6, 8, 3, 13, 7, 2, 3, 3, 2, 2, 2, 1]), "Testing the values were created correctly")
+ def testTwo(self):
+ self.assertEqual(key_with_min_value, "x", "Testing that key_with_min_value has been correctly assigned")
+
+myTests().main()
+
+
+
+
+
+
5. Create a dictionary called lett_d that keeps track of all of the characters in the string product and notes how many times each character was seen. Then, find the key with the highest value in this dictionary and assign that key to key_with_max_value.
+
+
+
+product = "iphone and android phones"
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sorted(lett_d.items()), sorted([('h', 2), ('a', 2), (' ', 3), ('n', 4), ('d', 3), ('o', 3), ('i', 2), ('p', 2), ('e', 2), ('r', 1), ('s', 1)]), "Testing that lett_d has been created correctly.")
+ def testTwo(self):
+ self.assertEqual(key_with_max_value, "n", "Testing that key_with_max_value has been correctly assigned")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Dictionaries/Aliasingandcopying.ptx b/pretext/Dictionaries/Aliasingandcopying.ptx
new file mode 100644
index 00000000..ac80c700
--- /dev/null
+++ b/pretext/Dictionaries/Aliasingandcopying.ptx
@@ -0,0 +1,77 @@
+
+
+ Aliasing and copying
+
Because dictionaries are mutable, you need to be aware of aliasing (as we saw with lists). Whenever
+ two variables refer to the same dictionary object, changes to one affect the other. For example, opposites is a
+ dictionary that contains pairs of opposites.
As you can see from the is operator, alias and opposites refer to the same object.
+
If you want to modify a dictionary and keep a copy of the original, use the dictionary
+ copy method. Since acopy is a copy of the dictionary, changes to it will not effect the original.
+
+
+acopy = opposites.copy()
+acopy['right'] = 'left' # does not change opposites
+
+
+
mydict and yourdict are both names for the same dictionary.
+
+
+
+
+
None
+
+
+
The dictionary is mutable so changes can be made to the keys and values.
+
+
+
+
+
999
+
+
+
Yes, since yourdict is an alias for mydict, the value for the key elephant has been changed.
+
+
+
+
+
Error, there are two different keys named elephant.
+
+
+
There is only one dictionary with only one key named elephant. The dictionary has two different names, mydict and yourdict.
+
+
+
+
+
diff --git a/pretext/Dictionaries/ChapterAssessment.ptx b/pretext/Dictionaries/ChapterAssessment.ptx
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ b/pretext/Dictionaries/ChapterAssessment.ptx
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git "a/pretext/Dictionaries/ChapterAssessment.ptx\n" "b/pretext/Dictionaries/ChapterAssessment.ptx\n"
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ "b/pretext/Dictionaries/ChapterAssessment.ptx\n"
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git a/pretext/Dictionaries/Dictionarymethods.ptx b/pretext/Dictionaries/Dictionarymethods.ptx
new file mode 100644
index 00000000..fa245c98
--- /dev/null
+++ b/pretext/Dictionaries/Dictionarymethods.ptx
@@ -0,0 +1,379 @@
+
+
+ Dictionary methods
+
Dictionaries have a number of useful built-in methods.
+ The following table provides a summary and more details can be found in the
+ Python Documentation.
+
+
+
+
+ Method
+
+
+ Parameters
+
+
+ Description
+
+
+
+
+ keys
+
+
+ none
+
+
+ Returns a view of the keys in the dictionary
+
+
+
+
+ values
+
+
+ none
+
+
+ Returns a view of the values in the dictionary
+
+
+
+
+ items
+
+
+ none
+
+
+ Returns a view of the key-value pairs in the dictionary
+
+
+
+
+ get
+
+
+ key
+
+
+ Returns the value associated with key; None otherwise
+
+
+
+
+ get
+
+
+ key,alt
+
+
+ Returns the value associated with key; alt otherwise
+
+
+
+
+
As we saw earlier with strings and lists, dictionary methods use dot notation, which specifies the name of the method
+ to the right of the dot and the name of the object on which to apply the method immediately to the left of the dot.
+ For example, if x is a variable
+ whose value is a dictionary, x.keys is the method object, and x.keys() invokes the method, returning a view of
+ the value.
+
+ Iterating over Dictionaries
+
There are three ways to iterate over the contents of a dictionary. Let's take a moment to examine them.
+
The first technique involves iterating over the keys of the dictionary using the keys method.
+ The keys method returns a collection of the keys in the dictionary.
+
+
+inventory = {'apples': 430, 'bananas': 312, 'pears': 217, 'oranges': 525}
+
+for akey in inventory.keys(): # the order in which we get the keys is not defined
+ print("Got key", akey, "which maps to value", inventory[akey])
+
+ks = list(inventory.keys()) # Make a list of all of the keys
+print(ks)
+print(ks[0]) # Display the first key
+
+
+
Note the first line of the for loop:
+
for akey in inventory.keys():
+
Each time through the loop, the loop variable akey is assigned a different key in the dictionary. In the loop body,
+ the value associated with the key is accessed by indexing the dictionary with akey using the expression
+ inventory[akey]. Note that the order in which the keys are assigned in the loop is not predictable. If you want to
+ visit the keys in alphabetic order, you must use the sorted function to produce a sorted collection of keys, like this:
+
for akey in sorted(inventory.keys()):
+
It's so common to iterate over the keys in a dictionary that you can
+ omit the keys method call in the for loop — iterating over
+ a dictionary implicitly iterates over its keys.
The items method returns a collection of tuples containing each key and its associated value.
+ Take a look at this example that iterates over the dictionary using the items method:
Take a close look at the first line of the for loop:
+
for k, v in inventory.items():
+
Each time through the loop, k receives a key from the dictionary, and v receives its associated
+ value. That avoids the need to index the dictionary inside the loop body to access the value associated
+ with the key.
+
+
You may have noticed in the examples above that, to print the result of the keys(), values(), and
+ items() methods, we used lines like this:
+
print(list(inventory.keys())
+
instead of this:
+
print(inventory.keys())
+
Technically, keys(), values(), and items() don't return actual lists. Like the range function described
+ previously, they return objects that produce the items one at a time, rather than producing and
+ storing all of them in advance as a list. If you need to perform an operation on the result of one of these methods such as
+ extracting the first item, you must convert the result to a list using the list conversion function. For example, if you want to get the first key,
+ this won't work: inventory.keys()[0]. You need to make the collection of keys into a real list before using
+ [0] to index into it: list(inventory.keys())[0].
+
+
+
+ Safely Retrieving Values
+
Looking up a value in a dictionary is a potentially dangerous operation. When using the [] operator to access
+ a key, if the key is not present, a runtime error occurs. There are two ways to deal with this problem.
+
The first approach is to use the in and not in operators, which can test if a key is in the dictionary:
+
+
+inventory = {'apples': 430, 'bananas': 312, 'oranges': 525, 'pears': 217}
+print('apples' in inventory)
+print('cherries' in inventory)
+
+if 'bananas' in inventory:
+ print(inventory['bananas'])
+else:
+ print("We have no bananas")
+
+
+
The second approach is to use the get method. get retrieves the value associated with a key, similar to the [] operator. The important
+ difference is that get will not cause a runtime error if the key is not present. It will instead return the value None.
+ There exists a variation of get that allows a second parameter that serves as an alternative return value in the
+ case where the key is not present. This can be seen in the final example below. In this case, since cherries is not
+ a key, get returns 0 (instead of None).
Yes, the in operator returns True if a key is in the dictionary, False otherwise.
+
+
+
+
+
+
+
What is printed by the following statements?
+
+
+total = 0
+mydict = {"cat":12, "dog":6, "elephant":23, "bear":20}
+for akey in mydict:
+ if len(akey) > 3:
+ total = total + mydict[akey]
+print(total)
+
+
+
+
+
+
+
18
+
+
+
Add the values that have keys longer than 3 characters, not those with exactly 3 characters.
+
+
+
+
+
43
+
+
+
Yes, the for statement iterates over the keys. It adds the values of the keys that have length greater than 3.
+
+
+
+
+
0
+
+
+
This is the accumulator pattern. Total starts at 0 but then changes as the iteration proceeds.
+
+
+
+
+
61
+
+
+
Not all the values are added together. The if statement only chooses some of them.
+
+
+
+
+
+
+
5. We have a dictionary of the specific events that Italy has won medals in and the number of medals they have won for each event. Assign to the variable events a list of the keys from the dictionary medal_events. Use a dictionary method and cast to a list; do not hard code or accumulate a list via iteration.
+
+
+
+medal_events = {'Shooting': 7, 'Fencing': 4, 'Judo': 2, 'Swimming': 3, 'Diving': 2}
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(type(events), list, "Testing that events is a list")
+ self.assertEqual(sorted(events), sorted(medal_events), "Testing that events was created correctly")
+ self.assertNotIn('[', self.getEditorText(), "Hard coding or accumulation detected; use a dictionary method instead")
+
+myTests().main()
+
+
+
+ Add the following line:
+ events = list(medal_events.keys())
+
+
The del statement removes a key-value pair from a dictionary. For example, the following dictionary contains the
+ names of various fruits and the number of each fruit in stock. If someone buys all of the pears, we can remove the
+ entry from the dictionary.
Dictionaries are mutable, as the delete operation above indicates. As we've seen before with lists, this means that the
+ dictionary can be modified by referencing an association on the left hand side of the assignment statement. In the
+ previous example, instead of deleting the entry for pears, we could have set the inventory to 0.
Setting the value associated with pears to 0 has a different effect than removing the key-value pair entirely
+ with del. Try printout out the two dictionaries in the examples above.
+
+
Similarily, a new shipment of 200 bananas arriving could be handled like this. Notice that there are now 512 bananas—
+ the dictionary has been modified. Note also that the len function also works on dictionaries. It returns the number
+ of key-value pairs.
Notice that there are now 512 bananas—the dictionary has been modified. Note also that the len function also
+ works on dictionaries. It returns the number of key-value pairs.
The key mouse will be associated with the sum of the two values.
+
+
+
+
+
18
+
+
+
Yes, add the value for cat and the value for dog (12 + 6) and create a new entry for mouse.
+
+
+
+
+
Error, there is no entry with mouse as the key.
+
+
+
Since the new key is introduced on the left hand side of the assignment statement, a new key-value pair is added to the dictionary.
+
+
+
+
+
+
+
2. Update the value for Phelps in the dictionary swimmers to include his medals from the Rio Olympics by adding 5 to the current value (Phelps will now have 28 total medals). Do not rewrite the dictionary.
+
+
+
+swimmers = {'Manuel':4, 'Lochte':12, 'Adrian':7, 'Ledecky':5, 'Dirado':4, 'Phelps':23}
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testThree(self):
+ self.assertEqual(sorted(swimmers.items()), sorted([('Adrian', 7), ('Dirado', 4), ('Ledecky', 5), ('Lochte', 12), ('Phelps', 28), ('Manuel',4)]), "Testing that swimmers is assigned to correct values.")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Dictionaries/Exercises.ptx b/pretext/Dictionaries/Exercises.ptx
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ b/pretext/Dictionaries/Exercises.ptx
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git "a/pretext/Dictionaries/Exercises.ptx\n" "b/pretext/Dictionaries/Exercises.ptx\n"
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ "b/pretext/Dictionaries/Exercises.ptx\n"
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git a/pretext/Dictionaries/Glossary.ptx b/pretext/Dictionaries/Glossary.ptx
new file mode 100644
index 00000000..82362417
--- /dev/null
+++ b/pretext/Dictionaries/Glossary.ptx
@@ -0,0 +1,33 @@
+
+
+ Glossary
+
+
+ dictionary
+
A collection of key-value pairs that maps from keys to values. The keys
+ can be any immutable type, and the values can be any type.
+
+
+ key
+
A data item that is mapped to a value in a dictionary. Keys are used
+ to look up values in a dictionary.
+
+
+ value
+
The value that is associated with each key in a dictionary.
+
+
+ key-value pair
+
One of the pairs of items in a dictionary. Values are looked up in a
+ dictionary by key.
+
+
+ mapping type
+
A mapping type is a data type comprised of a collection of keys and
+ associated values. Python's only built-in mapping type is the
+ dictionary. Dictionaries implement the
+ associative array
+ abstract data type.
+
+
+
diff --git a/pretext/Dictionaries/WPChoosingDictionaries.ptx b/pretext/Dictionaries/WPChoosingDictionaries.ptx
new file mode 100644
index 00000000..a7ea48d7
--- /dev/null
+++ b/pretext/Dictionaries/WPChoosingDictionaries.ptx
@@ -0,0 +1,32 @@
+
+
+ 👩💻 When to use a dictionary
+
Now that you have experience using lists and dictionaries, you will have to decide which one is best to use in each situation. The following guidelines will help you recognize when a dictionary will be beneficial:
+
+
+
+
When a piece of data consists of a set of properties of a single item, a dictionary is often better. You could try to keep track mentally that the zip code property is at index 2 in a list, but your code will be easier to read and you will make fewer mistakes if you can look up mydiction[‘zipcode'] than if you look up mylst[2].
+
+
+
When you have a collection of data pairs, and you will often have to look up one of the pairs based on its first value, it is better to use a dictionary than a list of (key, value) tuples. With a dictionary, you can find the value for any (key, value) tuple by looking up the key. With a list of tuples you would need to iterate through the list, examining each pair to see if it had the key that you want.
+
+
+
On the other hand, if you will have a collection of data pairs where multiple pairs share the same first data element, then you can't use a dictionary, because a dictionary requires all the keys to be distinct from each other.
You have previously seen the accumulator pattern; it goes through the items in a sequence,
+ updating an accumulator variable each time. Rather than accumulating a single result, it's
+ possible to accumulate many results. Suppose, for example, we wanted to find out which
+ letters are used most frequently in English.
+
Suppose we had a reasonably long text that we thought was representative of general English
+ usage. For our purposes in the this chapter, we will use the text of the Sherlock Holmes story,
+ A Study in Scarlet, by Sir Arthur Conan Doyle. The text actually includes a few
+ lines about the source of the transcription (Project Gutenberg), but those will not
+ materially affect our analyses so we will just leave them in. You can access this text
+ within this chapter with the code open('scarlet.txt', 'r').
+ <div class="alert alert-info">
+<p>As with other files that we access in this textbook environment, this one is actually pre-loaded in your browser, not retrieved from your computer's file system. That's why this chapter may be a little slower to load than others. You can view the text of "A Study in Scarlet" at the bottom of the page.</p>
+</div>
+
If we want to find out how often the letter ‘t' occurs, we can accumulate the result
+ in a count variable.
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+t_count = 0 #initialize the accumulator variable
+for c in txt:
+ if c == 't':
+ t_count = t_count + 1 #increment the counter
+print("t: " + str(t_count) + " occurrences")
+
+
+
We can accumulate counts for more than one character as we traverse the text.
+ Suppose, for example, we wanted to compare the counts of ‘t' and ‘s' in the text.
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+t_count = 0 #initialize the accumulator variable
+s_count = 0 # initialize the s counter accumulator as well
+for c in txt:
+ if c == 't':
+ t_count = t_count + 1 #increment the t counter
+ elif c == 's':
+ s_count = s_count + 1
+print("t: " + str(t_count) + " occurrences")
+print("s: " + str(s_count) + " occurrences")
+
+
+
OK, but you can see this is going to get tedious if we try to accumulate counts
+ for all the letters. We will have to initialize a lot of accumulators, and there will
+ be a very long if..elif..elif statement. Using a dictionary, we can do a lot better.
+
One dictionary can hold all of the accumulator variables. Each key in the dictionary
+ will be one letter, and the corresponding value will be the count so far of how
+ many times that letter has occurred.
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+letter_counts = {} # start with an empty dictionary
+letter_counts['t'] = 0 # initialize the t counter
+letter_counts['s'] = 0 # initialize the s counter
+for c in txt:
+ if c == 't':
+ letter_counts['t'] = letter_counts['t'] + 1 # increment the t counter
+ elif c == 's':
+ letter_counts['s'] = letter_counts['s'] + 1 # increment the s counter
+
+print("t: " + str(letter_counts['t']) + " occurrences")
+print("s: " + str(letter_counts['s']) + " occurrences")
+
+
+
In the example above, we've created a dictionary, letter_counts, to hold the letters ‘t'
+ and ‘c', with their associated counts. This hasn't really improved things yet, but look closely at lines 8-11 in the code above.
+ Whichever character we're seeing, t or s, we're incrementing the counter for that
+ character. So lines 9 and 11 could really be the same, if we make one small change:
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+letter_counts = {} # start with an empty dictionary
+letter_counts['t'] = 0 # intiialize the t counter
+letter_counts['s'] = 0 # initialize the s counter
+for c in txt:
+ if c == 't':
+ letter_counts[c] = letter_counts[c] + 1 # increment the t counter
+ elif c == 's':
+ letter_counts[c] = letter_counts[c] + 1 # increment the s counter
+
+print("t: " + str(letter_counts['t']) + " occurrences")
+print("s: " + str(letter_counts['s']) + " occurrences")
+
+
+
Lines 9 and 11 above may seem a little confusing at first. Previously, our assignment
+ statements referred directly to keys, with letter_counts['s'] and letter_counts['t']. Here we
+ are just using a variable c whose value is ‘s' or ‘t', or some other character.
+
If that made perfect sense to you, skip the next two paragraphs. Otherwise, read on.
+ Let's break down that line in a little more detail.
+
First, note that, as with all
+ assignment statements, the right side is evaluated first. In this case letter_counts[c] has to be
+ evaluated. As with all expressions, we first have to substitute values for variable names.
+ letter_counts is a variable bound to a dictionary. c is a variable bound to one letter from the
+ string that txt is bound to (that's what the for statement says to do:
+ execute lines 8-11 once for each character in txt, with the variable c bound to the current character
+ on each iteration.) So, let's suppose that the current character is the letter s (we are on line 11).
+ Then letter_counts[c] looks up the value associated with the key ‘s' in the dictionary letter_counts. If all is working correctly, that value should be the number of times ‘s' has previously occurred. For the sake of argument, suppose it's 25. Then
+ the right side evaluates to 25 + 1, 26. Watch this play out below.
+
+
Now we have assigned the value 26 to letter_counts[c]. That is, in dictionary x, we set the value associated with the
+ key ‘s' (the current value of the variable c) to be 26. In other words, we have incremented the value associated with
+ the key ‘s' from 25 to 26.
+
We can do better still. One other nice thing about using a dictionary is that we don't have to prespecify
+ what all the letters will be. In this case, we know in advance what the alphabet for
+ English is, but later in the chapter we will count the occurrences of words, and
+ we do not know in advance all the of the words that may be used. Rather than pre-specifying
+ which letters to keep accumulator counts for, we can start with an empty dictionary and
+ add a counter to the dictionary each time we encounter a new thing that we want to
+ start keeping count of.
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+# now txt is one long string containing all the characters
+letter_counts = {} # start with an empty dictionary
+for c in txt:
+ if c not in letter_counts:
+ # we have not seen this character before, so initialize a counter for it
+ letter_counts[c] = 0
+
+ #whether we've seen it before or not, increment its counter
+ letter_counts[c] = letter_counts[c] + 1
+
+print("t: " + str(letter_counts['t']) + " occurrences")
+print("s: " + str(letter_counts['s']) + " occurrences")
+
+
+
Notice that in the for loop, we no longer need to explicitly ask whether the current
+ letter is an ‘s' or ‘t'. The increment step on line 11 works for the counter
+ associated with whatever the current character is. Our code is now accumulating
+ counts for all letters, not just ‘s' and ‘t'.
+
As a final refinement, consider replacing lines 5-11 above with this for loop:
+
for c in txt:
+ letter_counts[c] = letter_counts.get(c, 0) + 1
+
This loop uses the get method to retrieve the count for the letter in the
+ variable c. If no such key is present, get returns 0, and letter_counts[c] is
+ set to 1 (0 + 1 = 1). If the key is present, get retrieves its value, which is
+ then incremented.
+
+ Check your understanding
+
+
+
+
Consider example ac10_5_5 above. After the program runs, which of the following will print out True if there are more
+ occurrences of e than t in the text of A Study in Scarlet, and False if t occurred more frequently?
+
+
+
+
+
print(txt['e'] > txt['t'])
+
+
+
txt is the variable that has the original text, not the dictionary of counts.
+
+
+
+
+
print(letter_counts['e'] > letter_counts['t'])
+
+
+
letter_counts is the dictionary of counts; you want to compare the values associated with 'e' and 't'.
+
+
+
+
+
print(letter_counts[e] > letter_counts[t])
+
+
+
letter_counts is the dictionary of counts, but you don't want to evaluate e and t as variables in order to determine which keys to look up in the dictionary.
+
+
+
+
+
print(letter_counts[c] > txt[c])
+
+
+
It seems like maybe you're guessing. Please review the material above and then try again.
+
+
+
+
+
print(e[letter_counts] > t[letter_counts])
+
+
+
It seems like you've reversed things. The variable that refers to the dictionary goes outside the square brackets; the key you're looking up goes inside.
+
+
+
+
+
+
+
The print statements at the end of program ac10_5_5 above pick out the specific keys ‘t' and ‘s'. Generalize that
+ to print out the occurrence counts for all of the characters. To pass the unit tests, your output must
+ use the same format as the original program above.
+
+
+
+f = open('scarlet.txt', 'r')
+txt = f.read()
+letter_counts = {}
+for c in txt:
+ if c not in letter_counts:
+ letter_counts[c] = 0
+
+ letter_counts[c] = letter_counts[c] + 1
+
+# Write a loop that prints the letters and their counts
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertIn("w:4745", self.getOutput().replace(' ',''), "Testing that correct number of w's is output.")
+ self.assertIn("h:12892", self.getOutput().replace(' ',''), "Testing that correct number of h's is output.")
+ self.assertIn("G:235", self.getOutput().replace(' ',''), "Testing that correct number of G's is output.")
+ self.assertEqual(len(self.getOutput().split('\n')), 90, "Testing that correct number of lines are output.")
+
+myTests().main()
+
+
+
+
Use a for loop to iterate through the keys in letter_counts.
+
+
+
Here's a for loop that will do the job:
+
for c in letter_counts.keys():
+ print(c + ": " + str(letter_counts[c]) + " occurrences")
+
+
+
In the solution to the problem above, note that only those letters that actually occur in the text are shown. Some
+ punctuation marks that are possible in English, but were never used in the
+ text, are omitted completely. The blank line partway through the output may surprise you.
+ That's actually saying that the newline character, \n, appears 5155 times in
+ the text. In other words, there are 5155 lines of text in the file. Let's
+ test that hypothesis. Run the following example and check its output:
+
+
+f = open('scarlet.txt', 'r')
+txt_lines = f.readlines()
+# now txt_lines is a list, where each item is one
+# line of text from the story
+print(len(txt_lines))
+
+
+
Now, here are some additional problems to try.
+
+
+
Split the string sentence into a list of words, then create a dictionary named word_counts that contains each word and the number of times it occurs.
+
+
+
+sentence = "The dog chased the rabbit into the forest but the rabbit was too quick."
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sorted(word_counts.items()), sorted([('The', 1), ('dog', 1), ('chased', 1), ('the', 3), ('rabbit', 2), ('into', 1), ('forest', 1), ('but', 1), ('was', 1), ('too', 1), ('quick.', 1)]), "Testing that word_counts was created correctly.")
+
+myTests().main()
+
+
+
+
The split() method will help split sentence into a list of words.
+
+
+ Here's one possible solution that uses the get method.
+ word_counts = {}
+
+for word in sentence.split():
+ word_counts[word] = word_counts.get(word, 0) + 1
+
+
Here's one possible solution that uses the get method.
Create a dictionary called char_d. The keys of the dictionary should be each character in stri, and the value for each key should be how many times the character occurs in the string.
+
+
+
+stri = "what can I do"
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(sorted(char_d.items()), sorted([('w', 1), ('h', 1), ('a', 2), ('t', 1), (' ', 3), ('c', 1), ('n', 1), ('I', 1), ('d', 1), ('o', 1)]), "Testing that char_d has been created correctly.")
+
+myTests().main()
+
+
+
+
This problem is very similar to the letter-counting problem discussed in this section. Review the solution above
+ and see if you can apply it to this problem.
+
+
+ Here's a solution that uses the get method:
+ char_d = {}
+for c in stri:
+ char_d[c] = char_d.get(c, 0) + 1
+
+
Here's a solution that uses the get method:
+
+char_d = {}
+for c in stri:
+ char_d[c] = char_d.get(c, 0) + 1
+
+
+
+
+
+
diff --git a/pretext/Dictionaries/intro-Dictionaries.ptx b/pretext/Dictionaries/intro-Dictionaries.ptx
new file mode 100644
index 00000000..b156691d
--- /dev/null
+++ b/pretext/Dictionaries/intro-Dictionaries.ptx
@@ -0,0 +1,192 @@
+
+
+ Getting Started with Dictionaries
+
Here is a video to help introduce you to the important concepts in creating and using Python dictionaries.
+
+
Let us look at an example of using a dictionary for a simple problem. We will create a dictionary to translate English words into Spanish.
+ For this dictionary, the keys are strings and the values will also be strings.
+
One way to create a dictionary is to start with the empty dictionary and add key-value pairs. The empty dictionary
+ is denoted {}.
The first assignment creates an empty dictionary named eng2sp. The other assignments add new key-value pairs to
+ the dictionary. The left hand side gives the dictionary and the key being associated. The right hand side gives the
+ value being associated with that key. We can print the current value of the dictionary in the usual way. The key-value
+ pairs of the dictionary are separated by commas. Each pair contains a key and a value separated by a colon.
+
The order of the pairs may not be what you expected. Python uses complex algorithms, designed for very fast access, to
+ determine where the key-value pairs are stored in a dictionary. For our purposes we can think of this ordering as
+ unpredictable * .
+
Another way to create a dictionary is to provide a bunch of key-value pairs using the same syntax as the previous
+ output.
It doesn't matter what order we write the pairs. The values in a dictionary are accessed with keys, not with indices,
+ so there is no need to care about ordering.
+
Here is how we use a key to look up the corresponding value.
Error, you cannot use the index operator with a dictionary.
+
+
+
The [ ] operator, when used with a dictionary, will look up a value based on its key.
+
+
+
+
+
+
+
3. Create a dictionary that keeps track of the USA's Olympic medal count. Each key of the dictionary should be the type of medal (gold, silver, or bronze) and each key's value should be the number of that type of medal the USA's won. Currently, the USA has 33 gold medals, 17 silver, and 12 bronze. Create a dictionary saved in the variable medals that reflects this information.
4. You are keeping track of olympic medals for Italy in the 2016 Rio Summer Olympics! At the moment, Italy has 7 gold medals, 8 silver medals, and 6 bronze medals. Create a dictionary called olympics where the keys are the types of medals, and the values are the number of that type of medals that Italy has won so far.
Every four years, the summer Olympics are held in a different country. Add a key-value pair to the dictionary places that reflects that the 2016 Olympics were held in Brazil. Do not rewrite the entire dictionary to do this!
Instructors note: Python version 3.7 and later provide ordering guarantees. However, it is best practice to write code that does not rely on any particular key order so this book will treat key-value pairs as unordered.
The compound data types we have studied in detail so far — strings and lists — are sequential collections. This means that the items in the collection are ordered from left to right and they use integers as indices to access the values they contain. This also means that looking for a particular value requires scanning the many items in the list until you find the desired value.
+
Data can sometimes be organized more usefully by associating a key with the value we are looking for. For example, if you are asked for the page number for the start of chapter 5 in a large textbook, you might flip around the book looking for the chapter 5 heading. If the chapter number appears in the header or footer of each page, you might be able to find the page number fairly quickly but it's generally easier and faster to go to the index page and see that chapter 5 starts on page 78.
+
This sort of direct look up of a value in Python is done with an object called a Dictionary. Dictionaries are a different kind of collection. They are Python's built-in mapping type. A map is an unordered, associative collection. The association, or mapping, is from a key, which can be of any immutable type (e.g., the chapter name and number in the analogy above), to a value (the starting page number), which can be any Python data object. You'll learn how to use these collections in the following chapter.
+
+ Learning Goals
+
+
+
+
To introduce the idea of Key, Value pairs
+
+
+
To introduce the idea of an unordered sequence
+
+
+
To understand the use of parallel construction in lists
+
+
+
To understand the performance benefit and simplicity of a dictionary over parallel lists
+
+
+
To understand that dictionary iteration iterates over keys
+
+
+
+
+
+ Objectives
+
To correctly use the following:
+
+
+
+
The index operator to add a key,value pair to a dictionary
The code below takes the list of country, country, and searches to see if it is in the dictionary gold which shows some countries who won gold during the Olympics. However, this code currently does not work. Correctly add try/except clause in the code so that it will correctly populate the list, country_gold, with either the number of golds won or the string Did not get gold.
+
+
+
+gold = {"US":46, "Fiji":1, "Great Britain":27, "Cuba":5, "Thailand":2, "China":26, "France":10}
+country = ["Fiji", "Chile", "Mexico", "France", "Norway", "US"]
+country_gold = []
+
+for x in country:
+ country_gold.append(gold[x])
+ country_gold.append("Did not get gold")
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOneA(self):
+ self.assertEqual(country_gold, [1, 'Did not get gold', 'Did not get gold', 10, 'Did not get gold', 46], "Testing that country_gold is assigned to correct values")
+
+myTests().main()
+
+
+
+
+
+
Provided is a buggy for loop that tries to accumulate some values out of some dictionaries. Insert a try/except so that the code passes.
The list, numb, contains integers. Write code that populates the list remainder with the remainder of 36 divided by each number in numb. For example, the first element should be 0, because 36/6 has no remainder. If there is an error, have the string Error appear in the remainder.
Provided is buggy code, insert a try/except so that the code passes.
+
+
+
+lst = [2,4,10,42,12,0,4,7,21,4,83,8,5,6,8,234,5,6,523,42,34,0,234,1,435,465,56,7,3,43,23]
+
+lst_three = []
+
+for num in lst:
+ if 3 % num == 0:
+ lst_three.append(num)
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(lst_three, [1,3], "Testing that lst_three has the correct values.")
+
+myTests().main()
+
+
+
+
+
+
Write code so that the buggy code provided works using a try/except. When the codes does not work in the try, have it append to the list attempt the string Error.
The following code tries to append the third element of each list in conts to the new list third_countries. Currently, the code does not work. Add a try/except clause so the code runs without errors, and the string ‘Continent does not have 3 countries' is appended to third_countries instead of producing an error.
+
+
+
+conts = [['Spain', 'France', 'Greece', 'Portugal', 'Romania', 'Germany'], ['USA', 'Mexico', 'Canada'], ['Japan', 'China', 'Korea', 'Vietnam', 'Cambodia'], ['Argentina', 'Chile', 'Brazil', 'Ecuador', 'Uruguay', 'Venezuela'], ['Australia'], ['Zimbabwe', 'Morocco', 'Kenya', 'Ethiopia', 'South Africa'], ['Antarctica']]
+
+third_countries = []
+
+for c in conts:
+ third_countries.append(c[2])
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(third_countries, ['Greece', 'Canada', 'Korea', 'Brazil', 'Continent does not have 3 countries', 'Kenya', 'Continent does not have 3 countries'], "Testing that third_countries is created correctly.")
+
+myTests().main()
+
+
+
+
+
+
The buggy code below prints out the value of the sport in the list sport. Use try/except so that the code will run properly. If the sport is not in the dictionary, ppl_play, add it in with the value of 1.
Provided is a buggy for loop that tries to accumulate some values out of some dictionaries. Insert a try/except so that the code passes. If the key is not there, initialize it in the dictionary and set the value to zero.
+
+
+
+di = [{"Puppies": 17, 'Kittens': 9, "Birds": 23, 'Fish': 90, "Hamsters": 49}, {"Puppies": 23, "Birds": 29, "Fish": 20, "Mice": 20, "Snakes": 7}, {"Fish": 203, "Hamsters": 93, "Snakes": 25, "Kittens": 89}, {"Birds": 20, "Puppies": 90, "Snakes": 21, "Fish": 10, "Kittens": 67}]
+total = 0
+for diction in di:
+ total = total + diction['Puppies']
+
+print("Total number of puppies:", total)
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ accum = 0
+ for diction in di:
+ if 'Puppies' in diction:
+ accum += 1
+ self.assertEqual(accum, 4, "Testing that every dictionary in di has the key 'Puppies'.")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Exceptions/Exercises.ptx b/pretext/Exceptions/Exercises.ptx
new file mode 100644
index 00000000..7506b4b2
--- /dev/null
+++ b/pretext/Exceptions/Exercises.ptx
@@ -0,0 +1,2 @@
+
+
diff --git a/pretext/Exceptions/intro-exceptions.ptx b/pretext/Exceptions/intro-exceptions.ptx
new file mode 100644
index 00000000..9fd61bd9
--- /dev/null
+++ b/pretext/Exceptions/intro-exceptions.ptx
@@ -0,0 +1,9 @@
+
+
+ What is an exception?
+
An exception is a signal that a condition has occurred that can't be easily
+ handled using the normal flow-of-control of a Python program. Exceptions
+ are often defined as being errors but this is not always the case. All
+ errors in Python are dealt with using exceptions, but not all
+ exceptions are errors.
+
diff --git a/pretext/Exceptions/standard-exceptions.ptx b/pretext/Exceptions/standard-exceptions.ptx
new file mode 100644
index 00000000..65ef513a
--- /dev/null
+++ b/pretext/Exceptions/standard-exceptions.ptx
@@ -0,0 +1,408 @@
+
+
+ Standard Exceptions
+
Most of the standard exceptions built into Python are listed below.
+ They are organized into related groups based on the types of issues they deal with.
+
+
+
+
+ Language Exceptions
+
+
+ Description
+
+
+
+
+ StandardError
+
+
+ Base class for all built-in exceptions except
+ StopIteration and SystemExit.
+
+
+
+
+ ImportError
+
+
+ Raised when an import statement fails.
+
+
+
+
+ SyntaxError
+
+
+ Raised when there is an error in Python syntax.
+
+
+
+
+ IndentationError
+
+
+ Raised when indentation is not specified properly.
+
+
+
+
+ NameError
+
+
+ Raised when an identifier is not found in the local
+ or global namespace.
+
+
+
+
+ UnboundLocalError
+
+
+ Raised when trying to access a local variable in a
+ function or method but no value has been assigned to it.
+
+
+
+
+ TypeError
+
+
+ Raised when an operation or function is attempted that
+ is invalid for the specified data type.
+
+
+
+
+ LookupError
+
+
+ Base class for all lookup errors.
+
+
+
+
+ IndexError
+
+
+ Raised when an index is not found in a sequence.
+
+
+
+
+ KeyError
+
+
+ Raised when the specified key is not found in the dictionary.
+
+
+
+
+ ValueError
+
+
+ Raised when the built-in function for a data type has
+ the valid type of arguments, but the arguments have
+ invalid values specified.
+
+
+
+
+ RuntimeError
+
+
+ Raised when a generated error does not fall into any category.
+
+
+
+
+ MemoryError
+
+
+ Raised when a operation runs out of memory.
+
+
+
+
+ RecursionError
+
+
+ Raised when the maximum recursion depth has been exceeded.
+
+
+
+
+ SystemError
+
+
+ Raised when the interpreter finds an internal problem,
+ but when this error is encountered the Python interpreter
+ does not exit.
+
+
+
+
+
+
+
+
+ Math Exceptions
+
+
+ Description
+
+
+
+
+ ArithmeticError
+
+
+ Base class for all errors that occur for numeric calculation.
+ You know a math error occurred, but you don't know the
+ specific error.
+
+
+
+
+ OverflowError
+
+
+ Raised when a calculation exceeds maximum limit for a
+ numeric type.
+
+
+
+
+ FloatingPointError
+
+
+ Raised when a floating point calculation fails.
+
+
+
+
+ ZeroDivisonError
+
+
+ Raised when division or modulo by zero takes place for
+ all numeric types.
+
+
+
+
+
+
+
+
+ I/O Exceptions
+
+
+ Description
+
+
+
+
+ FileNotFoundError
+
+
+ Raised when a file or directory is requested but doesn't exist.
+
+
+
+
+ IOError
+
+
+ Raised when an input/ output operation fails, such as
+ the print statement or the open() function when trying
+ to open a file that does not exist. Also raised for
+ operating system-related errors.
+
+
+
+
+ PermissionError
+
+
+ Raised when trying to run an operation without the
+ adequate access rights.
+
+
+
+
+ EOFError
+
+
+ Raised when there is no input from either the raw_input()
+ or input() function and the end of file is reached.
+
+
+
+
+ KeyboardInterrupt
+
+
+ Raised when the user interrupts program execution,
+ usually by pressing Ctrl+c.
+
+
+
+
+
+
+
+
+ Other Exceptions
+
+
+ Description
+
+
+
+
+ Exception
+
+
+ Base class for all exceptions. This catches most
+ exception messages.
+
+
+
+
+ StopIteration
+
+
+ Raised when the next() method of an iterator
+ does not point to any object.
+
+
+
+
+ AssertionError
+
+
+ Raised in case of failure of the Assert statement.
+
+
+
+
+ SystemExit
+
+
+ Raised when Python interpreter is quit by using the
+ sys.exit() function. If not handled in the code, it
+ causes the interpreter to exit.
+
+
+
+
+ OSError
+
+
+ Raises for operating system related errors.
+
+
+
+
+ EnvironmentError
+
+
+ Base class for all exceptions that occur outside the
+ Python environment.
+
+
+
+
+ AttributeError
+
+
+ Raised in case of failure of an attribute reference
+ or assignment.
+
+
+
+
+ NotImplementedError
+
+
+ Raised when an abstract method that needs to be
+ implemented in an inherited class is not actually implemented.
+
+
+
+
+
All exceptions are objects. The classes that define the objects are organized
+ in a hierarchy, which is shown below. This is important because the parent
+ class of a set of related exceptions will catch all exception messages for
+ itself and its child exceptions. For example, an ArithmeticError
+ exception will catch itself and all FloatingPointError, OverflowError,
+ and ZeroDivisionError exceptions.
The reason to use try/except is when you have a code block to execute that will sometimes run correctly and sometimes not, depending on conditions you can't foresee at the time you're writing the code.
+
For example, when you are running code that fetches data from a website, you may run the code when you don't have a network connection or when the external website is temporarily not responding. If your program can still do something useful in those situations, you would like to handle the exception and have the rest of your code execute.
+
As another example, suppose you have fetched some nested data from a website into a dictionary d. When you try to extract specific elements, some may be missing: d may not include a particular key, for example. If you anticipate a particular key potentially not being present, you could write an if..else check to take care of it.
+
+
+if somekey in d:
+ # it's there; extract the data
+ extract_data(d)
+else:
+ skip_this_one(d)
+
+
+
However, if you're extracting lots of different data, it can get tedious to check for all of them. You can wrap all the data extraction in a try/except.
It's considered poor practice to catch all exceptions this way. Instead, python provides a mechanism to specify just certain kinds of exceptions that you'll catch (for example, just catching exceptions of type KeyError, which happens when a key is missing from a dictionary.
We won't go into more details of exception handling in this introductory course. Check out the official python tutorial section on error handling if you're interested.