diff --git a/.gitignore b/.gitignore
index 3e6b857a..ed2fe66f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,95 @@
+# Boilerplate list of files in a PreTeXt project for git to ignore
+# ensure this file is tracked
+!.gitignore
+
+# don't track unpublished builds
+output
+
+# don't track assets generated from source
+generated-assets
+
+# don't track node packages
+node_modules
+
+# don't track error logs
+.error_schema.log
+cli.log
+
+# don't track OS related files (windows/macos/linux)
+.DS_Store
+.DS_Store?
+._*
+.AppleDouble
+.LSOverride
+.Spotlight-V100
+.Trashes
+Icon
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+*.stackdump
+*.lnk
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+[Dd]esktop.ini
+.directory
+.fuse_hidden*
+.Trash-*
+.nfs*
+
+# Don't include VSCode generated files
+.vscode
+*.code-workspace
+
+# Don't inlucde SublimeText files
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# Workspace files are user-specific
+*.sublime-workspace
+
+# Project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using Sublime Text
+*.sublime-project
+
+# SFTP configuration file
+sftp-config.json
+sftp-config-alt*.json
+
+# Package control specific files
+Package Control.last-run
+Package Control.ca-list
+Package Control.ca-bundle
+Package Control.system-ca-bundle
+Package Control.cache/
+Package Control.ca-certs/
+Package Control.merged-ca-bundle
+Package Control.user-ca-bundle
+oscrypto-ca-bundle.crt
+bh_unicode_properties.cache
+
+# Sublime-github package stores a github token in this file
+# https://packagecontrol.io/packages/sublime-github
+GitHub.sublime-settings
+
+
+# Don't include Dropbox settings and caches
+.dropbox
+.dropbox.attr
+.dropbox.cache
+
+# Original .gitignore file
__pycache__/
build/
build_info
diff --git a/pavement.py b/pavement.py
index 21294955..325974ab 100644
--- a/pavement.py
+++ b/pavement.py
@@ -28,6 +28,24 @@
else:
dest = "../../static"
+template_args={
+ 'course_id': 'fopp',
+ 'login_required':'false',
+ 'course_title': project_name,
+ 'appname':master_app,
+ 'dynamic_pages': True,
+ 'loglevel': 10,
+ 'course_url':master_url,
+ 'use_services': 'true',
+ 'python3': 'true',
+ 'dburl': 'postgresql://user:password@localhost/runestone',
+ 'default_ac_lang': 'python',
+ 'basecourse': 'fopp',
+ 'downloads_enabled': 'false',
+ 'enable_chatcodes': 'false',
+ 'allow_pairs': 'false'
+}
+
options(
sphinx = Bunch(docroot=".",),
diff --git a/pretext/AdvancedAccumulation/ChapterAssessment.ptx b/pretext/AdvancedAccumulation/ChapterAssessment.ptx
new file mode 100644
index 00000000..eb2ee691
--- /dev/null
+++ b/pretext/AdvancedAccumulation/ChapterAssessment.ptx
@@ -0,0 +1,200 @@
+
+ Chapter Assessment
+
+
+
+
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/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/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/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.
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.
+
diff --git a/pretext/Figures/AdvancedFunctions/Figures/lambda.gif b/pretext/Figures/AdvancedFunctions/Figures/lambda.gif
new file mode 100644
index 00000000..c545f360
Binary files /dev/null and b/pretext/Figures/AdvancedFunctions/Figures/lambda.gif differ
diff --git a/pretext/Figures/Classes/Figures/fractionpic1.png b/pretext/Figures/Classes/Figures/fractionpic1.png
new file mode 100644
index 00000000..4107f287
Binary files /dev/null and b/pretext/Figures/Classes/Figures/fractionpic1.png differ
diff --git a/pretext/Figures/Classes/Figures/fractionpic2.png b/pretext/Figures/Classes/Figures/fractionpic2.png
new file mode 100644
index 00000000..677db254
Binary files /dev/null and b/pretext/Figures/Classes/Figures/fractionpic2.png differ
diff --git a/pretext/Figures/Classes/Figures/fractionpic3.png b/pretext/Figures/Classes/Figures/fractionpic3.png
new file mode 100644
index 00000000..d675227d
Binary files /dev/null and b/pretext/Figures/Classes/Figures/fractionpic3.png differ
diff --git a/pretext/Figures/Classes/Figures/objectpic1.png b/pretext/Figures/Classes/Figures/objectpic1.png
new file mode 100644
index 00000000..cf2d3dd1
Binary files /dev/null and b/pretext/Figures/Classes/Figures/objectpic1.png differ
diff --git a/pretext/Figures/Classes/Figures/objectpic2.png b/pretext/Figures/Classes/Figures/objectpic2.png
new file mode 100644
index 00000000..9b8a37c3
Binary files /dev/null and b/pretext/Figures/Classes/Figures/objectpic2.png differ
diff --git a/pretext/Figures/Classes/Figures/objectpic3.png b/pretext/Figures/Classes/Figures/objectpic3.png
new file mode 100644
index 00000000..dfb6ffb2
Binary files /dev/null and b/pretext/Figures/Classes/Figures/objectpic3.png differ
diff --git a/pretext/Figures/Classes/Figures/objectpic4.png b/pretext/Figures/Classes/Figures/objectpic4.png
new file mode 100644
index 00000000..78e592c8
Binary files /dev/null and b/pretext/Figures/Classes/Figures/objectpic4.png differ
diff --git a/pretext/Figures/Classes/Figures/objectpic5.png b/pretext/Figures/Classes/Figures/objectpic5.png
new file mode 100644
index 00000000..12467b2e
Binary files /dev/null and b/pretext/Figures/Classes/Figures/objectpic5.png differ
diff --git a/pretext/Figures/Classes/Figures/point.png b/pretext/Figures/Classes/Figures/point.png
new file mode 100644
index 00000000..4b7dbcf5
Binary files /dev/null and b/pretext/Figures/Classes/Figures/point.png differ
diff --git a/pretext/Figures/Classes/Figures/tooltip_init.png b/pretext/Figures/Classes/Figures/tooltip_init.png
new file mode 100644
index 00000000..122cf5a6
Binary files /dev/null and b/pretext/Figures/Classes/Figures/tooltip_init.png differ
diff --git a/pretext/Figures/Conditionals/Figures/accum_o.gif b/pretext/Figures/Conditionals/Figures/accum_o.gif
new file mode 100644
index 00000000..5f1d450f
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/accum_o.gif differ
diff --git a/pretext/Figures/Conditionals/Figures/conditionals_overview.png b/pretext/Figures/Conditionals/Figures/conditionals_overview.png
new file mode 100644
index 00000000..3e0aa2ea
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/conditionals_overview.png differ
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_chained_conditional.png b/pretext/Figures/Conditionals/Figures/flowchart_chained_conditional.png
new file mode 100644
index 00000000..6683282f
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/flowchart_chained_conditional.png differ
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_if_else.png b/pretext/Figures/Conditionals/Figures/flowchart_if_else.png
new file mode 100644
index 00000000..da6eebe8
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/flowchart_if_else.png differ
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_if_only.png b/pretext/Figures/Conditionals/Figures/flowchart_if_only.png
new file mode 100644
index 00000000..8051cb3c
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/flowchart_if_only.png differ
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_if_only.svg b/pretext/Figures/Conditionals/Figures/flowchart_if_only.svg
new file mode 100644
index 00000000..555ce10f
--- /dev/null
+++ b/pretext/Figures/Conditionals/Figures/flowchart_if_only.svg
@@ -0,0 +1,124 @@
+
+
+
+
\ No newline at end of file
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.png b/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.png
new file mode 100644
index 00000000..dff98fb0
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.png differ
diff --git a/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.svg b/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.svg
new file mode 100644
index 00000000..2cfb7e03
--- /dev/null
+++ b/pretext/Figures/Conditionals/Figures/flowchart_nested_conditional.svg
@@ -0,0 +1,229 @@
+
+
+
+
\ No newline at end of file
diff --git a/pretext/Figures/Conditionals/Figures/valid_conditionals.png b/pretext/Figures/Conditionals/Figures/valid_conditionals.png
new file mode 100644
index 00000000..fb8ed25c
Binary files /dev/null and b/pretext/Figures/Conditionals/Figures/valid_conditionals.png differ
diff --git a/pretext/Figures/Debugging/Figures/error_dist.png b/pretext/Figures/Debugging/Figures/error_dist.png
new file mode 100644
index 00000000..20752e28
Binary files /dev/null and b/pretext/Figures/Debugging/Figures/error_dist.png differ
diff --git a/pretext/Figures/Files/Figures/ExampleFileHierarchy.png b/pretext/Figures/Files/Figures/ExampleFileHierarchy.png
new file mode 100644
index 00000000..c0007e18
Binary files /dev/null and b/pretext/Figures/Files/Figures/ExampleFileHierarchy.png differ
diff --git a/pretext/Figures/FrontBackMatter/Figures/LearningZones.png b/pretext/Figures/FrontBackMatter/Figures/LearningZones.png
new file mode 100644
index 00000000..d22c6e7f
Binary files /dev/null and b/pretext/Figures/FrontBackMatter/Figures/LearningZones.png differ
diff --git a/pretext/Figures/Functions/Figures/blackboxfun.png b/pretext/Figures/Functions/Figures/blackboxfun.png
new file mode 100644
index 00000000..d1f4a1e8
Binary files /dev/null and b/pretext/Figures/Functions/Figures/blackboxfun.png differ
diff --git a/pretext/Figures/Functions/Figures/blackboxproc.png b/pretext/Figures/Functions/Figures/blackboxproc.png
new file mode 100644
index 00000000..e7006472
Binary files /dev/null and b/pretext/Figures/Functions/Figures/blackboxproc.png differ
diff --git a/pretext/Figures/Functions/Figures/distance_formula.png b/pretext/Figures/Functions/Figures/distance_formula.png
new file mode 100644
index 00000000..bb8f16a9
Binary files /dev/null and b/pretext/Figures/Functions/Figures/distance_formula.png differ
diff --git a/pretext/Figures/Functions/Figures/five_squares.png b/pretext/Figures/Functions/Figures/five_squares.png
new file mode 100644
index 00000000..02584c72
Binary files /dev/null and b/pretext/Figures/Functions/Figures/five_squares.png differ
diff --git a/pretext/Figures/Functions/Figures/five_stars.png b/pretext/Figures/Functions/Figures/five_stars.png
new file mode 100644
index 00000000..1f680abe
Binary files /dev/null and b/pretext/Figures/Functions/Figures/five_stars.png differ
diff --git a/pretext/Figures/Functions/Figures/function_call.gif b/pretext/Figures/Functions/Figures/function_call.gif
new file mode 100644
index 00000000..0ca0c790
Binary files /dev/null and b/pretext/Figures/Functions/Figures/function_call.gif differ
diff --git a/pretext/Figures/Functions/Figures/nested_squares.png b/pretext/Figures/Functions/Figures/nested_squares.png
new file mode 100644
index 00000000..2e7596ca
Binary files /dev/null and b/pretext/Figures/Functions/Figures/nested_squares.png differ
diff --git a/pretext/Figures/Functions/Figures/regularpolygon.png b/pretext/Figures/Functions/Figures/regularpolygon.png
new file mode 100644
index 00000000..08fa1102
Binary files /dev/null and b/pretext/Figures/Functions/Figures/regularpolygon.png differ
diff --git a/pretext/Figures/Functions/Figures/squarefun.png b/pretext/Figures/Functions/Figures/squarefun.png
new file mode 100644
index 00000000..f8f3c2f7
Binary files /dev/null and b/pretext/Figures/Functions/Figures/squarefun.png differ
diff --git a/pretext/Figures/Functions/Figures/star.png b/pretext/Figures/Functions/Figures/star.png
new file mode 100644
index 00000000..c3257bee
Binary files /dev/null and b/pretext/Figures/Functions/Figures/star.png differ
diff --git a/pretext/Figures/Functions/Figures/tess08.png b/pretext/Figures/Functions/Figures/tess08.png
new file mode 100644
index 00000000..0cf6eb51
Binary files /dev/null and b/pretext/Figures/Functions/Figures/tess08.png differ
diff --git a/pretext/Figures/Functions/Figures/tess_bar_1.png b/pretext/Figures/Functions/Figures/tess_bar_1.png
new file mode 100644
index 00000000..d1e714db
Binary files /dev/null and b/pretext/Figures/Functions/Figures/tess_bar_1.png differ
diff --git a/pretext/Figures/Functions/Figures/tess_spirals.png b/pretext/Figures/Functions/Figures/tess_spirals.png
new file mode 100644
index 00000000..50bc7c88
Binary files /dev/null and b/pretext/Figures/Functions/Figures/tess_spirals.png differ
diff --git a/pretext/Figures/Functions/Figures/turtleproc.png b/pretext/Figures/Functions/Figures/turtleproc.png
new file mode 100644
index 00000000..b3b44477
Binary files /dev/null and b/pretext/Figures/Functions/Figures/turtleproc.png differ
diff --git a/pretext/Figures/GeneralIntro/Figures/compile.png b/pretext/Figures/GeneralIntro/Figures/compile.png
new file mode 100644
index 00000000..e297f732
Binary files /dev/null and b/pretext/Figures/GeneralIntro/Figures/compile.png differ
diff --git a/pretext/Figures/GeneralIntro/Figures/compile.svg b/pretext/Figures/GeneralIntro/Figures/compile.svg
new file mode 100644
index 00000000..f03b716c
--- /dev/null
+++ b/pretext/Figures/GeneralIntro/Figures/compile.svg
@@ -0,0 +1,820 @@
+
+
+
+
diff --git a/pretext/Figures/GeneralIntro/Figures/interpret.png b/pretext/Figures/GeneralIntro/Figures/interpret.png
new file mode 100644
index 00000000..f8a631f3
Binary files /dev/null and b/pretext/Figures/GeneralIntro/Figures/interpret.png differ
diff --git a/pretext/Figures/GeneralIntro/Figures/interpret.svg b/pretext/Figures/GeneralIntro/Figures/interpret.svg
new file mode 100644
index 00000000..cbf64107
--- /dev/null
+++ b/pretext/Figures/GeneralIntro/Figures/interpret.svg
@@ -0,0 +1,445 @@
+
+
+
+
diff --git a/pretext/Figures/Iteration/Figures/cy.png b/pretext/Figures/Iteration/Figures/cy.png
new file mode 100644
index 00000000..e9ff0f1a
Binary files /dev/null and b/pretext/Figures/Iteration/Figures/cy.png differ
diff --git a/pretext/Figures/Iteration/Figures/image.png b/pretext/Figures/Iteration/Figures/image.png
new file mode 100644
index 00000000..5eea32a5
Binary files /dev/null and b/pretext/Figures/Iteration/Figures/image.png differ
diff --git a/pretext/Figures/Iteration/Figures/iteration_vs_hardcoding.png b/pretext/Figures/Iteration/Figures/iteration_vs_hardcoding.png
new file mode 100644
index 00000000..773855f7
Binary files /dev/null and b/pretext/Figures/Iteration/Figures/iteration_vs_hardcoding.png differ
diff --git a/pretext/Figures/Iteration/Figures/new_flowchart_for.png b/pretext/Figures/Iteration/Figures/new_flowchart_for.png
new file mode 100644
index 00000000..f45f0368
Binary files /dev/null and b/pretext/Figures/Iteration/Figures/new_flowchart_for.png differ
diff --git a/pretext/Figures/Iteration/Figures/sum2n.PNG b/pretext/Figures/Iteration/Figures/sum2n.PNG
new file mode 100644
index 00000000..d3600091
Binary files /dev/null and b/pretext/Figures/Iteration/Figures/sum2n.PNG differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/cy.png b/pretext/Figures/MoreAboutIteration/Figures/cy.png
new file mode 100644
index 00000000..e9ff0f1a
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/cy.png differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/image.png b/pretext/Figures/MoreAboutIteration/Figures/image.png
new file mode 100644
index 00000000..5eea32a5
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/image.png differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/sum2n.PNG b/pretext/Figures/MoreAboutIteration/Figures/sum2n.PNG
new file mode 100644
index 00000000..d3600091
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/sum2n.PNG differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/while_and_break.png b/pretext/Figures/MoreAboutIteration/Figures/while_and_break.png
new file mode 100644
index 00000000..8646eff8
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/while_and_break.png differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/while_and_continue.png b/pretext/Figures/MoreAboutIteration/Figures/while_and_continue.png
new file mode 100644
index 00000000..6c2c5378
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/while_and_continue.png differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/while_flow.png b/pretext/Figures/MoreAboutIteration/Figures/while_flow.png
new file mode 100644
index 00000000..d549f452
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/while_flow.png differ
diff --git a/pretext/Figures/MoreAboutIteration/Figures/while_loop.png b/pretext/Figures/MoreAboutIteration/Figures/while_loop.png
new file mode 100644
index 00000000..6201c618
Binary files /dev/null and b/pretext/Figures/MoreAboutIteration/Figures/while_loop.png differ
diff --git a/pretext/Figures/Projects/Figures/The_Citadel.png b/pretext/Figures/Projects/Figures/The_Citadel.png
new file mode 100644
index 00000000..e84de5ec
Binary files /dev/null and b/pretext/Figures/Projects/Figures/The_Citadel.png differ
diff --git a/pretext/Figures/Projects/Figures/anscombe_512.svg.png b/pretext/Figures/Projects/Figures/anscombe_512.svg.png
new file mode 100644
index 00000000..9666b59a
Binary files /dev/null and b/pretext/Figures/Projects/Figures/anscombe_512.svg.png differ
diff --git a/pretext/Figures/Projects/Figures/bar_chart.png b/pretext/Figures/Projects/Figures/bar_chart.png
new file mode 100644
index 00000000..e577b0c2
Binary files /dev/null and b/pretext/Figures/Projects/Figures/bar_chart.png differ
diff --git a/pretext/Figures/Projects/Figures/caesar_wheel.jpg b/pretext/Figures/Projects/Figures/caesar_wheel.jpg
new file mode 100644
index 00000000..bb251406
Binary files /dev/null and b/pretext/Figures/Projects/Figures/caesar_wheel.jpg differ
diff --git a/pretext/Figures/Projects/Figures/enigma.jpg b/pretext/Figures/Projects/Figures/enigma.jpg
new file mode 100644
index 00000000..67ad4d5a
Binary files /dev/null and b/pretext/Figures/Projects/Figures/enigma.jpg differ
diff --git a/pretext/Figures/Projects/Figures/histogram.png b/pretext/Figures/Projects/Figures/histogram.png
new file mode 100644
index 00000000..7e71ba5a
Binary files /dev/null and b/pretext/Figures/Projects/Figures/histogram.png differ
diff --git a/pretext/Figures/Projects/Figures/pizza_best_fit.png b/pretext/Figures/Projects/Figures/pizza_best_fit.png
new file mode 100644
index 00000000..4a731385
Binary files /dev/null and b/pretext/Figures/Projects/Figures/pizza_best_fit.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/BlueTurtleL.png b/pretext/Figures/PythonTurtle/Figures/BlueTurtleL.png
new file mode 100644
index 00000000..1d77cdfa
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/BlueTurtleL.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/Turtle3Stamp.png b/pretext/Figures/PythonTurtle/Figures/Turtle3Stamp.png
new file mode 100644
index 00000000..1384666f
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/Turtle3Stamp.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TurtleCheckmark4.png b/pretext/Figures/PythonTurtle/Figures/TurtleCheckmark4.png
new file mode 100644
index 00000000..4a0e875d
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TurtleCheckmark4.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TurtleCircle.png b/pretext/Figures/PythonTurtle/Figures/TurtleCircle.png
new file mode 100644
index 00000000..40056814
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TurtleCircle.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TurtleL4.png b/pretext/Figures/PythonTurtle/Figures/TurtleL4.png
new file mode 100644
index 00000000..413ea2f8
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TurtleL4.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TurtleLineToWest.png b/pretext/Figures/PythonTurtle/Figures/TurtleLineToWest.png
new file mode 100644
index 00000000..3d538028
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TurtleLineToWest.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TurtleT.png b/pretext/Figures/PythonTurtle/Figures/TurtleT.png
new file mode 100644
index 00000000..8f472da6
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TurtleT.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TwoTurtles1.png b/pretext/Figures/PythonTurtle/Figures/TwoTurtles1.png
new file mode 100644
index 00000000..ce0e3576
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TwoTurtles1.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/TwoTurtlesL.png b/pretext/Figures/PythonTurtle/Figures/TwoTurtlesL.png
new file mode 100644
index 00000000..f337b4e7
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/TwoTurtlesL.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/new_flowchart_for.png b/pretext/Figures/PythonTurtle/Figures/new_flowchart_for.png
new file mode 100644
index 00000000..f45f0368
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/new_flowchart_for.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/star.png b/pretext/Figures/PythonTurtle/Figures/star.png
new file mode 100644
index 00000000..c3257bee
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/star.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/tess_clock1.png b/pretext/Figures/PythonTurtle/Figures/tess_clock1.png
new file mode 100644
index 00000000..ad61838e
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/tess_clock1.png differ
diff --git a/pretext/Figures/PythonTurtle/Figures/turtleTest1.png b/pretext/Figures/PythonTurtle/Figures/turtleTest1.png
new file mode 100644
index 00000000..c1732f4c
Binary files /dev/null and b/pretext/Figures/PythonTurtle/Figures/turtleTest1.png differ
diff --git a/pretext/Figures/Sequences/Figures/indexvalues.png b/pretext/Figures/Sequences/Figures/indexvalues.png
new file mode 100644
index 00000000..6d3a8887
Binary files /dev/null and b/pretext/Figures/Sequences/Figures/indexvalues.png differ
diff --git a/pretext/Figures/Sequences/Figures/join.gif b/pretext/Figures/Sequences/Figures/join.gif
new file mode 100644
index 00000000..54e889c6
Binary files /dev/null and b/pretext/Figures/Sequences/Figures/join.gif differ
diff --git a/pretext/Figures/Sequences/Figures/slicing.png b/pretext/Figures/Sequences/Figures/slicing.png
new file mode 100644
index 00000000..85fffa81
Binary files /dev/null and b/pretext/Figures/Sequences/Figures/slicing.png differ
diff --git a/pretext/Figures/Sequences/Figures/split_default.gif b/pretext/Figures/Sequences/Figures/split_default.gif
new file mode 100644
index 00000000..7d71a209
Binary files /dev/null and b/pretext/Figures/Sequences/Figures/split_default.gif differ
diff --git a/pretext/Figures/Sequences/Figures/split_on_e.jpeg b/pretext/Figures/Sequences/Figures/split_on_e.jpeg
new file mode 100644
index 00000000..5331b194
Binary files /dev/null and b/pretext/Figures/Sequences/Figures/split_on_e.jpeg differ
diff --git a/pretext/Figures/SimplePythonData/Figures/compoundInterest.png b/pretext/Figures/SimplePythonData/Figures/compoundInterest.png
new file mode 100644
index 00000000..386042c9
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/compoundInterest.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/expression_value_type.png b/pretext/Figures/SimplePythonData/Figures/expression_value_type.png
new file mode 100644
index 00000000..543d8a01
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/expression_value_type.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/function_calls.gif b/pretext/Figures/SimplePythonData/Figures/function_calls.gif
new file mode 100644
index 00000000..2d0bcb2a
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/function_calls.gif differ
diff --git a/pretext/Figures/SimplePythonData/Figures/function_object.png b/pretext/Figures/SimplePythonData/Figures/function_object.png
new file mode 100644
index 00000000..5267e786
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/function_object.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/python_shell.png b/pretext/Figures/SimplePythonData/Figures/python_shell.png
new file mode 100644
index 00000000..e1da0b15
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/python_shell.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/reassign1.png b/pretext/Figures/SimplePythonData/Figures/reassign1.png
new file mode 100644
index 00000000..afd57399
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/reassign1.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/refdiagram1.png b/pretext/Figures/SimplePythonData/Figures/refdiagram1.png
new file mode 100644
index 00000000..f1828833
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/refdiagram1.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/shell_expressions.png b/pretext/Figures/SimplePythonData/Figures/shell_expressions.png
new file mode 100644
index 00000000..65bcca58
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/shell_expressions.png differ
diff --git a/pretext/Figures/SimplePythonData/Figures/square_function.gif b/pretext/Figures/SimplePythonData/Figures/square_function.gif
new file mode 100644
index 00000000..e4261ab7
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/square_function.gif differ
diff --git a/pretext/Figures/SimplePythonData/Figures/type_cast.gif b/pretext/Figures/SimplePythonData/Figures/type_cast.gif
new file mode 100644
index 00000000..eee95ba0
Binary files /dev/null and b/pretext/Figures/SimplePythonData/Figures/type_cast.gif differ
diff --git a/pretext/Figures/TestCases/Figures/distance_formula.png b/pretext/Figures/TestCases/Figures/distance_formula.png
new file mode 100644
index 00000000..bb8f16a9
Binary files /dev/null and b/pretext/Figures/TestCases/Figures/distance_formula.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/refdiag1.png b/pretext/Figures/TransformingSequences/Figures/refdiag1.png
new file mode 100644
index 00000000..42c80ace
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/refdiag1.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/refdiag2.png b/pretext/Figures/TransformingSequences/Figures/refdiag2.png
new file mode 100644
index 00000000..3d9e55f7
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/refdiag2.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/refdiag3.png b/pretext/Figures/TransformingSequences/Figures/refdiag3.png
new file mode 100644
index 00000000..13a548b2
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/refdiag3.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/refdiag4.png b/pretext/Figures/TransformingSequences/Figures/refdiag4.png
new file mode 100644
index 00000000..c061f435
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/refdiag4.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a1_1.png b/pretext/Figures/TransformingSequences/Figures/week3a1_1.png
new file mode 100644
index 00000000..b56c2c04
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a1_1.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a1_2.png b/pretext/Figures/TransformingSequences/Figures/week3a1_2.png
new file mode 100644
index 00000000..af01e3de
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a1_2.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a2_1.png b/pretext/Figures/TransformingSequences/Figures/week3a2_1.png
new file mode 100644
index 00000000..322c817f
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a2_1.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a2_2.png b/pretext/Figures/TransformingSequences/Figures/week3a2_2.png
new file mode 100644
index 00000000..f217b61d
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a2_2.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a2_3.png b/pretext/Figures/TransformingSequences/Figures/week3a2_3.png
new file mode 100644
index 00000000..5e0126a6
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a2_3.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a2_4.png b/pretext/Figures/TransformingSequences/Figures/week3a2_4.png
new file mode 100644
index 00000000..a851ace2
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a2_4.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a3_1.png b/pretext/Figures/TransformingSequences/Figures/week3a3_1.png
new file mode 100644
index 00000000..f6584b16
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a3_1.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a3_2.png b/pretext/Figures/TransformingSequences/Figures/week3a3_2.png
new file mode 100644
index 00000000..8b440bb4
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a3_2.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a3_3.png b/pretext/Figures/TransformingSequences/Figures/week3a3_3.png
new file mode 100644
index 00000000..45f40c57
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a3_3.png differ
diff --git a/pretext/Figures/TransformingSequences/Figures/week3a3_4.png b/pretext/Figures/TransformingSequences/Figures/week3a3_4.png
new file mode 100644
index 00000000..3a057485
Binary files /dev/null and b/pretext/Figures/TransformingSequences/Figures/week3a3_4.png differ
diff --git a/pretext/Figures/_static/LutherBellPic.jpg b/pretext/Figures/_static/LutherBellPic.jpg
new file mode 100644
index 00000000..c6469a3b
Binary files /dev/null and b/pretext/Figures/_static/LutherBellPic.jpg differ
diff --git a/pretext/Figures/_static/cy.png b/pretext/Figures/_static/cy.png
new file mode 100644
index 00000000..e9ff0f1a
Binary files /dev/null and b/pretext/Figures/_static/cy.png differ
diff --git a/pretext/Figures/_static/ducky.jpg b/pretext/Figures/_static/ducky.jpg
new file mode 100755
index 00000000..54268f46
Binary files /dev/null and b/pretext/Figures/_static/ducky.jpg differ
diff --git a/pretext/Figures/_static/goldygopher.png b/pretext/Figures/_static/goldygopher.png
new file mode 100644
index 00000000..a05d68ea
Binary files /dev/null and b/pretext/Figures/_static/goldygopher.png differ
diff --git a/pretext/Figures/_static/yawning_squirrel.jpg b/pretext/Figures/_static/yawning_squirrel.jpg
new file mode 100644
index 00000000..9f33b712
Binary files /dev/null and b/pretext/Figures/_static/yawning_squirrel.jpg differ
diff --git a/pretext/Files/AlternativeFileReadingMethods.ptx b/pretext/Files/AlternativeFileReadingMethods.ptx
new file mode 100644
index 00000000..675b42b1
--- /dev/null
+++ b/pretext/Files/AlternativeFileReadingMethods.ptx
@@ -0,0 +1,213 @@
+
+
+ Alternative File Reading Methods
+
Once you have a file object, the thing returned by the open function, Python provides three methods to read data
+ from that object. The read() method returns the entire contents of the file as a single string (or just some
+ characters if you provide a number as an input parameter. The readlines method returns the entire contents of
+ the entire file as a list of strings, where each item in the list is one line of the file. The readline
+ method reads one line from the file and returns it as a string. The strings returned by readlines or
+ readline will contain the newline character at the end. summarizes these
+ methods and the following session shows them in action.
+
+
+
+
+ Method Name
+
+
+ Use
+
+
+ Explanation
+
+
+
+
+ write
+
+
+ filevar.write(astring)
+
+
+ Add a string to the end of the file.
+ filevar must refer to a file that has
+ been opened for writing.
+
+
+
+
+ read(n)
+
+
+ filevar.read()
+
+
+ Read and return a string of n
+ characters, or the entire file as a
+ single string if n is not provided.
+
+
+
+
+ readline(n)
+
+
+ filevar.readline()
+
+
+ Read and return the next line of the file with
+ all text up to and including the
+ newline character. If n is provided as
+ a parameter, then only n characters
+ will be returned if the line is longer
+ than n. Note the parameter n is not supported in the browser version of Python, and in fact is rarely used in practice, you can safely ignore it.
+
+
+
+
+ readlines(n)
+
+
+ filevar.readlines()
+
+
+ Returns a list of strings, each
+ representing a single line of the file.
+ If n is not provided then all lines of
+ the file are returned. If n is provided
+ then n characters are read but n is
+ rounded up so that an entire line is
+ returned. Note Like readlinereadlines ignores the parameter n in the browser.
+
+
+
+
+
In this course, we will generally either iterate through the lines returned by readlines() with a for loop,
+ or use read() to get all of the contents as a single string.
+
In other programming languages, where they don't have the convenient for loop method of going through the lines
+ of the file one by one, they use a different pattern which requires a different kind of loop, the while loop.
+ Fortunately, you don't need to learn this other pattern, and we will put off consideration of while loops
+ until later in this course. We don't need them for handling data from files.
+
+
A common error that novice programmers make is not realizing that all these ways of reading the file contents,
+ use up the file. After you call readlines(), if you call it again you'll get an empty list.
+
+
+ Check your Understanding
+
+ <pre id="school_prompt.txt">
+Writing essays for school can be difficult but
+many students find that by researching their topic that they
+have more to say and are better informed. Here are the university
+we require many undergraduate students to take a first year writing requirement
+so that they can
+have a solid foundation for their writing skills. This comes
+in handy for many students.
+Different schools have different requirements, but everyone uses
+writing at some point in their academic career, be it essays, research papers,
+technical write ups, or scripts.
+</pre>
+
+
+
+
+
+
Using the file school_prompt2.txt, find the number of characters in the file and assign that value to the variable num_char.
+
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(num_char, 537, "Testing that num_char has the correct value.")
+
+myTests().main()
+
+
+
+ <pre id="travel_plans.txt">
+This summer I will be travelling.
+I will go to...
+Italy: Rome
+Greece: Athens
+England: London, Manchester
+France: Paris, Nice, Lyon
+Spain: Madrid, Barcelona, Granada
+Austria: Vienna
+I will probably not even want to come back!
+However, I wonder how I will get by with all the different languages.
+I only know English!
+</pre>
+
+
+
+
+
+
Find the number of lines in the file, travel_plans2.txt, and assign it to the variable num_lines.
Create a string called first_forty that is comprised of the first 40 characters of emotion_words2.txt.
+
+
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(first_forty, 'Sad upset blue down melancholy somber bi', "Testing that first_forty was created correctly.")
+myTests().main()
+
+
+
+
+
+
+
+
+
+
diff --git a/pretext/Files/CSVFormat.ptx b/pretext/Files/CSVFormat.ptx
new file mode 100644
index 00000000..765cd80e
--- /dev/null
+++ b/pretext/Files/CSVFormat.ptx
@@ -0,0 +1,15 @@
+
+
+ CSV Format
+
CSV stands for Comma Separated Values. If you print out tabular data in CSV format, it can be easily imported into other programs like Excel, Google spreadsheets, or a statistics package (R, stata, SPSS, etc.).
+
For example, we can make a file with the following contents. If you save it as a file name grades.csv, then you could import it into one of those programs. The first line gives the column names and the later lines each give the data for one row.
The textfile, travel_plans.txt, contains the summer travel plans for someone with some commentary. Find the total number of characters in the file and save to the variable num.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(num, 316, "Testing that num value is assigned to correct value.")
+
+myTests().main()
+
+
+
+
+
+
We have provided a file called emotion_words.txt that contains lines of words that describe emotions. Find the total number of words in the file and assign this value to the variable num_words.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(num_words, 48, "Testing that num_words was assigned to the correct value.")
+
+myTests().main()
+
+
+
+
+
+
Assign to the variable num_lines the number of lines in the file school_prompt.txt.
Read in the contents of the file SP500.txt which has monthly data for 2016 and 2017 about the S&P 500 closing prices as well as some other financial indicators, including the Long Term Interest Rate, which is interest rate paid on 10-year U.S. government bonds.
+
Write a program that computes the average closing price (the second column, labeled SP500) and the highest long-term interest rate. Both should be computed only for the period from June 2016 through May 2017. Save the results in the variables mean_SP and max_interest.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertLess(abs(mean_SP - 2237), 0.5, "Testing that mean_SP is within 0.5 of the correct value. Make sure to use only the correct 12 month period.")
+ self.assertEqual(max_interest, 2.49, "Testing the max_interest is correct. Make sure to use only the correct 12 month period.")
+
+myTests().main()
+
+
+
+
+
+
diff --git a/pretext/Files/Exercises.ptx b/pretext/Files/Exercises.ptx
new file mode 100644
index 00000000..7506b4b2
--- /dev/null
+++ b/pretext/Files/Exercises.ptx
@@ -0,0 +1,2 @@
+
+
diff --git a/pretext/Files/FilesRecipe.ptx b/pretext/Files/FilesRecipe.ptx
new file mode 100644
index 00000000..2ff36aaf
--- /dev/null
+++ b/pretext/Files/FilesRecipe.ptx
@@ -0,0 +1,26 @@
+
+
+ Recipe for Reading and Processing a File
+
Here's a foolproof recipe for processing the contents of a text file. If you've fully digested the previous sections,
+ you'll understand that there are other options as well. Some of those options are preferable for some situations, and
+ some are preferred by python programmers for efficiency reasons. In this course, though, you can always succeed by
+ following this recipe.
+
#1. Open the file using with and open.
+
#2. Use .readlines() to get a list of the lines of text in the file.
+
#3. Use a for loop to iterate through the strings in the list, each being one line from the file. On each iteration, process that line of text
+
#4. When you are done extracting data from the file, continue writing your code outside of the indentation. Using with will automatically close the file once the program exits the with block.
+
fname = "yourfile.txt"
+with open(fname, 'r') as fileref: # step 1
+ lines = fileref.readlines() # step 2
+ for lin in lines: # step 3
+ #some code that references the variable lin
+#some other code not relying on fileref # step 4
+
However, this will not be good to use when you are working with large data. Imagine working with a datafile that has 1000
+ rows of data. It would take a long time to read in all the data and then if you had to iterate over it, even more time
+ would be necessary. This would be a case where programmers prefer another option for efficiency reasons.
+
This option involves iterating over the file itself while still iterating over each line in the file:
+
with open(fname, 'r') as fileref:
+ for lin in fileref:
+ ## some code that uses line as the current line of the file
+ ## some more code
+
diff --git a/pretext/Files/FindingaFileonyourDisk.ptx b/pretext/Files/FindingaFileonyourDisk.ptx
new file mode 100644
index 00000000..be9606f8
--- /dev/null
+++ b/pretext/Files/FindingaFileonyourDisk.ptx
@@ -0,0 +1,107 @@
+
+
+ Finding a File in your Filesystem
+
In the examples we have provided, and in the simulated file system that we've built for this online textbook, all files sit in a single directory, and it's the same directory where the Python program is stored. Thus, we can just write open('myfile.txt', 'r').
+
If you have installed Python on your local computer and you are trying to get file reading and writing operations to work, there's a little more that you may need to understand. Computer operating systems (like Windows and the Mac OS) organize files into a hierarchy of folders, with some folders containing other folders.
+
+
If your file and your Python program are in the same directory you can simply use
+ the filename. For example, with the file hierarchy in the diagram, the file myPythonProgram.py could contain the code open('data1.txt', 'r').
+
If your file and your Python program are in different directories, however, then you need to specify a path. You can think of the filename as the short name for a file, and the path as the full name. Typically, you will specify a relative file path, which says where to find the file to open, relative to the directory where the code is running from. For example, the file myPythonProgram.py could contain the code open('../myData/data2.txt', 'r'). The ../ means to go up one level in the directory structure, to the containing folder (allProjects); myData/ says to descend into the myData subfolder.
+
There is also an option to use an absolute file path. For example, suppose the file structure in the figure is stored on a computer in the user's home directory, /Users/joebob01/myFiles. Then code in any Python program running from any file folder could open data2.txt via open('/Users/joebob01/myFiles/allProjects/myData/data2.txt', 'r'). You can tell an absolute file path because it begins with a /.
+ If you will ever move your programs and data to another computer (e.g., to share them with someone else), it will be much more convenient if your use relative file paths rather than absolute. That way, if you preserve the folder structure when moving everything, you won't need to change your code. If you use absolute paths, then the person you are sharing with probably not have the same home directory name, /Users/joebob01/. Note that Python pathnames follow the UNIX conventions (Mac OS is a UNIX variant), rather than the Windows file pathnames that use : and \. The Python interpreter will translate to Windows pathnames when running on a Windows machine; you should be able to share your Python program between a Windows machine and a MAC without having to rewrite the file open commands.
+
+
For security reasons, our code running in your browser doesn't read or write files to your computer's file
+ system. Later, when you run Python natively on your own computer, you will be able to truly read files, using
+ path names as suggested above. To get you started, we have faked it by providing a few files that you can read
+ as if they were on your hard disk. In this chapter, we simulate the existence of one textfile; you can't open any other files from your local computer from textbook code running in your browser.
+
+
+ Check Your Understanding
+
+
+
+
Say you are in a directory called Project. In it, you have a file with your Python code. You would like to read in data from a file called YearlyProjections.csv which is in a folder called CompanyData, which is inside of Project. What is the best way to open the file in your Python program?
+
+
+
+
+
open("YearlyProjections.csv", "r")
+
+
+
This would try to open a file inside of Project (but that is not where the file is.)
+
+
+
+
+
open("../CompanyData/YearlyProjections.csv", "r")
+
+
+
This would go to the parent directory of Project and look for a subdirectory of that called CompanyData. But CompanyData is inside Project so it wouldn't be found.
This would try to find a subdirectory Project of the current directory called Project.
+
+
+
+
+
open("../YearlyProjections.csv", "r")
+
+
+
Remember that '..' will bring you up one level to the parent directory. This would try to open a csv file in the parent directory of Project (but that is not where the file is.)
+
+
+
+
+
+
+
Which of the following paths are relative file paths?
+
+
+
+
+
"Stacy/Applications/README.txt"
+
+
+
Yes, this is a relative file path. You can tell by the lack of "/" at the beginning of the path.
+
+
+
+
+
"/Users/Raquel/Documents/graduation_plans.doc"
+
+
+
This is an absolute file path. All absolute file paths start with "/".
+
+
+
+
+
"/private/tmp/swtag.txt"
+
+
+
This is an absolute file path. Not all absolute file paths contain "User"! Instead, check to see if the path starts with "/".
+
+
+
+
+
"ScienceData/ProjectFive/experiment_data.csv"
+
+
+
Yes, this is a relative file path. You can tell by the lack of "/" at the beginning of the path.
You must open a file before you can read its contents.
+
+
+ close
+
When you are done with a file, you should close it.
+
+
+ read
+
Will read the entire contents of a file as a string. This is often used in an assignment statement
+ so that a variable can reference the contents of the file.
+
+
+ readline
+
Will read a single line from the file, up to and including the first instance of the newline character.
+
+
+ readlines
+
Will read the entire contents of a file into a list where each line of the file is a string and is an element in the list.
+
+
+ write
+
Will add characters to the end of a file that has been opened for writing.
+
+
+
diff --git a/pretext/Files/Iteratingoverlinesinafile.ptx b/pretext/Files/Iteratingoverlinesinafile.ptx
new file mode 100644
index 00000000..a5a37431
--- /dev/null
+++ b/pretext/Files/Iteratingoverlinesinafile.ptx
@@ -0,0 +1,112 @@
+
+
+ Iterating over lines in a file
+
We will now use this file as input in a program that will do some data processing. In the program, we will
+ examine each line of the file and print it with some additional text. Because readlines() returns a list of
+ lines of text, we can use the for loop to iterate through each line of the file.
+
A line of a file is defined to be a sequence of characters up to and including a special character called
+ the newline character. If you evaluate a string that contains a newline character you will see the character
+ represented as \n. If you print a string that contains a newline you will not see the \n, you will just
+ see its effects (a carriage return).
+
As the for loop iterates through each line of the file the loop variable will contain the current line of the
+ file as a string of characters. The general pattern for processing each line of a text file is as follows:
+
for line in myFile.readlines():
+ statement1
+ statement2
+ ...
+
To process all of our olympics data, we will use a for loop to iterate over the lines of the file. Using
+ the split method, we can break each line into a list containing all the fields of interest about the
+ athlete. We can then take the values corresponding to name, team and event to
+ construct a simple sentence.
+
+
+olympicsfile = open("olympics.txt", "r")
+
+for aline in olympicsfile.readlines():
+ values = aline.split(",")
+ print(values[0], "is from", values[3], "and is on the roster for", values[4])
+
+olympicsfile.close()
+
+
+
To make the code a little simpler, and to allow for more efficient processing, Python provides a built-in way to
+ iterate through the contents of a file one line at a time, without first reading them all into a list. Some students find this confusing initially, so we don't recommend doing it this way, until you get a
+ little more comfortable with Python. But this idiom is preferred by Python programmers, so you should be prepared
+ to read it. And when you start dealing with big files, you may notice the efficiency gains of using it.
+
+
+olympicsfile = open("olympics.txt", "r")
+
+for aline in olympicsfile:
+ values = aline.split(",")
+ print(values[0], "is from", values[3], "and is on the roster for", values[4])
+
+olympicsfile.close()
+
+
+ <pre hidden id="olympics.txt">
+Name,Sex,Age,Team,Event,Medal
+A Dijiang,M,24,China,Basketball,NA
+A Lamusi,M,23,China,Judo,NA
+Gunnar Nielsen Aaby,M,24,Denmark,Football,NA
+Edgar Lindenau Aabye,M,34,Denmark/Sweden,Tug-Of-War,Gold
+Christine Jacoba Aaftink,F,21,Netherlands,Speed Skating,NA
+Christine Jacoba Aaftink,F,25,Netherlands,Speed Skating,NA
+Christine Jacoba Aaftink,F,25,Netherlands,Speed Skating,NA
+Christine Jacoba Aaftink,F,27,Netherlands,Speed Skating,NA
+Per Knut Aaland,M,31,United States,Cross Country Skiing,NA
+Per Knut Aaland,M,33,United States,Cross Country Skiing,NA
+John Aalberg,M,31,United States,Cross Country Skiing,NA
+John Aalberg,M,33,United States,Cross Country Skiing,NA
+"Cornelia ""Cor"" Aalten (-Strannood)",F,18,Netherlands,Athletics,NA
+"Cornelia ""Cor"" Aalten (-Strannood)",F,18,Netherlands,Athletics,NA
+Antti Sami Aalto,M,26,Finland,Ice Hockey,NA
+"Einar Ferdinand ""Einari"" Aalto",M,26,Finland,Swimming,NA
+Jorma Ilmari Aalto,M,22,Finland,Cross Country Skiing,NA
+Jyri Tapani Aalto,M,31,Finland,Badminton,NA
+Minna Maarit Aalto,F,30,Finland,Sailing,NA
+Minna Maarit Aalto,F,34,Finland,Sailing,NA
+Pirjo Hannele Aalto (Mattila-),F,32,Finland,Biathlon,NA
+Timo Antero Aaltonen,M,31,Finland,Athletics,NA
+Win Valdemar Aaltonen,M,54,Finland,Art Competitions,NA
+</pre>
+
Write code to find out how many lines are in the file emotion_words.txt as shown above. Save this value to the variable num_lines. Do not use the len method.
+
+
+
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(num_lines, 7, "Testing that num_lines was assigned to the correct value.")
+ self.assertNotIn('len', self.getEditorText(), "Checking that you didn't use the len function.")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Files/ReadingCSVFiles.ptx b/pretext/Files/ReadingCSVFiles.ptx
new file mode 100644
index 00000000..b1aa540f
--- /dev/null
+++ b/pretext/Files/ReadingCSVFiles.ptx
@@ -0,0 +1,39 @@
+
+
+ Reading in data from a CSV File
+
We are able to read in CSV files the same way we have with other text files. Because of the standardized structure of the data, there is a common pattern for processing it. To practice this,
+ we will be using data about olympic events.
+
Typically, CSV files will have a header as the first line, which contains column names. Then,
+ each following row in the file will contain data that corresponds to the appropriate columns.
+
All file methods that we have mentioned - read, readline, and readlines, and simply iterating over the file object itself - will work on CSV files. In our examples, we will iterate over the lines. Because the values on each line are separated with commas, we can use the .split() method to parse each line into a collection of separate value.
In the above code, we open the file, olympics.txt, which contains data on some olympians. The contents are similar to our previous olympics file, but include an extra column with information about medals they won.
+
We split the first row to get the field names. We split other rows to get values. Note that we specify to split on commas by passing that as a parameter. Also note that we first pass the row through the .strip() method to get rid of the trailing n.
+
Once we have parsed the lines into their separate values, we can use those values in the program. For example, in the code above, we select only those rows where the olympian won a medal, and we print out only three of the fields, in a different format.
+
Note that the trick of splitting the text for each row based on the presence of commas only works because commas are not used in any of the field values. Suppose that some of our events were more specific, and used commas. For example, Swimming, 100M Freestyle. How will a program processing a .csv file know when a comma is separating columns, and when it is just part of the text string giving a value within a column?
+
The CSV format is actually a little more general than we have described and has a couple of solutions for that problem. One alternative format uses a different column separator, such as | or a tab (\t). Sometimes, when a tab is used, the format is called tsv, for tab-separated values). If you get a file using a different separator, you can just call the .split('|') or .split('\t').
+
The other advanced CSV format uses commas to separate but encloses all values in double quotes.
If you are reading a .csv file that has enclosed all values in double quotes, it's actually a pretty tricky programming problem to split the text for one row into a list of values. You won't want to try to do it directly. Instead, you should use python's built-in csv module. However, there's a bit of a learning curve for that, and we find that students gain a better understanding of reading CSV format by first learning to read the simple, unquoted format and split lines on commas.
+
diff --git a/pretext/Files/ReadingaFile.ptx b/pretext/Files/ReadingaFile.ptx
new file mode 100644
index 00000000..65a9c968
--- /dev/null
+++ b/pretext/Files/ReadingaFile.ptx
@@ -0,0 +1,23 @@
+
+
+ Reading a File
+
As an example, suppose we have a text file called olympics.txt that contains
+ the data representing about olympians across different years. The contents of the file are shown at the bottom of the page.
+
To open this file, we would call the open function. The variable,
+ fileref, now holds a reference to the file object returned by
+ open. When we are finished with the file, we can close it by using
+ the close method. After the file is closed any further attempts to
+ use fileref will result in an error.
+
+
+fileref = open("olympics.txt", "r")
+## other code here that refers to variable fileref
+fileref.close()
+
+
+
+
A common mistake is to get confused about whether you are providing a variable name or a string literal as an input to the open function. In the code above, olympics.txt is a string literal that should correspond to the name of a file on your computer. If you put something without quotes, like open(x, "r"), it will be treated as a variable name. In this example, x should be a variable that's already been bound to a string value like olympics.txt.
When working with files, there are a few things to keep in mind. When naming files, it's best to not include spaces.
+ While most operating systems can handle files with spaces in their names, not all can.
+
Additionally, suffixes in files names, for example the .txt in FileNameExample.txt, are not magic. Instead, these
+ suffixes are a convention. For some operating systems the suffixes have no special significance, and only have meaning when
+ used in a program. Other operating systems infer information from the suffixes - for example, .EXE is a suffix that
+ means a file is executable.
+
It's a good idea to follow the conventions. If a file contains CSV formatted data, name it with the extension .csv, not .txt. A Python program will be able to read it either way, but if you follow the convention you will help other people guess what's in the file. And you will also help the computer's operating system to guess what application program it should open when you double-click on the file.
+
diff --git a/pretext/Files/With.ptx b/pretext/Files/With.ptx
new file mode 100644
index 00000000..313930cf
--- /dev/null
+++ b/pretext/Files/With.ptx
@@ -0,0 +1,40 @@
+
+
+ Using with for Files
+
+
This section is a bit of an advanced topic and can be easily skipped. But with statements are becoming very common and it doesn't hurt to know about them in case you run into one in the wild.
+
+
Now that you have seen and practiced a bit with opening and closing files, there is another mechanism that Python
+ provides for us that cleans up the often forgotten close. Forgetting to close a file does not necessarily cause a runtime
+ error in the kinds of programs you typically write in an introductory programing course. However if you are writing a
+ program that may run for days or weeks at a time that does a lot of file reading and writing you may run into trouble.
+
Python has the notion of a context manager that automates the process of doing
+ common operations at the start of some task, as well as automating certain operations at the end of some task. For reading and writing a file, the normal operation is to open the file and assign it to a variable. At the end
+ of working with a file the common operation is to make sure that file is closed.
+
The Python with statement makes using context managers easy. The general form of a with statement is:
+
with <create some object that understands context> as <some name>:
+ do some stuff with the object
+ ...
+
When the program exits the with block, the context manager handles the common stuff that normally happens at the end, in our case closing a file. A simple example will clear up all of this abstract discussion of contexts. Here are the contents of a file called mydata.txt.
+
+
+
+
+with open('mydata.txt', 'r') as md:
+ for line in md:
+ print(line)
+# continue on with other code
+
+
+
The first line of the with statement opens the file and assigns it to the variable md. Then we can iterate over the file in any of the usual ways. When we are done we simply stop indenting and let Python take care of closing the file and cleaning up.
+
This is equivalent to code that specifically closes the file at the end, but neatly marks the set of code that can make use of the open file as an indented block, and ensures that the programmer won't forget to include the .close() invocation.
+
+
+md = open('mydata.txt', 'r')
+for line in md:
+ print(line)
+md.close()
+# continue with other code
+
+
+
diff --git a/pretext/Files/WritingCSVFiles.ptx b/pretext/Files/WritingCSVFiles.ptx
new file mode 100644
index 00000000..5c2894ff
--- /dev/null
+++ b/pretext/Files/WritingCSVFiles.ptx
@@ -0,0 +1,74 @@
+
+
+ Writing data to a CSV File
+
The typical pattern for writing data to a CSV file will be to write a header row and loop
+ through the items in a list, outputting one row for
+ each.
+
Here is a simple example where we first make a list of the multiples of 12 and then write a file that looks like this.
+
1,12
+2,24
+3,36
+...
+
+
+n = [0] * 12
+for i in range(1,13):
+ n[i-1] = i *12
+outfile = open("Multiples of 12", "w")
+for j in range(0, len(n)):
+ outfile.write(str(j+1) + ',' + str(n[j]))
+ # +1 to j since the array starts at 0 and we start counting at 1
+ outfile.write('\n')
+outfile.close()
+
+
+
Here is a more complex example, where we a have a list of tuples, each representing one Olympian, a subset of the rows and columns from the file we have been reading from.
+
+
+olympians = [("John Aalberg", 31, "Cross Country Skiing"),
+ ("Minna Maarit Aalto", 30, "Sailing"),
+ ("Win Valdemar Aaltonen", 54, "Art Competitions"),
+ ("Wakako Abe", 18, "Cycling")]
+
+outfile = open("reduced_olympics.csv", "w")
+# output the header row
+outfile.write('Name,Age,Sport')
+outfile.write('\n')
+# output each of the rows:
+for olympian in olympians:
+ row_string = '{},{},{}'.format(olympian[0], olympian[1], olympian[2])
+ outfile.write(row_string)
+ outfile.write('\n')
+outfile.close()
+
+
+
There are a few things worth noting in the code above.
+
First, using .format() makes it really clear what we're doing when we create the variable row_string. We are making a comma separated set of values; the {} curly braces indicated where to substitute in the actual values. The equivalent string concatenation would be very hard to read. An alternative, also clear way to do it would be with the .join method: row_string = ','.join([olympian[0], str(olympian[1]), olympian[2]]).
+
Second, unlike the print statement, remember that the .write() method on a file object does not automatically insert a newline. Instead, we have to explicitly add the character \n at the end of each line.
+
Third, we have to explicitly refer to each of the elements of olympian when building the string to write. Note that just putting .format(olympian) wouldn't work because the interpreter would see only one value (a tuple) when it was expecting three values to try to substitute into the string template. Later in the book we will see that python provides an advanced technique for automatically unpacking the three values from the tuple, with .format(*olympian).
+
As described previously, if one or more columns contain text, and that text could contain commas, we need to do something
+ to distinguish a comma in the text from a comma that is separating different values (cells in the
+ table). If we want to enclose each value in double quotes, it can start to get a little tricky, because we will
+ need to have the double quote character inside the string output. But it is doable. Indeed, one
+ reason Python allows strings to be delimited with either single quotes or double quotes is so
+ that one can be used to delimit the string and the other can be a character in the string. If you get to the point where you need to quote all of the values, we recommend learning to use python's csv module.
+
+
+olympians = [("John Aalberg", 31, "Cross Country Skiing, 15KM"),
+ ("Minna Maarit Aalto", 30, "Sailing"),
+ ("Win Valdemar Aaltonen", 54, "Art Competitions"),
+ ("Wakako Abe", 18, "Cycling")]
+
+outfile = open("reduced_olympics2.csv", "w")
+# output the header row
+outfile.write('"Name","Age","Sport"')
+outfile.write('\n')
+# output each of the rows:
+for olympian in olympians:
+ row_string = '"{}", "{}", "{}"'.format(olympian[0], olympian[1], olympian[2])
+ outfile.write(row_string)
+ outfile.write('\n')
+outfile.close()
+
+
+
diff --git a/pretext/Files/WritingTextFiles.ptx b/pretext/Files/WritingTextFiles.ptx
new file mode 100644
index 00000000..a7c44618
--- /dev/null
+++ b/pretext/Files/WritingTextFiles.ptx
@@ -0,0 +1,70 @@
+
+
+ Writing Text Files
+
One of the most commonly performed data processing tasks is to read data from a file,
+ manipulate it in some way, and then write the resulting data out to a new data file to be used
+ for other purposes later. To accomplish this, the open function discussed above can also be
+ used to create a new file prepared for writing. Note in
+ that the only difference between opening a file for writing and opening a file for reading is
+ the use of the 'w' flag instead of the 'r' flag as the second parameter. When we open
+ a file for writing, a new, empty file with that name is created and made ready to accept our
+ data. If an existing file has the same name, its contents are overwritten. As before, the function returns a reference to the new file object.
+
shows one additional method on file objects that we have not used
+ thus far. The write method allows us to add data to a text file. Recall that text files
+ contain sequences of characters. We usually think of these character sequences as being the
+ lines of the file where each line ends with the newline \n character. Be very careful to
+ notice that the write method takes one parameter, a string. When invoked, the characters of
+ the string will be added to the end of the file. This means that it is the programmer's job to
+ include the newline characters as part of the string if desired.
+
Assume that we have been asked to provide a file consisting of all the squared numbers from 1
+ to 12.
+
First, we will need to open the file. Afterwards, we will iterate through the numbers 1 through
+ 12, and square each one of them. This new number will need to be converted to a string, and
+ then it can be written into the file.
+
The program below solves part of the problem. We first want to make sure that we've written the
+ correct code to calculate the square of each number.
+
+
+for number in range(1, 13):
+ square = number * number
+ print(square)
+
+
+
When we run this program, we see the lines of output on the screen. Once we are satisfied that
+ it is creating the appropriate output, the next step is to add the necessary pieces to produce
+ an output file and write the data lines to it. To start, we need to open a new output file by
+ calling the open function, outfile = open("squared_numbers.txt",'w'), using the 'w'
+ flag. We can choose any file name we like. If the file does not exist, it will be created.
+ However, if the file does exist, it will be reinitialized as empty and you will lose any
+ previous contents.
+
Once the file has been created, we just need to call the write method passing the string
+ that we wish to add to the file. In this case, the string is already being printed so we will
+ just change the print into a call to the write method. However, there is an additional
+ step to take, since the write method can only accept a string as input. We'll need to convert
+ the number to a string. Then, we just need to add one extra character to the string. The
+ newline character needs to be concatenated to the end of the line. The entire line now becomes
+ outfile.write(str(square)+ '\n'). The print statement automatically outputs a newline
+ character after whatever text it outputs, but the write method does not do that automatically.
+ We also need to close the file when we are done.
+
The complete program is shown below.
+
+
As with file reading, for security reasons the runestone interactive textbook environment does not write files to the file system on your local computer. In an activecode window, we simulate writing to a file. The contents of the written file are shown and you can do a subsequent read of the contents of that filename. If you try to overwrite a file that's built in to the page, it may not let you; don't try to get too fancy with our file system simulator!
+
Below, we have printed the first 10 characters to the output window.
+
+
+
+filename = "squared_numbers.txt"
+outfile = open(filename, "w")
+
+for number in range(1, 13):
+ square = number * number
+ outfile.write(str(square) + "\n")
+
+outfile.close()
+
+infile = open(filename, "r")
+print(infile.read()[:10])
+infile.close()
+
+
+
diff --git a/pretext/Files/intro-WorkingwithDataFiles.ptx b/pretext/Files/intro-WorkingwithDataFiles.ptx
new file mode 100644
index 00000000..785562d0
--- /dev/null
+++ b/pretext/Files/intro-WorkingwithDataFiles.ptx
@@ -0,0 +1,104 @@
+
+
+ Introduction: Working with Data Files
+
+
So far, the data we have used in this book have all been either coded right into the program, or have been
+ entered by the user. In real life data reside in files. For example the images we worked with in the image
+ processing unit ultimately live in files on your hard drive. Web pages, and word processing documents, and
+ music are other examples of data that live in files. In this short chapter we will introduce the Python
+ concepts necessary to use data from files in our programs.
+
For our purposes, we will assume that our data files are text files–that is, files filled with characters.
+ The Python programs that you write are stored as text files. We can create these files in any of a number of
+ ways. For example, we could use a text editor to type in and save the data. We could also download the data
+ from a website and then save it in a file. Regardless of how the file is created, Python will allow us to
+ manipulate the contents.
+
In Python, we must open files before we can use them and close them when we are done with them. As
+ you might expect, once a file is opened it becomes a Python object just like all other data.
+ shows the functions and methods that can be used to open and close files.
+
+
+
+
+ Method Name
+
+
+ Use
+
+
+ Explanation
+
+
+
+
+ open
+
+
+ open(filename,'r')
+
+
+ Open a file called filename and use it for reading. This will return a reference to a file object.
+
+
+
+
+ open
+
+
+ open(filename,'w')
+
+
+ Open a file called filename and use it for writing. This will also return a reference to a file object.
+
+
+
+
+ close
+
+
+ filevariable.close()
+
+
+ File use is complete.
+
+
+
+
+
+ Learning Goals
+
+
+
+
To understand the structure of file systems
+
+
+
To understand opening files with different modes
+
+
+
To introduce files as another kind of sequence that one can iterate over
+
+
+
To introduce the read/transform/write pattern
+
+
+
To introduce parallel assignment to two or three variables
+
+
+
+
+
+ Objectives
+
+
+
+
Demonstrate that you can read a single value from each line in a file
+
+
+
Convert the line to the appropriate value
+
+
+
Read a line and convert it into multiple values using split and assignment to multiple variables
To paraphrase the philosophy of the Free Software Foundation, this book is free
+ like free speech, but not necessarily free like free pizza. It came about
+ because of a collaboration that would not have been possible without the GNU
+ Free Documentation License. So we would like to thank the Free Software
+ Foundation for developing this license and, of course, making it available to
+ us.
+
We would also like to thank the more than 100 sharp-eyed and thoughtful readers
+ who have sent us suggestions and corrections over the past few years. In the
+ spirit of free software, we decided to express our gratitude in the form of a
+ contributor list. Unfortunately, this list is not complete, but we are doing
+ our best to keep it up to date. It was also getting too large to include
+ everyone who sends in a typo or two. You have our gratitude, and you have the
+ personal satisfaction of making a book you found useful better for you and
+ everyone else who uses it. New additions to the list for the 2nd edition will
+ be those who have made on-going contributions.
+
If you have a chance to look through the list, you should realize that each
+ person here has spared you and all subsequent readers from the confusion of a
+ technical error or a less-than-transparent explanation, just by sending us a
+ note.
+
Impossible as it may seem after so many corrections, there may still be errors
+ in this book. If you should stumble across one, we hope you will take a minute
+ to contact us. The email address is jeff@elkner.net
+ . Substantial changes made due to your suggestions will add you to the next
+ version of the contributor list (unless you ask to be omitted). Thank you!
+
+ Second Edition
+
+
+
+
An email from Mike MacHenry set me straight on tail recursion. He not only
+ pointed out an error in the presentation, but suggested how to correct it.
+
+
+
It wasn't until 5th Grade student Owen Davies came to me in a Saturday
+ morning Python enrichment class and said he wanted to write the card game,
+ Gin Rummy, in Python that I finally knew what I wanted to use as the case
+ study for the object oriented programming chapters.
+
+
+
A special thanks to pioneering students in Jeff's Python Programming class
+ at GCTAA during the 2009-2010 school year: Safath
+ Ahmed, Howard Batiste, Louis Elkner-Alfaro, and Rachel Hancock. Your
+ continual and thoughtfull feedback led to changes in most of the chapters of
+ the book. You set the standard for the active and engaged learners that will
+ help make the new Governor's Academy what it is to become. Thanks to you
+ this is truly a student tested text.
+
+
+
Thanks in a similar vein to the students in Jeff's Computer Science
+ class at the HB-Woodlawn program during the 2007-2008 school year: James
+ Crowley, Joshua Eddy, Eric Larson, Brian McGrail, and Iliana Vazuka.
+
+
+
Ammar Nabulsi sent in numerous corrections from Chapters 1 and 2.
+
+
+
Aldric Giacomoni pointed out an error in our definition of the Fibonacci
+ sequence in Chapter 5.
+
+
+
Roger Sperberg sent in several spelling corrections and pointed out a twisted
+ piece of logic in Chapter 3.
+
+
+
Adele Goldberg sat down with Jeff at PyCon 2007 and gave him a list of
+ suggestions and corrections from throughout the book.
+
+
+
Ben Bruno sent in corrections for chapters 4, 5, 6, and 7.
+
+
+
Carl LaCombe pointed out that we incorrectly used the term commutative in
+ chapter 6 where symmetric was the correct term.
+
+
+
Alessandro Montanile sent in corrections for errors in the code examples and
+ text in chapters 3, 12, 15, 17, 18, 19, and 20.
+
+
+
Emanuele Rusconi found errors in chapters 4, 8, and 15.
+
+
+
Michael Vogt reported an indentation error in an example in chapter 6, and
+ sent in a suggestion for improving the clarity of the shell vs. script
+ section in chapter 1.
+
+
+
+
+
+ First Edition
+
+
+
+
Lloyd Hugh Allen sent in a correction to Section 8.4.
+
+
+
Yvon Boulianne sent in a correction of a semantic error in Chapter 5.
+
+
+
Fred Bremmer submitted a correction in Section 2.1.
+
+
+
Jonah Cohen wrote the Perl scripts to convert the LaTeX source for this book
+ into beautiful HTML.
+
+
+
Michael Conlon sent in a grammar correction in Chapter 2 and an improvement
+ in style in Chapter 1, and he initiated discussion on the technical aspects
+ of interpreters.
+
+
+
Benoit Girard sent in a correction to a humorous mistake in Section 5.6.
+
+
+
Courtney Gleason and Katherine Smith wrote horsebet.py, which was used as a
+ case study in an earlier version of the book. Their program can now be found
+ on the website.
+
+
+
Lee Harr submitted more corrections than we have room to list here, and
+ indeed he should be listed as one of the principal editors of the text.
+
+
+
James Kaylin is a student using the text. He has submitted numerous
+ corrections.
+
+
+
David Kershaw fixed the broken catTwice function in Section 3.10.
+
+
+
Eddie Lam has sent in numerous corrections to Chapters 1, 2, and 3. He also
+ fixed the Makefile so that it creates an index the first time it is run and
+ helped us set up a versioning scheme.
+
+
+
Man-Yong Lee sent in a correction to the example code in Section 2.4.
+
+
+
David Mayo pointed out that the word unconsciously in Chapter 1 needed to be
+ changed to subconsciously .
+
+
+
Chris McAloon sent in several corrections to Sections 3.9 and 3.10.
+
+
+
Matthew J. Moelter has been a long-time contributor who sent in numerous
+ corrections and suggestions to the book.
+
+
+
Simon Dicon Montford reported a missing function definition and several typos
+ in Chapter 3. He also found errors in the increment function in Chapter 13.
+
+
+
John Ouzts corrected the definition of return value in Chapter 3.
+
+
+
Kevin Parks sent in valuable comments and suggestions as to how to improve
+ the distribution of the book.
+
+
+
David Pool sent in a typo in the glossary of Chapter 1, as well as kind words
+ of encouragement.
+
+
+
Michael Schmitt sent in a correction to the chapter on files and
+ exceptions.
+
+
+
Robin Shaw pointed out an error in Section 13.1, where the printTime function
+ was used in an example without being defined.
+
+
+
Paul Sleigh found an error in Chapter 7 and a bug in Jonah Cohen's Perl
+ script that generates HTML from LaTeX.
+
+
+
Craig T. Snydal is testing the text in a course at Drew University.
+ He has contributed several valuable suggestions and corrections.
+
+
+
Ian Thomas and his students are using the text in a programming course. They
+ are the first ones to test the chapters in the latter half of the book, and
+ they have make numerous corrections and suggestions.
+
+
+
Keith Verheyden sent in a correction in Chapter 3.
+
+
+
Peter Winstanley let us know about a longstanding error in our Latin in
+ Chapter 3.
+
+
+
Chris Wrobel made corrections to the code in the chapter on file I/O and
+ exceptions.
+
+
+
Moshe Zadka has made invaluable contributions to this project. In addition to
+ writing the first draft of the chapter on Dictionaries, he provided continual
+ guidance in the early stages of the book.
+
+
+
Christoph Zwerschke sent several corrections and pedagogic
+ suggestions, and explained the difference between gleich and
+ selbe.
+
+
+
James Mayer sent us a whole slew of spelling and typographical
+ errors, including two in the contributor list.
+
+
+
Hayden McAfee caught a potentially confusing inconsistency between two
+ examples.
+
+
+
Angel Arnal is part of an international team of translators working on the
+ Spanish version of the text. He has also found several errors in the English
+ version.
+
+
+
Tauhidul Hoque and Lex Berezhny created the illustrations in Chapter 1 and
+ improved many of the other illustrations.
+
+
+
Dr. Michele Alzetta caught an error in Chapter 8 and sent some interesting
+ pedagogic comments and suggestions about Fibonacci and Old Maid.
+
+
+
Andy Mitchell caught a typo in Chapter 1 and a broken example in Chapter 2.
+
+
+
Kalin Harvey suggested a clarification in Chapter 7 and caught some typos.
+
+
+
Christopher P. Smith caught several typos and is helping us prepare to update
+ the book for Python 2.2.
+
+
+
David Hutchins caught a typo in the Foreword.
+
+
+
Gregor Lingl is teaching Python at a high school in Vienna, Austria. He is
+ working on a German translation of the book, and he caught a couple of bad
+ errors in Chapter 5.
+
+
+
Julie Peters caught a typo in the Preface.
+
+
+
+
+
diff --git a/pretext/FrontBackMatter/copyright.ptx b/pretext/FrontBackMatter/copyright.ptx
new file mode 100644
index 00000000..72070deb
--- /dev/null
+++ b/pretext/FrontBackMatter/copyright.ptx
@@ -0,0 +1,13 @@
+
+ Copyright Notice
+
+ Copyright (C) Brad Miller, Paul Resnick, Lauren Murphy, Jeffrey Elkner, Peter Wentworth, Allen B. Downey, Chris
+ Meyers, and Dario Mitchell. Permission is granted to copy, distribute
+ and/or modify this document under the terms of the GNU Free Documentation
+ License, Version 1.3 or any later version published by the Free Software
+ Foundation; with Invariant Sections being Forward, Prefaces, and
+ Contributor List, no Front-Cover Texts, and no Back-Cover Texts. A copy of
+ the license is included in the section entitled GNU Free Documentation License.
+
+
+
diff --git a/pretext/FrontBackMatter/fdl-1.3.ptx b/pretext/FrontBackMatter/fdl-1.3.ptx
new file mode 100644
index 00000000..b6283dbd
--- /dev/null
+++ b/pretext/FrontBackMatter/fdl-1.3.ptx
@@ -0,0 +1,384 @@
+
+
+ GNU Free Documentation License
+
Everyone is permitted to copy and distribute verbatim copies of
+ this license document, but changing it is not allowed.
+
+ 0. PREAMBLE
+
The purpose of this License is to make a manual, textbook, or other functional
+ and useful document free in the sense of freedom: to assure everyone the
+ effective freedom to copy and redistribute it, with or without modifying it,
+ either commercially or noncommercially. Secondarily, this License preserves for
+ the author and publisher a way to get credit for their work, while not being
+ considered responsible for modifications made by others.
+
This License is a kind of copyleft, which means that derivative works of the
+ document must themselves be free in the same sense. It complements the GNU
+ General Public License, which is a copyleft license designed for free software.
+
We have designed this License in order to use it for manuals for free software,
+ because free software needs free documentation: a free program should come with
+ manuals providing the same freedoms that the software does. But this License is
+ not limited to software manuals; it can be used for any textual work,
+ regardless of subject matter or whether it is published as a printed book. We
+ recommend this License principally for works whose purpose is instruction or
+ reference.
+
+
+ 1. APPLICABILITY AND DEFINITIONS
+
This License applies to any manual or other work, in any medium, that contains
+ a notice placed by the copyright holder saying it can be distributed under the
+ terms of this License. Such a notice grants a world-wide, royalty-free license,
+ unlimited in duration, to use that work under the conditions stated herein. The
+ Document, below, refers to any such manual or work. Any member of the public
+ is a licensee, and is addressed as you. You accept the license if you copy,
+ modify or distribute the work in a way requiring permission under copyright
+ law.
+
A Modified Version of the Document means any work containing the Document or
+ a portion of it, either copied verbatim, or with modifications and/or
+ translated into another language.
+
A Secondary Section is a named appendix or a front-matter section of the
+ Document that deals exclusively with the relationship of the publishers or
+ authors of the Document to the Document's overall subject (or to related
+ matters) and contains nothing that could fall directly within that overall
+ subject. (Thus, if the Document is in part a textbook of mathematics, a
+ Secondary Section may not explain any mathematics.) The relationship could be a
+ matter of historical connection with the subject or with related matters, or of
+ legal, commercial, philosophical, ethical or political position regarding them.
+
The Invariant Sections are certain Secondary Sections whose titles are
+ designated, as being those of Invariant Sections, in the notice that says that
+ the Document is released under this License. If a section does not fit the
+ above definition of Secondary then it is not allowed to be designated as
+ Invariant. The Document may contain zero Invariant Sections. If the Document
+ does not identify any Invariant Sections then there are none.
+
The Cover Texts are certain short passages of text that are listed, as
+ Front-Cover Texts or Back-Cover Texts, in the notice that says that the
+ Document is released under this License. A Front-Cover Text may be at most 5
+ words, and a Back-Cover Text may be at most 25 words.
+
A Transparent copy of the Document means a machine-readable copy, represented
+ in a format whose specification is available to the general public, that is
+ suitable for revising the document straightforwardly with generic text editors
+ or (for images composed of pixels) generic paint programs or (for drawings)
+ some widely available drawing editor, and that is suitable for input to text
+ formatters or for automatic translation to a variety of formats suitable for
+ input to text formatters. A copy made in an otherwise Transparent file format
+ whose markup, or absence of markup, has been arranged to thwart or discourage
+ subsequent modification by readers is not Transparent. An image format is not
+ Transparent if used for any substantial amount of text. A copy that is not
+ Transparent is called Opaque.
+
Examples of suitable formats for Transparent copies include plain ASCII without
+ markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly
+ available DTD, and standard-conforming simple HTML, PostScript or PDF designed
+ for human modification. Examples of transparent image formats include PNG, XCF
+ and JPG. Opaque formats include proprietary formats that can be read and
+ edited only by proprietary word processors, SGML or XML for which the DTD
+ and/or processing tools are not generally available, and the machine-generated
+ HTML, PostScript or PDF produced by some word processors for output purposes
+ only.
+
The Title Page means, for a printed book, the title page itself, plus such
+ following pages as are needed to hold, legibly, the material this License
+ requires to appear in the title page. For works in formats which do not have
+ any title page as such, Title Page means the text near the most prominent
+ appearance of the work's title, preceding the beginning of the body of the
+ text.
+
The publisher means any person or entity that distributes copies of the
+ Document to the public.
+
A section Entitled XYZ means a named subunit of the Document whose title
+ either is precisely XYZ or contains XYZ in parentheses following text that
+ translates XYZ in another language. (Here XYZ stands for a specific section
+ name mentioned below, such as Acknowledgements, Dedications,
+ Endorsements, or History.) To Preserve the Title of such a section when
+ you modify the Document means that it remains a section Entitled XYZ
+ according to this definition.
+
The Document may include Warranty Disclaimers next to the notice which states
+ that this License applies to the Document. These Warranty Disclaimers are
+ considered to be included by reference in this License, but only as regards
+ disclaiming warranties: any other implication that these Warranty Disclaimers
+ may have is void and has no effect on the meaning of this License.
+
+
+ 2. VERBATIM COPYING
+
You may copy and distribute the Document in any medium, either commercially or
+ noncommercially, provided that this License, the copyright notices, and the
+ license notice saying this License applies to the Document are reproduced in
+ all copies, and that you add no other conditions whatsoever to those of this
+ License. You may not use technical measures to obstruct or control the reading
+ or further copying of the copies you make or distribute. However, you may
+ accept compensation in exchange for copies. If you distribute a large enough
+ number of copies you must also follow the conditions in section 3.
+
You may also lend copies, under the same conditions stated above, and you may
+ publicly display copies.
+
+
+ 3. COPYING IN QUANTITY
+
If you publish printed copies (or copies in media that commonly have printed
+ covers) of the Document, numbering more than 100, and the Document's license
+ notice requires Cover Texts, you must enclose the copies in covers that carry,
+ clearly and legibly, all these Cover Texts: Front-Cover Texts on the front
+ cover, and Back-Cover Texts on the back cover. Both covers must also clearly
+ and legibly identify you as the publisher of these copies. The front cover must
+ present the full title with all words of the title equally prominent and
+ visible. You may add other material on the covers in addition. Copying with
+ changes limited to the covers, as long as they preserve the title of the
+ Document and satisfy these conditions, can be treated as verbatim copying in
+ other respects.
+
If the required texts for either cover are too voluminous to fit legibly, you
+ should put the first ones listed (as many as fit reasonably) on the actual
+ cover, and continue the rest onto adjacent pages.
+
If you publish or distribute Opaque copies of the Document numbering more than
+ 100, you must either include a machine-readable Transparent copy along with
+ each Opaque copy, or state in or with each Opaque copy a computer-network
+ location from which the general network-using public has access to download
+ using public-standard network protocols a complete Transparent copy of the
+ Document, free of added material. If you use the latter option, you must take
+ reasonably prudent steps, when you begin distribution of Opaque copies in
+ quantity, to ensure that this Transparent copy will remain thus accessible at
+ the stated location until at least one year after the last time you distribute
+ an Opaque copy (directly or through your agents or retailers) of that edition
+ to the public.
+
It is requested, but not required, that you contact the authors of the Document
+ well before redistributing any large number of copies, to give them a chance to
+ provide you with an updated version of the Document.
+
+
+ 4. MODIFICATIONS
+
You may copy and distribute a Modified Version of the Document under the
+ conditions of sections 2 and 3 above, provided that you release the Modified
+ Version under precisely this License, with the Modified Version filling the
+ role of the Document, thus licensing distribution and modification of the
+ Modified Version to whoever possesses a copy of it. In addition, you must do
+ these things in the Modified Version:
+
+
+
+
A. Use in the Title Page (and on the covers, if any) a title distinct from
+ that of the Document, and from those of previous versions (which should, if
+ there were any, be listed in the History section of the Document). You may
+ use the same title as a previous version if the original publisher of that
+ version gives permission.
+
+
+
B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified Version,
+ together with at least five of the principal authors of the Document (all of
+ its principal authors, if it has fewer than five), unless they release you
+ from this requirement.
+
+
+
C. State on the Title page the name of the publisher of the Modified Version,
+ as the publisher.
+
+
+
D. Preserve all the copyright notices of the Document.
+
+
+
E. Add an appropriate copyright notice for your modifications adjacent to the
+ other copyright notices.
+
+
+
F. Include, immediately after the copyright notices, a license notice giving
+ the public permission to use the Modified Version under the terms of this
+ License, in the form shown in the Addendum below.
+
+
+
G. Preserve in that license notice the full lists of Invariant Sections and
+ required Cover Texts given in the Document's license notice.
+
+
+
H. Include an unaltered copy of this License.
+
+
+
I. Preserve the section Entitled History, Preserve its Title, and add to it
+ an item stating at least the title, year, new authors, and publisher of the
+ Modified Version as given on the Title Page. If there is no section Entitled
+ History in the Document, create one stating the title, year, authors, and
+ publisher of the Document as given on its Title Page, then add an item
+ describing the Modified Version as stated in the previous sentence.
+
+
+
J. Preserve the network location, if any, given in the Document for public
+ access to a Transparent copy of the Document, and likewise the network
+ locations given in the Document for previous versions it was based on. These
+ may be placed in the History section. You may omit a network location for a
+ work that was published at least four years before the Document itself, or if
+ the original publisher of the version it refers to gives permission.
+
+
+
K. For any section Entitled Acknowledgements or Dedications, Preserve the
+ Title of the section, and preserve in the section all the substance and tone
+ of each of the contributor acknowledgements and/or dedications given therein.
+
+
+
L. Preserve all the Invariant Sections of the Document, unaltered in their
+ text and in their titles. Section numbers or the equivalent are not
+ considered part of the section titles.
+
+
+
M. Delete any section Entitled Endorsements. Such a section may not be
+ included in the Modified Version.
+
+
+
N. Do not retitle any existing section to be Entitled Endorsements or to
+ conflict in title with any Invariant Section.
+
+
+
O. Preserve any Warranty Disclaimers.
+
+
+
+
If the Modified Version includes new front-matter sections or appendices that
+ qualify as Secondary Sections and contain no material copied from the Document,
+ you may at your option designate some or all of these sections as invariant. To
+ do this, add their titles to the list of Invariant Sections in the Modified
+ Version's license notice. These titles must be distinct from any other section
+ titles.
+
You may add a section Entitled Endorsements, provided it contains nothing but
+ endorsements of your Modified Version by various parties—for example,
+ statements of peer review or that the text has been approved by an organization
+ as the authoritative definition of a standard.
+
You may add a passage of up to five words as a Front-Cover Text, and a passage
+ of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts
+ in the Modified Version. Only one passage of Front-Cover Text and one of
+ Back-Cover Text may be added by (or through arrangements made by) any one
+ entity. If the Document already includes a cover text for the same cover,
+ previously added by you or by arrangement made by the same entity you are
+ acting on behalf of, you may not add another; but you may replace the old one,
+ on explicit permission from the previous publisher that added the old one.
+
The author(s) and publisher(s) of the Document do not by this License give
+ permission to use their names for publicity for or to assert or imply
+ endorsement of any Modified Version.
+
+
+ 5. COMBINING DOCUMENTS
+
You may combine the Document with other documents released under this License,
+ under the terms defined in section 4 above for modified versions, provided that
+ you include in the combination all of the Invariant Sections of all of the
+ original documents, unmodified, and list them all as Invariant Sections of your
+ combined work in its license notice, and that you preserve all their Warranty
+ Disclaimers.
+
The combined work need only contain one copy of this License, and multiple
+ identical Invariant Sections may be replaced with a single copy. If there are
+ multiple Invariant Sections with the same name but different contents, make the
+ title of each such section unique by adding at the end of it, in parentheses,
+ the name of the original author or publisher of that section if known, or else
+ a unique number. Make the same adjustment to the section titles in the list of
+ Invariant Sections in the license notice of the combined work.
+
In the combination, you must combine any sections Entitled History in the
+ various original documents, forming one section Entitled History; likewise
+ combine any sections Entitled Acknowledgements, and any sections Entitled
+ Dedications. You must delete all sections Entitled Endorsements.
+
+
+ 6. COLLECTIONS OF DOCUMENTS
+
You may make a collection consisting of the Document and other documents
+ released under this License, and replace the individual copies of this License
+ in the various documents with a single copy that is included in the collection,
+ provided that you follow the rules of this License for verbatim copying of each
+ of the documents in all other respects.
+
You may extract a single document from such a collection, and distribute it
+ individually under this License, provided you insert a copy of this License
+ into the extracted document, and follow this License in all other respects
+ regarding verbatim copying of that document.
+
+
+ 7. AGGREGATION WITH INDEPENDENT WORKS
+
A compilation of the Document or its derivatives with other separate and
+ independent documents or works, in or on a volume of a storage or distribution
+ medium, is called an aggregate if the copyright resulting from the
+ compilation is not used to limit the legal rights of the compilation's users
+ beyond what the individual works permit. When the Document is included in an
+ aggregate, this License does not apply to the other works in the aggregate
+ which are not themselves derivative works of the Document.
+
If the Cover Text requirement of section 3 is applicable to these copies of the
+ Document, then if the Document is less than one half of the entire aggregate,
+ the Document's Cover Texts may be placed on covers that bracket the Document
+ within the aggregate, or the electronic equivalent of covers if the Document is
+ in electronic form. Otherwise they must appear on printed covers that bracket
+ the whole aggregate.
+
+
+ 8. TRANSLATION
+
Translation is considered a kind of modification, so you may distribute
+ translations of the Document under the terms of section 4. Replacing Invariant
+ Sections with translations requires special permission from their copyright
+ holders, but you may include translations of some or all Invariant Sections in
+ addition to the original versions of these Invariant Sections. You may include
+ a translation of this License, and all the license notices in the Document, and
+ any Warranty Disclaimers, provided that you also include the original English
+ version of this License and the original versions of those notices and
+ disclaimers. In case of a disagreement between the translation and the original
+ version of this License or a notice or disclaimer, the original version will
+ prevail.
+
If a section in the Document is Entitled Acknowledgements, Dedications, or
+ History, the requirement (section 4) to Preserve its Title (section 1) will
+ typically require changing the actual title.
+
+
+ 9. TERMINATION
+
You may not copy, modify, sublicense, or distribute the Document except as
+ expressly provided under this License. Any attempt otherwise to copy, modify,
+ sublicense, or distribute it is void, and will automatically terminate your
+ rights under this License.
+
However, if you cease all violation of this License, then your license from a
+ particular copyright holder is reinstated (a) provisionally, unless and until
+ the copyright holder explicitly and finally terminates your license, and (b)
+ permanently, if the copyright holder fails to notify you of the violation by
+ some reasonable means prior to 60 days after the cessation.
+
Moreover, your license from a particular copyright holder is reinstated
+ permanently if the copyright holder notifies you of the violation by some
+ reasonable means, this is the first time you have received notice of violation
+ of this License (for any work) from that copyright holder, and you cure the
+ violation prior to 30 days after your receipt of the notice.
+
Termination of your rights under this section does not terminate the licenses
+ of parties who have received copies or rights from you under this License. If
+ your rights have been terminated and not permanently reinstated, receipt of a
+ copy of some or all of the same material does not give you any rights to use
+ it.
+
+
+ 10. FUTURE REVISIONS OF THIS LICENSE
+
The Free Software Foundation may publish new, revised versions of the GNU Free
+ Documentation License from time to time. Such new versions will be similar in
+ spirit to the present version, but may differ in detail to address new problems
+ or concerns. See http://www.gnu.org/copyleft/.
+
Each version of the License is given a distinguishing version number. If the
+ Document specifies that a particular numbered version of this License or any
+ later version applies to it, you have the option of following the terms and
+ conditions either of that specified version or of any later version that has
+ been published (not as a draft) by the Free Software Foundation. If the
+ Document does not specify a version number of this License, you may choose any
+ version ever published (not as a draft) by the Free Software Foundation. If the
+ Document specifies that a proxy can decide which future versions of this
+ License can be used, that proxy's public statement of acceptance of a version
+ permanently authorizes you to choose that version for the Document.
+
+
+ 11. RELICENSING
+
Massive Multiauthor Collaboration Site (or MMC Site) means any World Wide
+ Web server that publishes copyrightable works and also provides prominent
+ facilities for anybody to edit those works. A public wiki that anybody can edit
+ is an example of such a server. A Massive Multiauthor Collaboration (or
+ MMC) contained in the site means any set of copyrightable works thus
+ published on the MMC site.
+
CC-BY-SA means the Creative Commons Attribution-Share Alike 3.0 license
+ published by Creative Commons Corporation, a not-for-profit corporation with a
+ principal place of business in San Francisco, California, as well as future
+ copyleft versions of that license published by that same organization.
+
Incorporate means to publish or republish a Document, in whole or in part, as
+ part of another Document.
+
An MMC is eligible for relicensing if it is licensed under this License, and
+ if all works that were first published under this License somewhere other than
+ this MMC, and subsequently incorporated in whole or in part into the MMC, (1)
+ had no cover texts or invariant sections, and (2) were thus incorporated prior
+ to November 1, 2008.
+
The operator of an MMC Site may republish an MMC contained in the site under
+ CC-BY-SA on the same site at any time before August 1, 2009, provided the MMC
+ is eligible for relicensing.
As an educator, researcher, and book author, I am delighted to see the
+ completion of this book. Python is a fun and extremely easy-to-use programming
+ language that has steadily gained in popularity over the last few years.
+ Developed over ten years ago by Guido van Rossum, Python's simple syntax and
+ overall feel is largely derived from ABC, a teaching language that was
+ developed in the 1980's. However, Python was also created to solve real
+ problems and it borrows a wide variety of features from programming languages
+ such as C++, Java, Modula-3, and Scheme. Because of this, one of Python's most
+ remarkable features is its broad appeal to professional software developers,
+ scientists, researchers, artists, and educators.
+
Despite Python's appeal to many different communities, you may still wonder why
+ Python? or why teach programming with Python? Answering these questions is no
+ simple task—especially when popular opinion is on the side of more
+ masochistic alternatives such as C++ and Java. However, I think the most
+ direct answer is that programming in Python is simply a lot of fun and more
+ productive.
+
When I teach computer science courses, I want to cover important concepts in
+ addition to making the material interesting and engaging to students.
+ Unfortunately, there is a tendency for introductory programming courses to
+ focus far too much attention on mathematical abstraction and for students to
+ become frustrated with annoying problems related to low-level details of
+ syntax, compilation, and the enforcement of seemingly arcane rules. Although
+ such abstraction and formalism is important to professional software engineers
+ and students who plan to continue their study of computer science, taking such
+ an approach in an introductory course mostly succeeds in making computer
+ science boring. When I teach a course, I don't want to have a room of
+ uninspired students. I would much rather see them trying to solve interesting
+ problems by exploring different ideas, taking unconventional approaches,
+ breaking the rules, and learning from their mistakes. In doing so, I don't want
+ to waste half of the semester trying to sort out obscure syntax problems,
+ unintelligible compiler error messages, or the several hundred ways that a
+ program might generate a general protection fault.
+
One of the reasons why I like Python is that it provides a really nice balance
+ between the practical and the conceptual. Since Python is interpreted,
+ beginners can pick up the language and start doing neat things almost
+ immediately without getting lost in the problems of compilation and linking.
+ Furthermore, Python comes with a large library of modules that can be used to
+ do all sorts of tasks ranging from web-programming to graphics. Having such a
+ practical focus is a great way to engage students and it allows them to
+ complete significant projects. However, Python can also serve as an excellent
+ foundation for introducing important computer science concepts. Since Python
+ fully supports procedures and classes, students can be gradually introduced to
+ topics such as procedural abstraction, data structures, and object-oriented
+ programming — all of which are applicable to later courses on Java or C++.
+ Python even borrows a number of features from functional programming languages
+ and can be used to introduce concepts that would be covered in more detail in
+ courses on Scheme and Lisp.
+
In reading Jeffrey's preface, I am struck by his comments that Python allowed
+ him to see a higher level of success and a lower level of frustration and that
+ he was able to move faster with better results. Although these comments refer
+ to his introductory course, I sometimes use Python for these exact same reasons
+ in advanced graduate level computer science courses at the University of
+ Chicago. In these courses, I am constantly faced with the daunting task of
+ covering a lot of difficult course material in a blistering nine week quarter.
+ Although it is certainly possible for me to inflict a lot of pain and suffering
+ by using a language like C++, I have often found this approach to be
+ counterproductive—especially when the course is about a topic unrelated to
+ just programming. I find that using Python allows me to better focus on the
+ actual topic at hand while allowing students to complete substantial class
+ projects.
+
Although Python is still a young and evolving language, I believe that it has a
+ bright future in education. This book is an important step in that direction.
+ David Beazley University of Chicago Author of the Python Essential Reference
Today, it is hard to imagine life without computers. We carry them in our pockets, read with them on the couch, and use them on our desk at work. Computing is the Swiss Army knife of the 21st century: a convenient tool that can be used to solve a wide array of problems. The original computers were large and limited in function: nothing more than very basic calculators. The military was the first to find some of the many applications of this technology: made small, computers could be used to improve the precision of bombing; made large and (relatively) powerful, they could be used to break military codes. As computers continued to grow in power and drop in price, many more peaceful applications were found:
+
+
+
+
Scientists in both natural and social sciences can use them to analyze vast quantities of data. For example, astronomers are able to identify distant stars in large images of the sky and sociologists are able to analyze the behavior of all the citizens of a country.
+
+
+
Artists and students of literature can use them to analyze great works, to find patterns in them that the human eye or ear had missed and use them to algorithmically generate some new works (examples from literature[1, 2] and music).
+
+
+
Business people can use them to instantaneously keep track of the health of their business across the globe and analyze past behaviors to better prepare for the future.
+
+
+
Doctors and medical researchers can use them to track the effect of new medicines across vast populations of patients.
+
+
+
Software engineers can use them to create apps and websites that attract billions of users.
+
+
+
+
Today, the amount of computing power and the volume of data available to us is staggering. The field of machine learning is just beginning to harness this power and data and is transforming the field of computer science. At one level, machine learning, allows programs to do for themselves what programmers have had to do for years, which is to recognize complex patterns in the data. Machine learning algorithms are now routinely doing better than humans in tasks like reading X-rays, or recommending a restaurant to try.
+
This course is about exploring the use of computer programs to solve these kinds of problems, whatever your area of interest and major might be.
+
To do so, this book will teach you how to understand and create computer programs in Python.
+ Python is a programming language that is in wide use both for professional software development and in education. In the professional world, it is used for anything from creating small scripts that rename files in a folder, to developing full web applications such as YouTube, SnapChat and DropBox. In the world of education, Python is a popular language because of its relatively simple syntax, its robust set of built-in functionality and its beginner-friendly error messages. For all these reasons and more, Python has become widespread in the world of data science and machine learning as well; in fact, it is the principal language of TensorFlow, Google's open-source machine learning library.
+
At the beginning of each chapter, we will outline for you the learning goals and objectives that should be accomplished once you have gone through the chapter. And, throughout the textbook, you will find projects that connect what you have learned to solving real world problems.
+
Understanding computer programs requires orderly, logical, mechanistic thinking. Programs are just sequences of actions to perform; when executed, they transform input data into output data:
+
+
+
+
numbers turn into other numbers (e.g. basic math operations like sin or log)
+
+
+
images turn into words or numbers (e.g. a cell phone photo of a diseased-looking leaf of a plant becomes the name of the disease affecting the plant or the number of whiteflies infesting it)
+
+
+
images turn into other images (e.g. filters in Instagram)
+
+
+
words turn into tables (e.g. reporting the number of times each character speaks in Shakespeare's works)
+
+
+
numbers turn into 3D models of great works of art (e.g. Stanford's Michelangelo project)
+
+
+
+
+ Get in the Learning Zone
+
Programs execute very reliably, and very quickly, but not creatively. Computers do what you tell them to do, not what you mean for them to do. Thus, understanding computer code involves a lot of mental simulation of what will actually happen, not what you wish would happen. This can be frustrating at times but it's something that you will get better at with practice. As you go through the activities in this class, some will be easy for you to complete, i.e. in your comfort zone. Being in your comfort zone is nice but it probably means you are not learning very much. More challenging activities will encourage you to think through the problems and to refer back to the reading to allow you to enter your learning zone. This is the zone that you strive to be in for as much of the course as possible. Beyond your learning zone lies the panic zone where the problem overwhelms your ability to grow and learn. If you find yourself in the panic zone, please seek help from your instructor and/or classmates: none of the activities in this book are intended to stump you. As you understand how to solve some simpler problems, you will develop the ability to join these solutions together to solve increasingly challenging problems with real-world applications.
+
+
In addition to mechanistic thinking, writing computer programs requires creative problem solving or the ability to identify a complex situation, think creatively about possible solutions, and express those solutions clearly and accurately. As it turns out, the process of learning to program is an excellent opportunity to practice problem solving skills you can use in other parts of your life. We sincerely believe that the combination of knowledge of Python, creative problem solving skills and expressing those solutions in such a way that a computer can effectively carry them out (computational thinking) will make you more productive and efficient in tackling your work in future classes,whether in Computer Science, Business, Psychology or History. And it may even pique your interest in becoming a Data Scientist or a Computer Scientist.
+
+
diff --git a/pretext/Functions/Afunctionthataccumulates.ptx b/pretext/Functions/Afunctionthataccumulates.ptx
new file mode 100644
index 00000000..9b72fd3b
--- /dev/null
+++ b/pretext/Functions/Afunctionthataccumulates.ptx
@@ -0,0 +1,96 @@
+
+
+ A function that accumulates
+
We have used the len function a lot already. If it weren't part of python, our lives as programmers would have been
+ a lot harder.
+
Well, actually, not that much harder. Now that we know how to define functions, we could define len ourselves if it
+ did not exist. Previously, we have used the accumlator pattern to count the number of lines in a file. Let's use that
+ same idea and just wrap it in a function definition. We'll call it mylen to distinguish it from the real len
+ which already exists. We actually could call it len, but that wouldn't be a very good idea, because it would replace
+ the original len function, and our implementation may not be a very good one.
+
+
+def mylen(seq):
+ c = 0 # initialize count variable to 0
+ for _ in seq:
+ c = c + 1 # increment the counter for each item in seq
+ return c
+
+print(mylen("hello"))
+print(mylen([1, 2, 7]))
+
+
+
+
+
Rearrange the code statements to match the activecode window above. (This is an exercise in noticing where the indenting and outdenting happens, and where the return statement goes.)
+
+
+
+ def mylen(x):
+
+
+ c = 0 # initialize count variable to 0
+
+
+ for y in x:
+
+
+ c = c + 1 # increment the counter for each item in x
+
+
+ return c
+
+
+ print(mylen("hello"))
+ print(mylen([1, 2, 7]))
+
+
+
+
+ Check your Understanding
+
+
+
+
1. Write a function named total that takes a list of integers as input, and returns the total value of all those integers added together.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(total([1, 2, 3, 4, 5]), 15, "Testing the total function on input [1, 2, 3, 4, 5].")
+ self.assertEqual(total([0, 0, 0, 0]), 0, "Testing the total function on input [0, 0, 0, 0].")
+ self.assertEqual(total([]), 0, "Testing the total function on input [].")
+ self.assertEqual(total([2]), 2, "Testing the total function on input [2].")
+
+myTests().main()
+
+
+
+
+
+
2. Write a function called count that takes a list of numbers as input and returns a count of the number of elements in the list.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(count([]), 0, "Testing the function count with input []")
+ self.assertEqual(count([1, 5, 9, -2, 9, 23]), 6, "Testing the function count with input [1, 5, 9, -2, 9, 23]")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Functions/ChapterAssessment.ptx b/pretext/Functions/ChapterAssessment.ptx
new file mode 100644
index 00000000..d8b75341
--- /dev/null
+++ b/pretext/Functions/ChapterAssessment.ptx
@@ -0,0 +1,137 @@
+
+
+ Chapter Assessment
+
+
+
Write a function called int_return that takes an integer as input and returns the same integer.
Write a function, length, that takes in a list as the input. If the length of the list is greater than or equal to 5, return Longer than 5. If the length is less than 5, return Less than 5.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testFive(self):
+ self.assertEqual(length([]), "Less than 5", "Tests that length([]) returns 'Less than 5'")
+ self.assertEqual(length([2, 2]), "Less than 5", "Tests that length([2, 2]) returns 'Less than 5'")
+ self.assertEqual(length([4, 4, 4, 3, 5, 6, 7, 8, 9]), "Longer than 5", "Tests that length([4, 4, 4, 3, 5, 6, 7, 8, 9]) returns 'Longer than 5'")
+ self.assertEqual(length([1, 1, 1, 1, 1]), "Longer than 5", "Tests that length([1, 1, 1, 1, 1]) returns 'Longer than 5'")
+
+myTests().main()
+
+
+
+
+
+
You will need to write two functions for this problem. The first function, divide that takes in any number and returns that same number divided by 2. The second function called sum should take any number, divide it by 2, and add 6. It should return this new number. You should call the divide function within the sum function. Do not worry about decimals.
In general, when you see a function definition you will try figure out what the function does, but,
+ unless you are writing the function, you won't care how it does it.
+
For example, here is a summary of some functions we have seen already.
+
+
+
+
input takes one parameter, a string. It is displayed to the user.
+ Whatever the user types is returned, as a string.
+
+
+
int takes one parameter. It can be of any type that can be converted
+ into an integer, such as a floating point number or a string whose characters
+ are all digits.
+
+
+
+
Sometimes, you will be presented with a function definition whose operation is not so neatly summarized
+ as above. Sometimes you will need to look at the code, either the function definition or code that
+ invokes the function, in order to figure out what it does.
+
To build your understanding of any function, you should aim to answer the following questions:
+
+
+
+
How many parameters does it have?
+
+
+
What is the type of values that will be passed when the function is
+ invoked?
+
+
+
What is the type of the return value that the function produces when it
+ executes?
+
+
+
+
If you try to make use of functions, ones you write or that others write, without being able to answer
+ these questions, you will find that your debugging sessions are long and painful.
+
The first question is always easy to answer. Look at the line with the function definition, look inside
+ the parentheses, and count how many variable names there are.
+
The second and third questions are not always so easy to answer. In Python, unlike some other programming
+ languages, variables are not declared to have fixed types, and the same holds true for the variable names
+ that appear as formal parameters of functions. You have to figure it out from context.
+
To figure out the types of values that a function expects to receive as parameters, you can look at the
+ function invocations or you can look at the operations that are performed on the parameters inside the function.
+
Here are some clues that can help you determine the type of object associated with any variable, including a
+ function parameter. If you see…
+
+
+
+
len(x), then x must be a string or a list. (Actually, it can also be a
+ dictionary, in which case it is equivalent to the expression
+ len(x.keys()). Later in the course, we will also see some other sequence
+ types that it could be). x can't be a number or a Boolean.
+
+
+
x - y, x and y must be numbers (integer or float)
+
+
+
x + y, x and y must both be numbers, both be strings, or both be lists
+
+
+
x[3], x must be a string or a list containing at least four items, or x
+ must be a dictionary that includes 3 as a key.
+
+
+
x['3'], x must be a dictionary, with ‘3' as a key.
+
+
+
x[y:z], x must be a sequence (string or list), and y and z must be
+ integers
+
+
+
x and y, x and y must be Boolean
+
+
+
for x in y, y must be a sequence (string or list) or a dictionary (in which case it's really the dictionary's keys); x must be a character
+ if y is a string; if y is a list, x could be of any type.
+
+
+
+
+ Check your understanding: decode this function definition
+
+
+
+
How many parameters does function cyu3 take?
+
+
+def cyu3(x, y, z):
+ if x - y > 0:
+ return y -2
+ else:
+ z.append(y)
+ return x + 3
+
+
+
+
+
+
+
0
+
+
+
Count the number of variable names inside the parenetheses on line 1.
+
+
+
+
+
1
+
+
+
Count the number of variable names inside the parenetheses on line 1.
+
+
+
+
+
2
+
+
+
Count the number of variable names inside the parenetheses on line 1.
+
+
+
+
+
3
+
+
+
x, y, and z.
+
+
+
+
+
Can't tell
+
+
+
You can tell by looking inside the parentheses on line 1. Each variable name is separated by a comma.
+
+
+
+
+
+
+
What are the possible types of variables x and y?
+
+
+def cyu3(x, y, z):
+ if x - y > 0:
+ return y -2
+ else:
+ z.append(y)
+ return x + 3
+
+
+
+
+
+
+
integer
+
+
+
x - y, y-2, and x+3 can all be performed on integers.
+
+
+
+
+
float
+
+
+
x - y, y-2, and x+3 can all be performed on floats.
+
+
+
+
+
list
+
+
+
x - y, y-2, and x+3 can't be performed on lists.
+
+
+
+
+
string
+
+
+
x - y and y-2 can't be performed on strings.
+
+
+
+
+
Can't tell
+
+
+
You can tell from some of the operations that are performed on them.
+
+
+
+
+
+
+
What are the possible types of variable z?
+
+
+def cyu3(x, y, z):
+ if x - y > 0:
+ return y -2
+ else:
+ z.append(y)
+ return x + 3
+
+
+
+
+
+
+
integer
+
+
+
append can't be performed on integers.
+
+
+
+
+
float
+
+
+
append can't be performed on floats.
+
+
+
+
+
list
+
+
+
append can be performed on lists.
+
+
+
+
+
string
+
+
+
append can't be performed on strings.
+
+
+
+
+
Can't tell
+
+
+
You can tell from some of the operations that are performed on it.
+
+
+
+
+
+
+
What are the possible types of the return value from cyu3?
+
+
+def cyu3(x, y, z):
+ if x - y > 0:
+ return y -2
+ else:
+ z.append(y)
+ return x + 3
+
+
+
+
+
+
+
integer
+
+
+
y-2 or x+3 could produce an integer.
+
+
+
+
+
float
+
+
+
y-2 or x+3 could produce a float.
+
+
+
+
+
list
+
+
+
y-2 or x+3 can't produce a list.
+
+
+
+
+
string
+
+
+
neither y-2 or x+3 could produce a string.
+
+
+
+
+
Can't tell
+
+
+
You can tell from the expressions that follow the word return.
When you are working with functions it is really important to know the order in which statements are executed. This is
+ called the flow of execution and we've already talked about it a number of times in this chapter.
+
Execution always begins at the first statement of the program. Statements are executed one at a time, in order, from
+ top to bottom. Function definitions do not alter the flow of execution of the program, but remember that statements
+ inside the function are not executed until the function is called. Function calls are like a detour in the flow of
+ execution. Instead of going to the next statement, the flow jumps to the first line of the called function, executes
+ all the statements there, and then comes back to pick up where it left off.
+
That sounds simple enough, until you remember that one function can call another. While in the middle of one function,
+ the program might have to execute the statements in another function. But while executing that new function, the
+ program might have to execute yet another function!
+
Fortunately, the Python interperter is adept at keeping track of where it is, so each time a function completes, the
+ program picks up where it left off in the function that called it. When it gets to the end of the program, it terminates.
+
What does all that mean for us when we try to understand a program? Don't read from top to bottom. Instead, follow the
+ flow of execution. This means that you will read the def statements as you are scanning from top to bottom, but you
+ should skip the body of the function until you reach a point where that function is called.
+
+ Check your understanding
+
+
+
+
Consider the following Python code. Note that line numbers are included on the left.
+
+
+def pow(b, p):
+ y = b ** p
+ return y
+
+def square(x):
+ a = pow(x, 2)
+ return a
+
+n = 5
+result = square(n)
+print(result)
+
+
+
What does this function print?
+
+
+
+
+
25
+
+
+
The function square returns the square of its input (via a call to pow).
+
+
+
+
+
5
+
+
+
What is printed is the output of the square function. 5 is the input to the square function.
+
+
+
+
+
125
+
+
+
Notice that pow is called from within square with a base (b) of 5 and a power (p) of two.
+
+
+
+
+
32
+
+
+
Notice that pow is called from within square with a base (b) of 5 and a power (p) of two.
+
+
+
+
+
diff --git a/pretext/Functions/FunctionDefinitions.ptx b/pretext/Functions/FunctionDefinitions.ptx
new file mode 100644
index 00000000..bc8ad637
--- /dev/null
+++ b/pretext/Functions/FunctionDefinitions.ptx
@@ -0,0 +1,85 @@
+
+
+ Function Definition
+
The syntax for creating a named function, a function definition, is:
+
+
+def name( parameters ):
+ statements
+
+
+
You can make up any names you want for the functions you create, except that you can't use a name that is a Python
+ keyword, and the names must follow the rules for legal identifiers that were given previously. The parameters specify
+ what information, if any, you have to provide in order to use the new function. Another way to say this is that the
+ parameters specify what the function needs to do its work.
+
There can be any number of statements inside the function, but they have to be indented from the def. In the
+ examples in this book, we will use the standard indentation of four spaces. Function definitions are the third of
+ several compound statements we will see, all of which have the same pattern:
+
+
+
+
A header line which begins with a keyword and ends with a colon.
+
+
+
A body consisting of one or more Python statements, each
+ indented the same amount – 4 spaces is the Python standard – from
+ the header line.
+
+
+
+
We've already seen the for statement which has the same structure, with an indented block of code, and the
+ if, elif, and else statements that do so as well.
+
In a function definition, the keyword in the header is def, which is followed by the name of the function and
+ some parameter names enclosed in parentheses. The parameter list may be empty, or it may contain any number of
+ parameters separated from one another by commas. In either case, the parentheses are required.
+
We will come back to the parameters in a little while, but first let's see what happens when a function is executed,
+ using a function without any parameters to illustrate.
+
Here's the definition of a simple function, hello.
+
+
+def hello():
+ """This function says hello and greets you"""
+ print("Hello")
+ print("Glad to meet you")
+
+
+
+ docstrings
+
If the first thing after the function header is a string (some tools insist that
+ it must be a triple-quoted string), it is called a docstring
+ and gets special treatment in Python and in some of the programming tools.
+
Another way to retrieve this information is to use the interactive
+ interpreter, and enter the expression <function_name>.__doc__, which will retrieve the
+ docstring for the function. So the string you write as documentation at the start of a function is
+ retrievable by python tools at runtime. This is different from comments in your code,
+ which are completely eliminated when the program is parsed.
+
By convention, Python programmers use docstrings for the key documentation of
+ their functions.
+
+
We can apply functions to the turtle drawings we've done in the past as well.
+
+
+import turtle
+
+def drawSquare(t, sz):
+ """Make turtle t draw a square of with side sz."""
+
+ for i in range(4):
+ t.forward(sz)
+ t.left(90)
+
+
+wn = turtle.Screen() # Set up the window and its attributes
+wn.bgcolor("lightgreen")
+
+alex = turtle.Turtle() # create alex
+drawSquare(alex, 50) # Call the function to draw the square passing the actual turtle and the actual side size
+
+wn.exitonclick()
+
+
+
This function is named drawSquare. It has two parameters — one to tell the function which turtle to move around
+ and the other to tell it the size of the square we want drawn. In the function definition they are called t and
+ sz respectively. Make sure you know where the body of the function ends — it depends on the indentation and the
+ blank lines don't count for this purpose!
+
diff --git a/pretext/Functions/FunctionInvocation.ptx b/pretext/Functions/FunctionInvocation.ptx
new file mode 100644
index 00000000..e811350e
--- /dev/null
+++ b/pretext/Functions/FunctionInvocation.ptx
@@ -0,0 +1,229 @@
+
+
+ Function Invocation
+
Defining a new function does not make the function run.
+ To execute the function, we need a function call. This is also known as a function invocation.
+
+
This section is a review of something we learned in the beginning of the textbook.
+
+
The way to invoke a function is to refer to it by name, followed by parentheses. Since there are no parameters for
+ the function hello, we won't need to put anything inside the parentheses when we call it. Once we've defined a
+ function, we can call it as often as we like and its statements will be executed each time we call it.
+
+
+def hello():
+ print("Hello")
+ print("Glad to meet you")
+
+print(type(hello))
+print(type("hello"))
+
+hello()
+print("Hey, that just printed two lines with one line of code!")
+hello() # do it again, just because we can...
+
+
+
Let's take a closer look at what happens when you define a function and when you execute the function.
+ Try stepping through the code above.
+
First, note that in Step 1, when it executes line 1, it does not execute lines 2 and 3. Instead, as you can see in
+ blue Global variables area, it creates a variable named hello whose value is a python function object. In the
+ diagram that object is labeled hello() with a notation above it that it is a function.
+
At Step 2, the next line of code to execute is line 5. Just to emphasize that hello is a variable like any other, and
+ that functions are python objects like any other, just of a particular type, line 5 prints out the type of the object
+ referred to by the variable hello. It's type is officially ‘function'.
+
Line 6 is just there to remind you of the difference between referring to the
+ variable name (function name) hello and referring to the string hello.
+
At Step 4 we get to line 8, which has an invocation of the function. The way function invocation works is that the
+ code block inside the function definition is executed in the usual way, but at the end, execution jumps to the point
+ after the function invocation.
+
You can see that by following the next few steps. At Step 5, the red arrow has moved to line 2, which will execute
+ next. We say that control has passed from the top-level program to the function hello. After Steps 5 and 6 print out
+ two lines, at Step 7, control will be passed back to the point after where the invocation was started. At Step 8, that
+ has happened.
+
The same process of invocation occurs again on line 10, with lines 2 and 3 getting executed a second time.
+
+ Common Mistake with Functions
+
It is a common mistake for beginners to forget their parenthesis after the function name. This is particularly
+ common in the case where there parameters are not required. Because the hello function defined above does not
+ require parameters, it's easy to forget the parenthesis. This is less common, but still possible, when trying to
+ call functions that require parameters.
+
+
+ Check your understanding
+
+
+
+
What is a function in Python?
+
+
+
+
+
A named sequence of statements.
+
+
+
Yes, a function is a named sequence of statements.
+
+
+
+
+
Any sequence of statements.
+
+
+
While functions contain sequences of statements, not all sequences of statements are considered functions.
+
+
+
+
+
A mathematical expression that calculates a value.
+
+
+
While some functions do calculate values, the python idea of a function is slightly different from the mathematical idea of a function in that not all functions calculate values. Consider, for example, the turtle functions in this section. They made the turtle draw a specific shape, rather than calculating a value.
+
+
+
+
+
A statement of the form x = 5 + 4.
+
+
+
This statement is called an assignment statement. It assigns the value on the right (9), to the name on the left (x).
+
+
+
+
+
+
+
What is one main purpose of a function?
+
+
+
+
+
To improve the speed of execution
+
+
+
Functions have little effect on how fast the program runs.
+
+
+
+
+
To help the programmer organize programs into chunks that match how they think about the solution to the problem.
+
+
+
While functions are not required, they help the programmer better think about the solution by organizing pieces of the solution into logical chunks that can be reused.
+
+
+
+
+
All Python programs must be written using functions
+
+
+
In the first several chapters, you have seen many examples of Python programs written without the use of functions. While writing and using functions is desirable and essential for good programming style as your programs get longer, it is not required.
+
+
+
+
+
To calculate values.
+
+
+
Not all functions calculate values.
+
+
+
+
+
+
+
How many lines will be output by executing this code?
Here the the function is invoked and there is also a separate print statement.
+
+
+
+
+
1
+
+
+
There is only one print statement outside the funciton, but the invocations of hello also cause lines to print.
+
+
+
+
+
3
+
+
+
There are three print statements, but the function is invoked more than once.
+
+
+
+
+
4
+
+
+
Each time the function is invoked, it will print two lines, not one.
+
+
+
+
+
7
+
+
+
Three invocations generate two lines each, plus the line "It works".
+
+
+
+
+
diff --git a/pretext/Functions/FunctionParameters.ptx b/pretext/Functions/FunctionParameters.ptx
new file mode 100644
index 00000000..687ec9b5
--- /dev/null
+++ b/pretext/Functions/FunctionParameters.ptx
@@ -0,0 +1,345 @@
+
+
+ Function Parameters
+
Named functions are nice because, once they are defined and we understand what they do, we can refer to them by name
+ and not think too much about what they do. With parameters, functions are even more powerful, because they can do
+ pretty much the same thing on each invocation, but not exactly the same thing. The parameters can cause them to do
+ something a little different.
+
+
The figure below shows this relationship. A function needs certain information to do its work. These values, often
+ called arguments or actual parameters or parameter values, are passed to the function by the user.
+
+
This type of diagram is often called a black-box diagram because it only states the requirements from the
+ perspective of the user (well, the programmer, but the programmer who uses the function, who may be different than the
+ programmer who created the function). The user must know the name of the function and what arguments need to be
+ passed. The details of how the function works are hidden inside the black-box.
+
You have already been making function invocations with parameters. For example, when you write len("abc") or
+ len([3, 9, "hello"]), len is the name of a function, and the value that you put inside the parentheses, the string
+ abc or the list [3, 9, hello], is a parameter value.
+
When a function has one or more parameters, the names of the parameters appear in the function definition, and the
+ values to assign to those parameters appear inside the parentheses of the function invocation. Let's look at each of
+ those a little more carefully.
+
In the definition, the parameter list is sometimes referred to as the formal parameters or parameter names.
+ These names can be any valid variable name. If there is more than one, they are separated by commas.
+
In the function invocation, inside the parentheses one value should be provided for each of the parameter names. These
+ values are separated by commas. The values can be specified either directly, or by any python expression including a
+ reference to some other variable name.
+
That can get kind of confusing, so let's start by looking at a function with just one parameter. The revised hello
+ function personalizes the greeting: the person to greet is specified by the parameter.
First, notice that hello2 has one formal parameter, s. You can tell that because
+ there is exactly one variable name inside the parentheses on line 1.
+
Next, notice what happened during Step 2. Control was passed to the function, just like we saw before. But in
+ addition, the variable s was bound to a value, the string Iman. When it got to Step 7, for the second invocation of
+ the function, s was bound to Jackie.
+
Function invocations always work that way. The expression inside the parentheses on the line that invokes the function
+ is evaluated before control is passed to the function. The value is assigned to the corresponding formal parameter.
+ Then, when the code block inside the function is executing, it can refer to that formal parameter and get its value,
+ the value that was ‘passed into' the function.
+
+
To get a feel for that, let's invoke hello2 using some more complicated expressions. Try some of your own, too.
Now let's consider a function with two parameters. This version of hello takes
+ a parameter that controls how many times the greeting will be printed.
At Step 3 of the execution, in the first invocation of hello3, notice that the variable s is bound
+ to the value Wei and the variable n is bound to the value 4.
+
That's how function invocations always work. Each of the expressions, separated by commas, that are inside the
+ parentheses are evaluated to produce values. Then those values are matched up positionally
+ with the formal parameters. The first parameter name is bound to the first value
+ provided. The second parameter name is bound to the second value provided. And so on.
+
+ Check your understanding
+
+
+
+
Which of the following is a valid function header (first line of a function definition)?
+
+
+
+
+
def greet(t):
+
+
+
A function may take zero or more parameters. In this case it has one.
+
+
+
+
+
def greet:
+
+
+
A function needs to specify its parameters in its header. If there are no paramters, put () after the function name.
+
+
+
+
+
greet(t, n):
+
+
+
A function definition needs to include the keyword def.
+
+
+
+
+
def greet(t, n)
+
+
+
A function definition header must end in a colon (:).
+
+
+
+
+
+
+
What is the name of the following function?
+
+
+def print_many(x, y):
+ """Print out string x, y times."""
+ for i in range(y):
+ print(x)
+
+
+
+
+
+
+
def print_many(x, y):
+
+
+
This line is the complete function header (except for the semi-colon) which includes the name as well as several other components.
+
+
+
+
+
print_many
+
+
+
Yes, the name of the function is given after the keyword def and before the list of parameters.
+
+
+
+
+
print_many(x, y)
+
+
+
This includes the function name and its parameters
+
+
+
+
+
Print out string x, y times.
+
+
+
This is a comment stating what the function does.
+
+
+
+
+
+
+
What are the parameters of the following function?
+
+
+def print_many(x, y):
+ """Print out string x, y times."""
+ for i in range(y):
+ print(x)
+
+
+
+
+
+
+
i
+
+
+
i is a variable used inside of the function, but not a parameter, which is passed in to the function.
+
+
+
+
+
x
+
+
+
x is only one of the parameters to this function.
+
+
+
+
+
x, y
+
+
+
Yes, the function specifies two parameters: x and y.
+
+
+
+
+
x, y, i
+
+
+
the parameters include only those variables whose values that the function expects to receive as input. They are specified in the header of the function.
+
+
+
+
+
+
+
Considering the function below, which of the following statements correctly invokes, or calls, this function (i.e., causes it to run)?
+
+
+def print_many(x, y):
+ """Print out string x, y times."""
+ for i in range(y):
+ print(x)
+
+z = 3
+
+
+
+
+
+
+
print_many(x, y)
+
+
+
No, x and y are the names of the formal parameters to this function. When the function is called, it requires actual values to be passed in.
+
+
+
+
+
print_many
+
+
+
A function call always requires parentheses after the name of the function.
+
+
+
+
+
print_many("Greetings")
+
+
+
This function takes two parameters (arguments)
+
+
+
+
+
print_many("Greetings", 10):
+
+
+
A colon is only required in a function definition. It will cause an error with a function call.
+
+
+
+
+
print_many("Greetings", z)
+
+
+
Since z has the value 3, we have passed in two correct values for this function. "Greetings" will be printed 3 times.
+
+
+
+
+
+
+
True or false: A function can be called several times by placing a function call in the body of a for loop.
+
+
+
+
+
True
+
+
+
Yes, you can call a function multiple times by putting the call in a loop.
+
+
+
+
+
False
+
+
+
One of the purposes of a function is to allow you to call it more than once. Placing it in a loop allows it to executed multiple times as the body of the loop runs multiple times.
s1 is a variable name; its value would print out, not the variable name.
+
+
+
+
+
s2
+
+
+
s2 is a variable name; its value would print out, not the variable name.
+
+
+
+
+
diff --git a/pretext/Functions/Functionscancallotherfunctions.ptx b/pretext/Functions/Functionscancallotherfunctions.ptx
new file mode 100644
index 00000000..061bb36a
--- /dev/null
+++ b/pretext/Functions/Functionscancallotherfunctions.ptx
@@ -0,0 +1,107 @@
+
+
+ Functions can call other functions (Composition)
+
It is important to understand that each of the functions we write can be used and called from other functions we
+ write. This is one of the most important ways that computer programmers take a large problem and break it down into a
+ group of smaller problems. This process of breaking a problem into smaller subproblems is called functional decomposition.
+
Here's a simple example of functional decomposition using two functions. The first function called square simply
+ computes the square of a given number. The second function called sum_of_squares makes use of square to compute
+ the sum of three numbers that have been squared.
+
+
+def square(x):
+ y = x * x
+ return y
+
+def sum_of_squares(x,y,z):
+ a = square(x)
+ b = square(y)
+ c = square(z)
+
+ return a+b+c
+
+a = -5
+b = 2
+c = 10
+result = sum_of_squares(a,b,c)
+print(result)
+
+
+
Even though this is a pretty simple idea, in practice this example illustrates many very important Python concepts,
+ including local and global variables along with parameter passing. Note that the body of square is not executed
+ until it is called from inside the sum_of_squares function for the first time on line 6.
+
Also notice that when square is called (at Step 8, for example), there are two groups of local variables, one for
+ square and one for sum_of_squares. Each group of local variables is called a stack frame. The variables
+ x, and y are local variables in both functions. These are completely different variables, even though they
+ have the same name. Each function invocation creates a new frame, and variables are looked up in that frame. Notice
+ that at step 11 of the execution, y has the value 25 in one frame and 2 in the other.
+
What happens when you to refer to variable y on line 3? Python looks up the value of y in the stack frame for the
+ square function. If it didn't find it there, it would go look in the global frame.
+
Let's use composition to build up a little more useful function. Recall from the dictionaries chapter that we had a two-step process for finding the letter that appears most frequently in a text string:
+
+
+
+
Accumulate a dictionary with letters as keys and counts as values. See .
+
+
+
Find the best key from that dictionary. See .
+
+
+
+
We can make functions for each of those and then compose them into a single function that finds the most common letter.
+
+
+def most_common_letter(s):
+ frequencies = count_freqs(s)
+ return best_key(frequencies)
+
+def count_freqs(st):
+ d = {}
+ for c in st:
+ if c not in d:
+ d[c] = 0
+ d[c] = d[c] + 1
+ return d
+
+def best_key(dictionary):
+ ks = dictionary.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 dictionary[k] > dictionary[best_key_so_far]:
+ best_key_so_far = k
+ return best_key_so_far
+
+print(most_common_letter("abbbbbbbbbbbccccddddd"))
+
+
+
+ Check your Understanding
+
+
+
+
1. Write two functions, one called addit and one called mult. addit takes one number as an input and adds 5. mult takes one number as an input, and multiplies that input by whatever is returned by addit, and then returns the result.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(mult(1), 6,"Testing the function mult with input 1 (should be 6)")
+ self.assertEqual(mult(-2), -6, "Testing the function mult with input -2 (should be -6)")
+ self.assertEqual(mult(0), 0, "Testing the function mult with input 0 (should be 0)")
+
+ def testTwo(self):
+ self.assertEqual(addit(1), 6, "Testing the function addit with input 1 (should be 6)")
+ self.assertEqual(addit(-2), 3, "Testing the function addit with input -2 (should be 3)")
+ self.assertEqual(addit(0), 5, "Testing the function addit with input 0 (should be 5)")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Functions/GlobalVariables.ptx b/pretext/Functions/GlobalVariables.ptx
new file mode 100644
index 00000000..8722183d
--- /dev/null
+++ b/pretext/Functions/GlobalVariables.ptx
@@ -0,0 +1,199 @@
+
+
+ Global Variables
+
Variable names that are at the top-level, not inside any function definition,
+ are called global.
+
It is legal for a function to access a global variable. However, this is considered
+ bad form by nearly all programmers and should be avoided. This subsection
+ includes some examples that illustrate the potential interactions of global and
+ local variables. These will help you understand exactly how python works. Hopefully,
+ they will also convince you that things can get pretty confusing when you mix
+ local and global variables, and that you really shouldn't do it.
+
Look at the following, nonsensical variation of the square function.
+
+
+def badsquare(x):
+ y = x ** power
+ return y
+
+power = 2
+result = badsquare(10)
+print(result)
+
+
+
Although the badsquare function works, it is silly and poorly written. We have done it here to illustrate
+ an important rule about how variables are looked up in Python.
+ First, Python looks at the variables that are defined as local variables in
+ the function. We call this the local scope. If the variable name is not
+ found in the local scope, then Python looks at the global variables,
+ or global scope. This is exactly the case illustrated in the code above.
+ power is not found locally in badsquare but it does exist globally.
+ The appropriate way to write this function would be to pass power as a parameter.
+ For practice, you should rewrite the badsquare example to have a second parameter called power.
+
There is another variation on this theme of local versus global variables. Assignment statements in the local function cannot
+ change variables defined outside the function. Consider the following
+ codelens example:
+
+
+def powerof(x,p):
+ power = p # Another dumb mistake
+ y = x ** power
+ return y
+
+power = 3
+result = powerof(10,2)
+print(result)
+
+
+
Now step through the code. What do you notice about the values of variable power
+ in the local scope compared to the variable power in the global scope?
+
The value of power in the local scope was different than the global scope.
+ That is because in this example power was used on the left hand side of the
+ assignment statement power = p. When a variable name is used on the
+ left hand side of an assignment statement Python creates a local variable.
+ When a local variable has the same name as a global variable we say that the
+ local shadows the global. A shadow means that the global variable cannot
+ be accessed by Python because the local variable will be found first. This is
+ another good reason not to use global variables. As you can see,
+ it makes your code confusing and difficult to understand.
+
If you really want to change the value of a global variable inside a function,
+ you can can do it by explicitly declaring the variable to be global, as in the example
+ below. Again, you should not do this in your code. The example is here only
+ to cement your understanding of how python works.
+
+
+def powerof(x,p):
+ global power # a really...
+ power = p # ...bad idea, but valid code
+ y = x ** power
+ return y
+
+power = 3
+result = powerof(10,2)
+print(result)
+print(power)
+
+
+
To cement all of these ideas even further lets look at one final example.
+ Inside the square function we are going to make an assignment to the
+ parameter x There's no good reason to do this other than to emphasize
+ the fact that the parameter x is a local variable. If you step through
+ the example in codelens you will see that although x is 0 in the local
+ variables for square, the x in the global scope remains 2. This is confusing
+ to many beginning programmers who think that an assignment to a
+ formal parameter will cause a change to the value of the variable that was
+ used as the actual parameter, especially when the two share the same name.
+ But this example demonstrates that that is clearly not how Python operates.
+
+
+def square(x):
+ y = x * x
+ x = 0 # assign a new value to the parameter x
+ return y
+
+x = 2
+z = square(x)
+print(z)
+
+
+
+ Check your understanding
+
+
+
+
What is a variable's scope?
+
+
+
+
+
Its value
+
+
+
Value is the contents of the variable. Scope concerns where the variable is "known".
+
+
+
+
+
The range of statements in the code where a variable can be accessed.
+
+
+
Correct.
+
+
+
+
+
Its name
+
+
+
The name of a variable is just an identifier or alias. Scope concerns where the variable is "known".
+
+
+
+
+
+
+
What is a local variable?
+
+
+
+
+
A temporary variable that is only used inside a function
+
+
+
Yes, a local variable is a temporary variable that is only known (only exists) in the function it is defined in.
+
+
+
+
+
The same as a parameter
+
+
+
While parameters may be considered local variables, functions may also define and use additional local variables.
+
+
+
+
+
Another name for any variable
+
+
+
Variables that are used outside a function are not local, but rather global variables.
+
+
+
+
+
+
+
Can you use the same name for a local variable as a global variable?
+
+
+
+
+
Yes, and there is no reason not to.
+
+
+
While there is no problem as far as Python is concerned, it is generally considered bad style because of the potential for the programmer to get confused.
+
+
+
+
+
Yes, but it is considered bad form.
+
+
+
it is generally considered bad style because of the potential for the programmer to get confused. If you must use global variables (also generally bad form) make sure they have unique names.
+
+
+
+
+
No, it will cause an error.
+
+
+
Python manages global and local scope separately and has clear rules for how to handle variables with the same name in different scopes, so this will not cause a Python error.
+
+
+
+
+
+
WP: Scope
+
You may be asking yourself at this point when you should make some object a local variable and when should you make it a global variable. Generally, we do not recommend making variables global. Imagine you are trying to write a program that keeps track of money while purchasing groceries. You may make a variable that represents how much money the person has, called wallet. You also want to make a function called purchase, which will take the name of the item and its price, and then add the item to a list of groceries, and deduct the price from the amount stored in wallet. If you initialize wallet before the function as a variable within the global scope instead of passing it as a third parameter for purchase, then an error would occur because wallet would not be found in the local scope. Though there are ways to get around this, as outlined in this page, if your program was supposed to handle groceries for multiple people, then you would need to declare each wallet as a global variable in the functions that want to use wallet, and that would become very confusing and tedious to deal with.
A value provided to a function when the function is called. This value
+ is assigned to the corresponding parameter in the function. The argument
+ can be the result of an expression which may involve operators,
+ operands and calls to other fruitful functions.
+
+
+ body
+
The second part of a compound statement. The body consists of a
+ sequence of statements all indented the same amount from the beginning
+ of the header. The standard amount of indentation used within the
+ Python community is 4 spaces.
+
+
+ calling stack
+
A sequence (stack) of frames, showing all the function calls that are in process
+ but not yet complete. When one function's code invokes another function call,
+ there will be more than one frame on the stack.
+
+
+ compound statement
+
A statement that consists of two parts:
+
+
+
+
header - which begins with a keyword determining the statement
+ type, and ends with a colon.
+
+
+
body - containing one or more statements indented the same amount
+ from the header.
+
+
+
+
The syntax of a compound statement looks like this:
If the first thing in a function body is a string (or, we'll see later, in other situations
+ too) that is attached to the function as its __doc__ attribute.
+
+
+ flow of execution
+
The order in which statements are executed during a program run.
+
+
+ function
+
A named sequence of statements that performs some useful operation.
+ Functions may or may not take parameters and may or may not produce a
+ result.
+
+
+ function call
+
A statement that executes a function. It consists of the name of the
+ function followed by a list of arguments enclosed in parentheses.
+
+
+ function composition
+
Using the output from one function call as the input to another.
+
+
+ function definition
+
A statement that creates a new function, specifying its name,
+ parameters, and the statements it executes.
+
+
+ fruitful function
+
A function that returns a value when it is called.
+
+
+ global variable
+
A variable defined at the top level, not inside any function.
+
+
+ header line
+
The first part of a compound statement. A header line begins with a keyword and
+ ends with a colon (:)
+
+
+ lifetime
+
Variables and objects have lifetimes — they are created at some point during
+ program execution, and will be destroyed at some time. In python, objects
+ live as long as there is some variable pointing to it, or it is part of some
+ other compound object, like a list or a dictionary. In python, local variables
+ live only until the function finishes execution.
+
+
+ local variable
+
A variable defined inside a function. A local variable can only be used
+ inside its function. Parameters of a function are also a special kind
+ of local variable.
+
+
+ method
+
A special kind of function that is invoked on objects of particular types of
+ objects, using the syntax <expr>.<methodname>(<additional parameter values>)
+
+
+ None
+
A special Python value. One use in Python is that it is returned
+ by functions that do not execute a return statement with a return argument.
+
+
+ parameter
+
A name used inside a function to refer to the value which was passed
+ to it as an argument.
+
+
+ return value
+
The value provided as the result of a function call.
+
+
+ side effect
+
Some lasting effect of a function call, other than its return value. Side effects include print statements, changes to mutable objects, and changes to the values of global variables.
+
+
+ stack frame
+
A frame that keeps track of the values of local variables during a function execution,
+ and where to return control when the function execution completes.
+
+
+ type annotation
+
An optional notation that specifies the type of a function parameter or function result.
+
+
+
diff --git a/pretext/Functions/PassingMutableObjects.ptx b/pretext/Functions/PassingMutableObjects.ptx
new file mode 100644
index 00000000..a793f7d5
--- /dev/null
+++ b/pretext/Functions/PassingMutableObjects.ptx
@@ -0,0 +1,4 @@
+
+
+ Placeholder - needs manual fixing
+
\ No newline at end of file
diff --git a/pretext/Functions/Printvsreturn.ptx b/pretext/Functions/Printvsreturn.ptx
new file mode 100644
index 00000000..90a35fd5
--- /dev/null
+++ b/pretext/Functions/Printvsreturn.ptx
@@ -0,0 +1,207 @@
+
+
+ 👩💻 Print vs. return
+
Many beginning programmers find the distinction between print and return very confusing, especially since most of the
+ illustrations of return values in intro texts like this one show the returned value from a function call by printing
+ it, as in print(square(g(2))).
+
The print statement is fairly easy to understand. It takes a python object and outputs a printed representation of it
+ in the output window. You can think of the print statement as something that takes an object from the land of the
+ program and makes it visible to the land of the human observer.
+
+
Print is for people. Remember that slogan. Printing has no effect on the ongoing execution of a program. It doesn't assign a value to a variable. It doesn't return a value from a function call.
+
+
If you're confused, chances are the source of your confusion is really about returned values and the evaluation of
+ complex expressions. A function that returns a value is producing a value for use by the program, in particular for
+ use in the part of the code where the function was invoked. Remember that when a function is invoked, the function's
+ code block is executed – all that code indented under the def statement gets executed, following the rules of the
+ Python formal language for what should and should not execute as it goes. But when the function returns, control goes
+ back to the calling location, and a return value may come back with it.
+
You've already seen some function calls in Chapter 1. When we told you about the function square that we defined,
+ you saw that the expression square(2) evaluated to the integer value 4.
+
That's because the square function returns a value: the square of whatever input is passed into it.
+
If a returned value is for use by the program, why did you make that function invocation to return a value? What do
+ you use the result of the function call for? There are three possibilities.
+
+
+
+
+
+ Save it for later.
+
The returned value may be:
+
+
+
+
Assigned to a variable. For example, w = square(3)
+
+
+
Put in a list. For example, L.append(square(3))
+
+
+
Put in a dictionary. For example, d[3] = square(3)
+
+
+
+
+
+
+
+
+
+ Use it in a more complex expression.
+
In that case, think of the return value as
+ replacing the entire text of the function invocation. For example, if there is a line
+ of code w = square(square(3) + 7) - 5, think of the return value 9 replacing the
+ text square(3) in that invocation, so it becomes square(9 + 7) -5.
+
+
+
+
+
+
+ Print it for human consumption.
+
For example, print(square(3)) outputs 9 to the
+ output area. Note that, unless the return value is first saved as in possibility 1, it will be available
+ only to the humans watching the output area, not to the program as it continues executing.
+
+
+
+
+
+
If your only purpose in running a function is to make an output visible for human consumption, there are two ways to
+ do it. You can put one or more print statements inside the function definition and not bother to return anything from
+ the function (the value None will be returned). In that case, invoke the function without a print statement. For
+ example, you can have an entire line of code that reads f(3). That will run the function f and throw away the
+ return value. Of course, if square doesn't print anything out or have any side effects, it's useless to call it and do
+ nothing with the return value. But with a function that has print statements inside it, it can be quite useful.
+
The other possibility is to return a value from the function and print it, as in print(f(3)). As you start to
+ write larger, more complex programs, this will be more typical. Indeed the print statement will usually only be a
+ temporary measure while you're developing the program. Eventually, you'll end up calling f and saving the return value
+ or using it as part of a more complex expression.
+
You will know you've really internalized the idea of functions when you are no longer confused about the difference
+ between print and return. Keep working at it until it makes sense to you!
h(2) returns 7, so y is bound to 7 when g is invoked.
+
+
+
+
+
Error: you can't nest function calls
+
+
+
Ah, but you can nest function calls.
+
+
+
+
+
diff --git a/pretext/Functions/Returningavaluefromafunction.ptx b/pretext/Functions/Returningavaluefromafunction.ptx
new file mode 100644
index 00000000..f3688ff4
--- /dev/null
+++ b/pretext/Functions/Returningavaluefromafunction.ptx
@@ -0,0 +1,630 @@
+
+
+ Returning a value from a function
+
+
Not only can you pass a parameter value into a function, a function can also produce a value. You have already
+ seen this in some previous functions that you have used. For example, len takes a list or string as a parameter
+ value and returns a number, the length of that list or string. range takes an integer as a parameter value and
+ returns a list containing all the numbers from 0 up to that parameter value.
+
Functions that return values are sometimes called fruitful functions. In many other languages, a function that
+ doesn't return a value is called a procedure, but we will stick here with the Python way of also calling it a
+ function, or if we want to stress it, a non-fruitful function.
+
+
+
How do we write our own fruitful function? Let's start by creating a very simple mathematical function that we will
+ call square. The square function will take one number as a parameter and return the result of squaring that
+ number. Here is the black-box diagram with the Python code following.
+
+
+
+def square(x):
+ y = x * x
+ return y
+
+toSquare = 10
+result = square(toSquare)
+print("The result of {} squared is {}.".format(toSquare, result))
+
+
+
The return statement is followed by an expression which is evaluated. Its result is returned to the caller as the
+ fruit of calling this function. Because the return statement can contain any Python expression we could have
+ avoided creating the temporary variabley and simply used return x*x. Try modifying the square function
+ above to see that this works just the same. On the other hand, using temporary variables like y in the program
+ above makes debugging easier. These temporary variables are referred to as local variables.
+
Notice something important here. The name of the variable we pass as an argument — toSquare — has nothing to
+ do with the name of the formal parameter — x. It is as if x = toSquare is executed when square is
+ called. It doesn't matter what the value was named in the caller (the place where the function was invoked). Inside
+ square, it's name is x. You can see this very clearly in codelens, where the global variables and the local
+ variables for the square function are in separate boxes.
+
+
+def square(x):
+ y = x * x
+ return y
+
+toSquare = 10
+squareResult = square(toSquare)
+
+
+
There is one more aspect of function return values that should be noted. All Python functions return the special value
+ None unless there is an explicit return statement with a value other than None. Consider the following common
+ mistake made by beginning Python programmers. As you step through this example, pay very close attention to the return
+ value in the local variables listing. Then look at what is printed when the function is over.
+
+
+def square(x):
+ y = x * x
+ print(y) # Bad! This is confusing! Should use return instead!
+
+toSquare = 10
+squareResult = square(toSquare)
+print("The result of {} squared is {}.".format(toSquare, squareResult))
+
+
+
The problem with this function is that even though it prints the value of the squared input, that value will not be
+ returned to the place where the call was done. Instead, the value None will be returned. Since line 6 uses the
+ return value as the right hand side of an assignment statement, squareResult will have None as its value and
+ the result printed in line 7 is incorrect. Typically, functions will return values that can be printed or processed in
+ some other way by the caller.
+
A return statement, once executed, immediately terminates execution of a function, even if it is not the last
+ statement in the function. In the following code, when line 3 executes, the value 5 is returned and assigned to the
+ variable x, then printed. Lines 4 and 5 never execute. Run the following code and try making some modifications of
+ it to make sure you understand why there and 10 never print out.
The fact that a return statement immediately ends execution of the code block inside a function is important to
+ understand for writing complex programs, and it can also be very useful. The following example is a situation where
+ you can use this to your advantage – and understanding this will help you understand other people's code better, and
+ be able to walk through code more confidently.
+
Consider a situation where you want to write a function to find out, from a class attendance list, whether anyone's
+ first name is longer than five letters, called longer_than_five. If there is anyone in class whose first name is
+ longer than 5 letters, the function should return True. Otherwise, it should return False.
+
In this case, you'll be using conditional statements in the code that exists in the function body, the code block
+ indented underneath the function definition statement (just like the code that starts with the line print("here")
+ in the example above – that's the body of the function weird, above).
+
Bonus challenge for studying: After you look at the explanation below, stop looking at the code – just the
+ description of the function above it, and try to write the code yourself! Then test it on different lists and make
+ sure that it works. But read the explanation first, so you can be sure you have a solid grasp on these function
+ mechanics.
+
First, an English plan for this new function to define called longer_than_five:
+
+
+
+
You'll want to pass in a list of strings (representing people's first names) to the function.
+
+
+
You'll want to iterate over all the items in the list, each of the strings.
+
+
+
As soon as you get to one name that is longer than five letters, you know the function should return True – yes, there is at least one name longer than five letters!
+
+
+
And if you go through the whole list and there was no name longer than five letters, then the function should return False.
+
+
+
+
Now, the code:
+
+
+def longer_than_five(list_of_names):
+ for name in list_of_names: # iterate over the list to look at each name
+ if len(name) > 5: # as soon as you see a name longer than 5 letters,
+ return True # then return True!
+ # If Python executes that return statement, the function is over and the rest of the code will not run -- you already have your answer!
+ return False # You will only get to this line if you
+ # iterated over the whole list and did not get a name where
+ # the if expression evaluated to True, so at this point, it's correct to return False!
+
+# Here are a couple sample calls to the function with different lists of names. Try running this code in Codelens a few times and make sure you understand exactly what is happening.
+
+list1 = ["Sam","Tera","Sal","Amita"]
+list2 = ["Rey","Ayo","Lauren","Natalie"]
+
+print(longer_than_five(list1))
+print(longer_than_five(list2))
+
+
+
So far, we have just seen return values being assigned to variables. For example, we had the line
+ squareResult = square(toSquare). As with all assignment statements, the right hand side is executed first. It
+ invokes the square function, passing in a parameter value 10 (the current value of toSquare). That returns a
+ value 100, which completes the evaluation of the right-hand side of the assignment. 100 is then assigned to the
+ variable squareResult. In this case, the function invocation was the entire expression that was evaluated.
+
Function invocations, however, can also be used as part of more complicated expressions. For example,
+ squareResult = 2 * square(toSquare). In this case, the value 100 is returned and is then multiplied by 2 to
+ produce the value 200. When python evaluates an expression like x * 3, it substitutes the current value of x into
+ the expression and then does the multiplication. When python evaluates an expression like 2 * square(toSquare), it
+ substitutes the return value 100 for entire function invocation and then does the multiplication.
+
To reiterate, when executing a line of code squareResult = 2 * square(toSquare), the python
+ interpreter does these steps:
+
+
+
+
It's an assignment statement, so evaluate the right-hand side expression 2 * square(toSquare).
+
+
+
Look up the values of the variables square and toSquare: square is a function object and toSquare is 10
+
+
+
Pass 10 as a parameter value to the function, get back the return value 100
+
+
+
Substitute 100 for square(toSquare), so that the expression now reads 2 * 100
+
+
+
Assign 200 to variable squareResult
+
+
+
+
+ Check your understanding
+
+
+
+
What is wrong with the following function definition:
You should never use a print statement in a function definition.
+
+
+
Although you should not mistake print for return, you may include print statements inside your functions.
+
+
+
+
+
You should not have any statements in a function after the return statement. Once the function gets to the return statement it will immediately stop executing the function.
+
+
+
This is a very common mistake so be sure to watch out for it when you write your code!
+
+
+
+
+
You must calculate the value of x+y+z before you return it.
+
+
+
Python will automatically calculate the value x+y+z and then return it in the statement as it is written
+
+
+
+
+
A function cannot return a number.
+
+
+
Functions can return any legal data, including (but not limited to) numbers, strings, lists, dictionaries, etc.
We have accidentally used print where we mean return. Therefore, the function will return the value None by default. This is a VERY COMMON mistake so watch out! This mistake is also particularly difficult to find because when you run the function the output looks the same. It is not until you try to assign its value to a variable that you can notice a difference.
+
+
+
+
+
The value of x+y+z
+
+
+
Careful! This is a very common mistake. Here we have printed the value x+y+z but we have not returned it. To return a value we MUST use the return keyword.
+
+
+
+
+
The string 'x+y+z'
+
+
+
x+y+z calculates a number (assuming x+y+z are numbers) which represents the sum of the values x, y and z.
+
+
+
+
+
+
+
What will the following code output?
+
+
+def square(x):
+ y = x * x
+ return y
+
+print(square(5) + square(5))
+
+
+
+
+
+
+
25
+
+
+
It squares 5 twice, and adds them together.
+
+
+
+
+
50
+
+
+
The two return values are added together.
+
+
+
+
+
25 + 25
+
+
+
The two results are substituted into the expression and then it is evaluated. The returned values are integers in this case, not strings.
+
+
+
+
+
+
+
What will the following code output?
+
+
+def square(x):
+ y = x * x
+ return y
+
+print(square(square(2)))
+
+
+
+
+
+
+
8
+
+
+
It squares 2, yielding the value 4. But that doesn't mean the next value multiplies 2 and 4.
+
+
+
+
+
16
+
+
+
It squares 2, yielding the value 4. 4 is then passed as a value to square again, yeilding 16.
+
+
+
+
+
Error: can't put a function invocation inside parentheses
+
+
+
This is a more complicated expression, but still valid. The expression square(2) is evaluated, and the return value 4 substitutes for square(2) in the expression.
+
+
+
+
+
+
+
What will the following code output?
+
+
+def cyu2(s1, s2):
+ x = len(s1)
+ y = len(s2)
+ return x-y
+
+z = cyu2("Yes", "no")
+if z > 0:
+ print("First one was longer")
+else:
+ print("Second one was at least as long")
+
+
+
+
+
+
+
1
+
+
+
cyu2 returns the value 1, but that's not what prints.
+
+
+
+
+
Yes
+
+
+
"Yes" is longer, but that's not what prints.
+
+
+
+
+
First one was longer
+
+
+
cyu2 returns the value 1, which is assigned to z.
+
+
+
+
+
Second one was at least as long
+
+
+
cyu2 returns the value 1, which is assigned to z.
+
+
+
+
+
Error
+
+
+
what do you think will cause an error.
+
+
+
+
+
+
+
Which will print out first, square, g, or a number?
Before executing square, it has to figure out what value to pass in, so g is executed first
+
+
+
+
+
g
+
+
+
g has to be executed and return a value in order to know what paramater value to provide to x.
+
+
+
+
+
a number
+
+
+
square and g both have to execute before the number is printed.
+
+
+
+
+
+
+
How many lines will the following code print?
+
+
+def show_me_numbers(list_of_ints):
+ print(10)
+ print("Next we'll accumulate the sum")
+ accum = 0
+ for num in list_of_ints:
+ accum = accum + num
+ return accum
+ print("All done with accumulation!")
+
+show_me_numbers([4,2,3])
+
+
+
+
+
+
+
3
+
+
+
The function gets to a return statement after 2 lines are printed, so the third print statement will not run.
+
+
+
+
+
2
+
+
+
Yes! Two printed lines, and then the function body execution reaches a return statement.
+
+
+
+
+
None
+
+
+
The function returns an integer value! However, this code does not print out the result of the function invocation, so you can't see it (print is for people). The only lines you see printed are the ones that occur in the print statements before the return statement.
+
+
+
+
+
+
+
8. Write a function named same that takes a string as input, and simply returns that string.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(same('hello'), 'hello', "Testing the same function on input 'hello'.")
+
+myTests().main()
+
+
+
+
+
+
9. Write a function called same_thing that returns the parameter, unchanged.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(same_thing(5), 5,"Testing the function same_thing with input 5")
+ self.assertEqual(same_thing("Welcome"), "Welcome", "Testing the function same_thing with input 'Welcome'")
+
+myTests().main()
+
+
+
+
+
+
10. Write a function called subtract_three that takes an integer or any number as input, and returns that number minus three.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(subtract_three(9), 6, "Testing the subtract_three function on input 9.")
+ self.assertEqual(subtract_three(-5), -8, "Testing the subtract_three function on input -5.")
+
+myTests().main()
+
+
+
+
+
+
11. Write a function called change that takes one number as its input and returns that number, plus 7.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(change(5), 12,"Testing the function change with input 5")
+ self.assertEqual(change(-10), -3, "Testing the function change with input -10")
+
+myTests().main()
+
+
+
+
+
+
12. Write a function named intro that takes a string as input. This string ist intended to be a person's name and the output is a standardized greeting. For example, given the string Becky as input, the function should return: Hello, my name is Becky and I love SI 106.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(intro("Mike"), "Hello, my name is Mike and I love SI 106.", "Testing the intro function on input 'Mike'.")
+
+myTests().main()
+
+
+
+
+
+
13. Write a function called s_change that takes one string as input and returns that string, concatenated with the string for fun..
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(s_change("Coding"), "Coding for fun." ,"Testing the function s_change with input coding")
+ self.assertEqual(s_change("We go to the beach"), "We go to the beach for fun." , "Testing the function s_change with input We go to the beach")
+
+myTests().main()
+
+
+
+
+
+
14. Write a function called decision that takes a string as input, and then checks the number of characters. If it has over 17 characters, return This is a long string, if it is shorter or has 17 characters, return This is a short string.
+
+
+
+
+=====
+
+from unittest.gui import TestCaseGui
+
+class myTests(TestCaseGui):
+
+ def testOne(self):
+ self.assertEqual(decision("Well hello dolly"), "This is a short string", "Testing the function decision with input 'Well hello dolly'")
+ self.assertEqual(decision("In olden days a glimps of stocking was looked on a something shocking but heaven knows, anything goes"), "This is a long string", "Testing the function decision with input 'In olden days a glimps of stocking was looked on a something shocking but heaven knows, anything goes'")
+ self.assertEqual(decision("how do you do sir"), "This is a short string", "Testing the function decision with input 'how do you do sir'")
+
+myTests().main()
+
+
+
+
diff --git a/pretext/Functions/SideEffects.ptx b/pretext/Functions/SideEffects.ptx
new file mode 100644
index 00000000..77a52f5e
--- /dev/null
+++ b/pretext/Functions/SideEffects.ptx
@@ -0,0 +1,69 @@
+
+
+ Side Effects
+
We say that the function changeit has a side effect on the list object that is passed to it. Global
+ variables are another way to have side effects. For example, similar to examples you have seen above, we could make
+ double have a side effect on the global variable y.
+
+
+def double(n):
+ global y
+ y = 2 * n
+
+y = 5
+double(y)
+print(y)
+
+
+
Side effects are sometimes convenient. For example, it may be convenient to have a single dictionary that accumulates
+ information, and pass it around to various functions that might add to it or modify it.
+
However, programs that have side effects can be very difficult to debug. When an object has a value that is not what
+ you expected, it can be difficult to track down exactly where in the code it was set. Wherever it is practical to do
+ so, it is best to avoid side effects. The way to avoid using side effects is to use return values instead.
+
Instead of modifying a global variable inside a function, pass the global variable's value in as a parameter, and set
+ that global variable to be equal to a value returned from the function. For example, the following is a better version
+ of the code above.
You can use the same coding pattern to avoid confusing side effects with sharing of mutable objects. To do that,
+ explicitly make a copy of an object and pass the copy in to the function. Then return the modified copy and reassign it
+ to the original variable if you want to save the changes. The built-in list function, which takes a sequence as a
+ parameter and returns a new list, works to copy an existing list. For dictionaries, you can similarly call the dict
+ function, passing in a dictionary to get a copy of the dictionary back as a return value.
In general, any lasting effect that occurs in a function, not through its return value, is called a side effect. There
+ are three ways to have side effects:
+
+
+
+
Printing out a value. This doesn't change any objects or variable bindings, but it does have a potential lasting effect outside the function execution, because a person might see the output and be influenced by it.
+
+
+
Changing the value of a mutable object.
+
+
+
Changing the binding of a global variable.
+
+
+
+
diff --git a/pretext/Functions/TypeAnnotations.ptx b/pretext/Functions/TypeAnnotations.ptx
new file mode 100644
index 00000000..acad79c5
--- /dev/null
+++ b/pretext/Functions/TypeAnnotations.ptx
@@ -0,0 +1,157 @@
+
+
+ Type Annotations
+
In the previous section, we discussed the decoding work that is required when you look at a function and
+ are trying to determine the types of its parameters. In this section, we'll introduce a feature that can help
+ reduce the amount of sleuthing that is needed.
+
Consider the following function definition:
+
+
+def duplicate(msg):
+ """Returns a string containing two copies of `msg`"""
+
+ return msg + msg
+
+
+
This function is intended to duplicate a message; if called with the value ‘Hello', it returns the value
+ ‘HelloHello'. If called with other types of data, however, it will not work properly. (What will the
+ function do if given an int or a float value?)
+
Python allows you to indicate the intended type of the function parameters and the type of the function return value
+ in a function definition using a special notation demonstrated in this example:
+
+
+def duplicate(msg: str) -> str:
+ """Returns a string containing two copies of `msg`"""
+
+ return msg + msg
+
+result = duplicate('Hello')
+print(result)
+
+
+
This definition of duplicate makes use of type annotations that indicate the function's parameter type and return
+ type. A type annotation, sometimes called a type hint, is an optional notation that specifies the type of a parameter or function result. It
+ tells the programmer using the function what kind of data to pass to the function, and what kind of data to expect when
+ the function returns a value.
+
In the definition above, the annotation : str in msg: str indicates that the caller should pass a str value as an argument.
+ The annotation -> str indicates that the function will produce a str result.
+
Here are some more examples of functions with type annotations:
+
+
+def add(x: int, y: int) -> int:
+ """Returns the sum of `x` and `y`"""
+
+ return x + y
+
+def get_number(msg: str) -> float:
+ """Prompts with `msg` for input; returns numeric response."""
+
+ return float(input(msg))
+
+def display_msg(msg: str):
+ """Displays `msg` with dashed line underneath"""
+
+ print(msg)
+ print('-------------------------------------')
+
+
+
It's important to understand that adding type annotations to a function definition does not cause the Python interpreter
+ to check that the values passed to a function are the expected types, or cause the returned value to be converted to the
+ expected type. For example, if the function add in the example above is called like this:
+
result = add('5', '15')
+
the function will receive two string values, concatenate them, and return the resulting string ‘515'. The int
+ annotations are completely ignored by the Python interpreter. Think of type annotations as a kind of function
+ documentation, and remember that they have no effect on the program's behavior.
+
Type annotations are an optional aspect of documenting functions. Still, type annotations are an important tool to increase
+ the readability of your code, and you should use them in your programs.
+
+
Although type annotations are ignored by the Python interpreter, there are tools such as
+ mypy that can analyze your code containing type annotations and flag potential problems.
+
+
Type hints can be especially useful for container types, like lists and dictionaries. When type hinting was first introduced into python, in version 3.5, it was possible to specify them, but a little clunky. Later versions made it a little easier.
+
For example, in the following code, which is valid in python version 3.10, the count_words function takes a string as input and returns a dictionary. That dictionary's keys should all be strings and the value associated with every key should be an integer.
+
+
+def count_words(text: str) -> dict[str, int]:
+ words = text.split()
+ d = {}
+ for word in words:
+ if word not in d:
+ d[word] = 1
+ else:
+ d[word] += 1
+ return d
+
+
+
In the code below, the function add_em_up takes an input that is expected to be a list of numbers. It returns the sum of all of them.
+
+
+def add_em_up(nums: list[int]) -> int:
+ tot = 0
+ for num in nums:
+ tot += num
+ return tot
+
+
+
Actually, this code should work just fine if the inputs are either integers or floats. If any are floats, then the return value will be a float. The more recent versions of type annotations in python allow the use the | symbol (pronounced pipe) to specify a union, that either of two types is permitted. You may find that it's not permitted in the current runestone interpreter, though.
+
+
+def add_em_up(nums: list[int | float]) -> int | float:
+ tot = 0
+ for num in nums:
+ tot += num
+ return tot
+
+
+
Correct! Python ignores the ': str' annotation and returns the sum of msg (the float 2.5) + 2.
+
+
+
+
+
The value 2.52 is displayed on the screen.
+
+
+
Incorrect. In this call, msg contains the float value 2.5; the ': str' annotation serves only as documentation.
+
+
+
+
+
A runtime error occurs when the function is invoked because 2.5 is not a string.
+
+
+
Incorrect. Python ignores the ': str' annotation and allows the float value 2.5 to be passed to msg.
+
+
+
+
+
A runtime error occurs because the expression 'msg + 2' illegally attempts to concatenate a str and an int.
+
+
+
Incorrect. In this call, msg contains the float value 2.5, not a str, so the + operation is legal.
+
+
+
+
+
diff --git a/pretext/Functions/Variablesandparametersarelocal.ptx b/pretext/Functions/Variablesandparametersarelocal.ptx
new file mode 100644
index 00000000..daac43fd
--- /dev/null
+++ b/pretext/Functions/Variablesandparametersarelocal.ptx
@@ -0,0 +1,279 @@
+
+
+ Variables and parameters are local
+
+
An assignment statement in a function creates a local variable for the variable on the left hand side of the
+ assignment operator. It is called local because this variable only exists inside the function and you cannot use it
+ outside. For example, consider again the square function:
+
+
+def square(x):
+ y = x * x
+ return y
+
+z = square(10)
+print(y)
+
+
+
Try running this in Codelens. When a function is invoked in Codelens, the local scope is separated from global scope by
+ a blue box. Variables in the local scope will be placed in the blue box while global variables will stay in the global
+ frame. If you press the ‘last >>' button you will see an error message. When we try to use y on line 6 (outside the
+ function) Python looks for a global variable named y but does not find one. This results in the error:
+ Name Error: 'y' is not defined.
+
The variable y only exists while the function is being executed — we call this its lifetime. When the
+ execution of the function terminates (returns), the local variables are destroyed. Codelens helps you visualize this
+ because the local variables disappear after the function returns. Go back and step through the statements paying
+ particular attention to the variables that are created when the function is called. Note when they are subsequently
+ destroyed as the function returns.
+
Formal parameters are also local and act like local variables. For example, the lifetime of x begins when
+ square is called, and its lifetime ends when the function completes its execution.
+
So it is not possible for a function to set some local variable to a value, complete its execution, and then when it
+ is called again next time, recover the local variable. Each call of the function creates new local variables, and
+ their lifetimes expire when the function returns to the caller.
+
+ Check Your Understanding
+
+
+
+
True or False: Local variables can be referenced outside of the function they were defined in.
+
+
+
+
+
True
+
+
+
Local variables cannot be referenced outside of the function they were defined in.
+
+
+
+
+
False
+
+
+
Local variables cannot be referenced outside of the function they were defined in.
+
+
+
+
+
+
+
Which of the following are local variables? Please, write them in order of what line they are on in the code.
Correct, the output is right because the subtract function takes in x as the global variable for the z parameter and puts it into the function. The subtract function uses the local variable y for its return.
+
+
+
+
+
6
+
+
+
Incorrect, look again at what is being produced in the subtract function.
+
+
+
+
+
10
+
+
+
Incorrect, look again at what is being produced in the subtract function.
+
+
+
+
+
Code will give an error because x and z do not match.
+
+
+
Incorrect, there shouldn't be any error.
+
+
+
+
+
+
+
What would be the result of running the following code?
+
+
+def adding(x):
+ y = 3
+ z = y + x + x
+ return z
+
+def producing(x):
+ z = x * y
+ return z
+
+print(producing(adding(4)))
+
+
+
+
+
+
+
33
+
+
+
Incorrect, look again at what is happening in producing.
+
+
+
+
+
12
+
+
+
Incorrect, look again at what is happening in producing.
+
+
+
+
+
There is an error in the code.
+
+
+
Yes! There is an error because we reference y in the producing function, but it was defined in adding. Because y is a local variable, we can't use it in both functions without initializing it in both. If we initialized y as 3 in both though, the answer would be 33.
+
+
+
+
+
+
+
What would be the result of running the following code?
Incorrect, pay attention to the local scope in the function.
+
+
+
+
+
9
+
+
+
Incorrect, pay attention to the local scope in the function.
+
+
+
+
+
10
+
+
+
Incorrect, pay attention to the local scope in the function.
+
+
+
+
+
Error, local variable 'x' is referenced before assignment.
+
+
+
This code gives an error because the local variable 'x' was referenced in the local scope before it was assigned a value.
+
+
+
+
+
+
+ v1 += 1
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+NameError: name 'v1' is not defined
+ def foo():
+ v1 += 1
+ foo()
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ File "<stdin>", line 2, in foo
+UnboundLocalError: local variable 'v1' referenced before assignment
+
+
+
In the code above, notice and understand the different error messages.
+ The local variables are created at the same time the local namespace
+ is created. That is any variable that is assigned to anywhere
+ in the function gets added to the local namespace immediately but
+ it will remain unbound until the assignment statement is executed.
+
diff --git a/pretext/Functions/intro-Functions.ptx b/pretext/Functions/intro-Functions.ptx
new file mode 100644
index 00000000..61ca164f
--- /dev/null
+++ b/pretext/Functions/intro-Functions.ptx
@@ -0,0 +1,86 @@
+
+
+ Introduction to Functions
+
+
In Python, a function is a chunk of code that performs some operation that is meaningful for a person to think about as a whole unit, for example calculating a student's GPA in a learning system or responding to the jump action in a video game. Once a function has been defined and you are satisfied that it does what it is supposed to do, you will start thinking about it in terms of the larger operation that it performs rather than the specific lines of code that make it work.
+
This breaking down of a task or problem is crucial to the successful implementation of any program of more than 50 or so lines (and plenty of smaller ones too). For example, the program that displays the Instagram landing page is made up of functions that:
+
+
+
+
display the header bar
+
+
+
display your friends' posts
+
+
+
display your friends' stories
+
+
+
display the ad at the bottom of the screen recommending you use the app
+
+
+
+
And each of those is made up of functions as well. For example, the function that displays your friends' posts is a for loop that calls a function to:
+
+
+
+
display a single post which in turn calls functions to:
+
+
+
display the photo and name of the person posting the story
+
+
+
display the photo itself
+
+
+
display other users' likes to the story
+
+
+
display the comments on the story
+
+
+
etc.
+
+
+
+
In this chapter you will learn about named functions, functions that can be referred to by name when you want to execute them.
+
+ Topics
+
+
+
+
functions as a means of abstraction
+
+
+
local and global scope
+
+
+
side effects
+
+
+
+
+
+ Learning Objectives
+
At the end of this chapter, you should be able to:
+
+
+
+
identify formal parameters and parameter values in a code sample
+
+
+
predict the return value of a function given sample parameter values
+
+
+
define functions with appropriate names for formal parameter
+
+
+
avoid the use of global variables in function definitions by creating formal parameters for all values that are needed