diff --git a/.gitignore b/.gitignore
index 6d8b9b0c..200982db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,9 @@ slides_sources/build
*.pyc
*junk*
+# testing detritus
+.cache
+
#ignore sublime workspace files
*.sublime*
@@ -13,3 +16,6 @@ slides_sources/build
# editor back-up files
*.*~
+
+# pycache
+__pycache__/*
\ No newline at end of file
diff --git a/Examples/Session01/schedule.py b/Examples/Session01/schedule.py
index 7dcb1300..4bdb59db 100644
--- a/Examples/Session01/schedule.py
+++ b/Examples/Session01/schedule.py
@@ -8,16 +8,17 @@
# remove the header line
del students[0]
+# strip the whitespace
+students = [line.strip() for line in students]
+
# remove the languages, colon, etc.
students = [line.split(":")[0] for line in students]
# reverse the first, last names
-
# separate them:
students = [line.split(",") for line in students]
-
# put them back together
-students = ["{} {}".format(first, last) for last, first in students]
+students = ["{} {}".format(first.strip(), last) for last, first in students]
# put them in random order
random.shuffle(students)
@@ -26,7 +27,7 @@
weeks = list(range(2, 11))
# make three of them...
-weeks = weeks * 3
+weeks = weeks * 4
# put the students together with the weeks
schedule = zip(weeks, students)
diff --git a/Examples/Session01/schedule.txt b/Examples/Session01/schedule.txt
deleted file mode 100644
index 9d7c5ee7..00000000
--- a/Examples/Session01/schedule.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-week 2: Brendan Fogarty
-week 2: Bruce Bauman
-week 2: Michelle Yu
-week 3: Eric Rosko
-week 3: Michael Waddle
-week 3: Robert Stevens Alford
-week 4: Andrey Gusev
-week 4: Cheryl Ohashi
-week 4: Maxwell MacCamy
-week 5: Michael Cimino
-week 5: Pei Lin
-week 5: Tiffany Ku
-week 6: Gabriel Meringolo
-week 6: Joseph Cardenas
-week 6: Marc Teale
-week 7: Eric Starr Vegors
-week 7: Ian Cote
-week 7: Masako Tebbetts
-week 8: Kathleen Devlin
-week 8: Robert Ryan Leslie
-week 8: Ryan Morin
-week 9: Erica Winberry
-week 9: Robert Jenkins
-week 10: Austin Scara
-week 10: Marty Pitts
diff --git a/Examples/Session01/students.txt b/Examples/Session01/students.txt
deleted file mode 100644
index 2f992dfd..00000000
--- a/Examples/Session01/students.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-name: languages
-Alford, Robert Stevens: javascript php
-Bauman, Bruce: chemstation macro fortran, java
-Cardenas, Joseph: python C html CSS lisp javascript
-Cimino, Michael: C C++ Java SQL
-Cote, Ian: bash ruby perl python
-Devlin, Kathleen: 4D
-Fogarty, Brendan: SQL
-Gusev, Andrey: perl java bash
-Jenkins, Robert: assm pascal
-Ku, Tiffany: python SQL
-Leslie, Robert Ryan: python
-Lin, Pei: SQL java R
-MacCamy, Maxwell: C C++ C# assm java
-Meringolo, Gabriel: python
-Morin, Ryan: python sql
-Ohashi, Cheryl:
-Pitts, Marty: python, similink and matlab
-Rosko, Eric: C C++
-Scara, Austin: VBA SQL
-Teale, Marc: perl bash
-Tebbetts, Masako: SQL
-Vegors, Eric Starr: bash perl
-Waddle, Michael:
-Winberry, Erica: python
-Yu, Michelle: ruby objectiveC
diff --git a/Examples/Session01/test.py b/Examples/Session01/test.py
index 1466c954..8a557325 100644
--- a/Examples/Session01/test.py
+++ b/Examples/Session01/test.py
@@ -1,9 +1,12 @@
x = 5
y = 55
-print x, y
+print(x, y)
def f():
x = 5
return x
+
+def f2():
+ 5 + "5"
diff --git a/Examples/Session03/mailroom_start.py b/Examples/Session03/mailroom_start.py
new file mode 100644
index 00000000..9605afe7
--- /dev/null
+++ b/Examples/Session03/mailroom_start.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+
+def print_report():
+ print("This will print a report")
+
+
+def send_thanks():
+ print("This will write a thank you note")
+
+# here is where triple quoted strings can be helpful
+msg = """
+What would you like to do?
+
+To send a thank you: type "s"
+To print a report: type "p"
+To exit: type "x"
+"""
+
+
+def main():
+ """
+ run the main interactive loop
+ """
+
+ response = ''
+ # keep asking until the users responds with an 'x'
+ while True: # make sure there is a break if you have infinite loop!
+ print(msg)
+ response = input("==> ").strip() # strip() in case there are any spaces
+
+ if response == 'p':
+ print_report()
+ elif response == 's':
+ send_thanks()
+ elif response == 'x':
+ break
+ else:
+ print('please type "s", "p", or "x"')
+
+if __name__ == "__main__":
+ main()
diff --git a/Examples/Session03/slicing_lab.py b/Examples/Session03/slicing_lab.py
new file mode 100644
index 00000000..a13c5623
--- /dev/null
+++ b/Examples/Session03/slicing_lab.py
@@ -0,0 +1,31 @@
+# slicing lab
+
+
+def swap(seq):
+ return seq[-1:]+seq[1:-1]+seq[:1]
+
+
+assert swap('something') == 'gomethins'
+assert swap(tuple(range(10))) == (9,1,2,3,4,5,6,7,8,0)
+
+def rem(seq):
+ return seq[::2]
+
+assert rem('a word') == 'awr'
+
+def rem4(seq):
+ return seq[4:-4:2]
+
+print(rem4( (1,2,3,4,5,6,7,8,9,10,11), ) )
+
+def reverse(seq):
+ return seq[::-1]
+
+print(reverse('a string'))
+
+def thirds(seq):
+ i = len(seq)//3
+ #return seq[i*2:i*3+1] + seq[:i] + seq[i:i*2]
+ return seq[i:-i] + seq[-i:] + seq[:i]
+
+print (thirds(tuple(range(12))))
diff --git a/Examples/Session04/__main__example.py b/Examples/Session04/__main__example.py
new file mode 100755
index 00000000..603f2e57
--- /dev/null
+++ b/Examples/Session04/__main__example.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+print("every module has a __name__")
+
+print("What it is depends on how it is used")
+
+print("right now, this module's __name__ is: {}".format(__name__))
+
+# so if you want coce to run only when a module is a top level script,
+# you use this clause:
+#if __name__ == "__main__":
+
+print("I must be running as a top-level script")
diff --git a/Examples/Session05/arg_test.py b/Examples/Session05/arg_test.py
index c84b5bdf..83c76e80 100644
--- a/Examples/Session05/arg_test.py
+++ b/Examples/Session05/arg_test.py
@@ -2,5 +2,4 @@
import sys
-print sys.argv
-
+print(sys.argv)
diff --git a/Examples/Session05/codingbat.py b/Examples/Session05/codingbat.py
index 1dcf82eb..3971c4eb 100644
--- a/Examples/Session05/codingbat.py
+++ b/Examples/Session05/codingbat.py
@@ -10,6 +10,4 @@
def sleep_in(weekday, vacation):
- return not (weekday == True and vacation == False)
-
-
+ return not (weekday and vacation)
diff --git a/Examples/Session05/test_codingbat.py b/Examples/Session05/test_codingbat.py
index 6e845b0e..6681bdc3 100755
--- a/Examples/Session05/test_codingbat.py
+++ b/Examples/Session05/test_codingbat.py
@@ -14,7 +14,7 @@ def test_false_false():
def test_true_false():
- assert not ( sleep_in(True, False) )
+ assert not (sleep_in(True, False))
def test_false_true():
diff --git a/Examples/Session05/test_pytest_parameter.py b/Examples/Session05/test_pytest_parameter.py
index 52449af3..4e82a3ab 100644
--- a/Examples/Session05/test_pytest_parameter.py
+++ b/Examples/Session05/test_pytest_parameter.py
@@ -8,6 +8,7 @@
"""
import pytest
+
# a (really simple) function to test
def add(a, b):
"""
@@ -17,14 +18,14 @@ def add(a, b):
# now some test data:
-test_data = [ ( ( 2, 3), 5),
- ( (-3, 2), -1),
- ( ( 2, 0.5), 2.5),
- ( ( "this", "that"), "this that"),
- ( ( [1,2,3], [6,7,8]), [1,2,3,6,7,8]),
- ]
+test_data = [((2, 3), 5),
+ ((-3, 2), -1),
+ ((2, 0.5), 2.5),
+ (("this", "that"), "this that"),
+ (([1, 2, 3], [6, 7, 8]), [1, 2, 3, 6, 7, 8]),
+ ]
+
@pytest.mark.parametrize(("input", "result"), test_data)
def test_add(input, result):
assert add(*input) == result
-
diff --git a/Examples/Session05/test_random_pytest.py b/Examples/Session05/test_random_pytest.py
index e8b80f8c..c798efa9 100644
--- a/Examples/Session05/test_random_pytest.py
+++ b/Examples/Session05/test_random_pytest.py
@@ -8,20 +8,19 @@
import pytest
-seq = range(10)
+seq = list(range(10))
def test_shuffle():
# make sure the shuffled sequence does not lose any elements
random.shuffle(seq)
- seq.sort()
- print "seq:", seq
- ## expect this to fail -- so we can see the output.
- assert seq == range(10)
+ # seq.sort() # this will amke it fail, so we can see output
+ print("seq:", seq) # only see output if it fails
+ assert seq == list(range(10))
def test_shuffle_immutable():
- pytest.raises(TypeError, random.shuffle, (1,2,3) )
+ pytest.raises(TypeError, random.shuffle, (1, 2, 3))
def test_choice():
diff --git a/Examples/Session05/test_random_unitest.py b/Examples/Session05/test_random_unitest.py
index 6458e6ce..f825be5b 100644
--- a/Examples/Session05/test_random_unitest.py
+++ b/Examples/Session05/test_random_unitest.py
@@ -1,19 +1,20 @@
import random
import unittest
+
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
- self.seq = range(10)
+ self.seq = list(range(10))
def test_shuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
- self.assertEqual(self.seq, range(10))
+ self.assertEqual(self.seq, list(range(10)))
# should raise an exception for an immutable sequence
- self.assertRaises(TypeError, random.shuffle, (1,2,3) )
+ self.assertRaises(TypeError, random.shuffle, (1, 2, 3))
def test_choice(self):
element = random.choice(self.seq)
@@ -26,4 +27,4 @@ def test_sample(self):
self.assertTrue(element in self.seq)
if __name__ == '__main__':
- unittest.main()
\ No newline at end of file
+ unittest.main()
diff --git a/Examples/Session06/cigar_party.py b/Examples/Session06/cigar_party.py
index 18878463..992d99bd 100644
--- a/Examples/Session06/cigar_party.py
+++ b/Examples/Session06/cigar_party.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
"""
When squirrels get together for a party, they like to have cigars.
@@ -11,18 +10,6 @@
"""
-def cigar_party(cigars, is_weekend):
- """
- basic solution
- """
- if ( 40 <= cigars <= 60 ) or ( cigars >= 40 and is_weekend):
- return True
- else:
- return False
+def cigar_party(num, weekend):
+ return num >= 40 and (num <= 60 or weekend)
-
-def cigar_party3(cigars, is_weekend):
- """
- conditional expression
- """
- return (cigars >= 40) if is_weekend else (cigars >= 40 and cigars <= 60)
diff --git a/Examples/Session06/closure.py b/Examples/Session06/closure.py
new file mode 100644
index 00000000..a7e7d07d
--- /dev/null
+++ b/Examples/Session06/closure.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+"""
+Example code for closures / currying
+"""
+
+from functools import partial
+
+
+def counter(start_at=0):
+ count = [start_at]
+
+ def incr():
+ count[0] += 1
+ return count[0]
+ return incr
+
+
+def power(base, exponent):
+ """returns based raised to the given exponent"""
+ return base ** exponent
+
+# now some specialized versions:
+
+square = partial(power, exponent=2)
+cube = partial(power, exponent=3)
diff --git a/Examples/Session06/codingbat.py b/Examples/Session06/codingbat.py
new file mode 100644
index 00000000..25865839
--- /dev/null
+++ b/Examples/Session06/codingbat.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+"""
+Examples from: http://codingbat.com
+
+Put here so we can write unit tests for them ourselves
+"""
+
+# Python > Warmup-1 > sleep_in
+
+# The parameter weekday is True if it is a weekday, and the parameter
+# vacation is True if we are on vacation.
+#
+# We sleep in if it is not a weekday or we're on vacation.
+# Return True if we sleep in.
+
+
+def sleep_in(weekday, vacation):
+ return not weekday or vacation
+
+
+# We have two monkeys, a and b, and the parameters a_smile and b_smile
+# indicate if each is smiling.
+
+# We are in trouble if they are both smiling or if neither of them is
+# smiling.
+
+# Return True if we are in trouble.
+
+def monkey_trouble(a_smile, b_smile):
+ return a_smile is b_smile
diff --git a/Examples/Session06/safe_input.py b/Examples/Session06/safe_input.py
new file mode 100644
index 00000000..81785375
--- /dev/null
+++ b/Examples/Session06/safe_input.py
@@ -0,0 +1,25 @@
+def safe_input():
+ try:
+ the_input = input("\ntype something >>> ")
+ except (KeyboardInterrupt, EOFError) as error:
+ print("the error: ", error)
+ error.extra_info = "extra info for testing"
+ # raise
+ return None
+ return the_input
+
+def main():
+ safe_input()
+
+def divide(x,y):
+ try:
+ return x/y
+ except ZeroDivisionError as err:
+ print("you put in a zero!!!")
+ print("the exeption:", err)
+ err.args = (("very bad palce for a zero",))
+ err.extra_stuff = "all kinds of things"
+ raise
+
+# if __name__ == '__main__':
+# main()
\ No newline at end of file
diff --git a/Examples/Session06/test_cigar_party.py b/Examples/Session06/test_cigar_party.py
index a03ca3c5..80bc0920 100644
--- a/Examples/Session06/test_cigar_party.py
+++ b/Examples/Session06/test_cigar_party.py
@@ -1,10 +1,20 @@
#!/usr/bin/env python
-import cigar_party
+"""
+When squirrels get together for a party, they like to have cigars.
+A squirrel party is successful when the number of cigars is between
+40 and 60, inclusive. Unless it is the weekend, in which case there
+is no upper bound on the number of cigars.
-#cigar_party = cigar_party.cigar_party
-#cigar_party = cigar_party.cigar_party2
-cigar_party = cigar_party.cigar_party3
+Return True if the party with the given values is successful,
+or False otherwise.
+"""
+
+
+# you can change this import to test different versions
+from cigar_party import cigar_party
+# from cigar_party import cigar_party2 as cigar_party
+# from cigar_party import cigar_party3 as cigar_party
def test_1():
diff --git a/Examples/Session06/test_codingbat.py b/Examples/Session06/test_codingbat.py
new file mode 100755
index 00000000..354dcb34
--- /dev/null
+++ b/Examples/Session06/test_codingbat.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+"""
+test file for codingbat module
+
+This version can be run with nose or py.test
+"""
+
+from codingbat import sleep_in, monkey_trouble
+
+
+# tests for sleep_in
+def test_false_false():
+ assert sleep_in(False, False)
+
+
+def test_true_false():
+ assert not (sleep_in(True, False))
+
+
+def test_false_true():
+ assert sleep_in(False, True)
+
+
+def test_true_true():
+ assert sleep_in(True, True)
+
+
+# put tests for monkey_trouble here
+# monkey_trouble(True, True) → True
+# monkey_trouble(False, False) → True
+# monkey_trouble(True, False) → False
+
+def test_monkey_true_true():
+ assert monkey_trouble(True, True)
+
+def test_monkey_false_false():
+ assert monkey_trouble(False, False)
+
+def test_monkey_true_false():
+ assert monkey_trouble(True, False) is False
+
+# more!
diff --git a/Examples/Session06/test_pytest_parameter.py b/Examples/Session06/test_pytest_parameter.py
new file mode 100644
index 00000000..4e82a3ab
--- /dev/null
+++ b/Examples/Session06/test_pytest_parameter.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+"""
+pytest example of a parameterized test
+
+NOTE: there is a failure in here! can you fix it?
+
+"""
+import pytest
+
+
+# a (really simple) function to test
+def add(a, b):
+ """
+ returns the sum of a and b
+ """
+ return a + b
+
+# now some test data:
+
+test_data = [((2, 3), 5),
+ ((-3, 2), -1),
+ ((2, 0.5), 2.5),
+ (("this", "that"), "this that"),
+ (([1, 2, 3], [6, 7, 8]), [1, 2, 3, 6, 7, 8]),
+ ]
+
+
+@pytest.mark.parametrize(("input", "result"), test_data)
+def test_add(input, result):
+ assert add(*input) == result
diff --git a/Examples/Session06/test_random_pytest.py b/Examples/Session06/test_random_pytest.py
new file mode 100644
index 00000000..441f239a
--- /dev/null
+++ b/Examples/Session06/test_random_pytest.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+"""
+port of the random unit tests from the python docs to py.test
+"""
+
+import random
+import pytest
+
+
+seq = list(range(10))
+
+
+def test_shuffle():
+ # make sure the shuffled sequence does not lose any elements
+ random.shuffle(seq)
+ # seq.sort() # commenting this out will make it fail, so we can see output
+ print("seq:", seq) # only see output if it fails
+ assert seq == list(range(10))
+
+
+def test_shuffle_immutable():
+ """should get a TypeError with an imutable type """
+ with pytest.raises(TypeError):
+ random.shuffle((1, 2, 3))
+
+
+def test_choice():
+ """make sure a random item selected is in the original sequence"""
+ element = random.choice(seq)
+ assert (element in seq)
+
+
+def test_sample():
+ """make sure all items returned by sample are there"""
+ for element in random.sample(seq, 5):
+ assert element in seq
+
+
+def test_sample_too_large():
+ """should get a ValueError if you try to sample too many"""
+ with pytest.raises(ValueError):
+ random.sample(seq, 20)
diff --git a/Examples/Session06/test_random_unitest.py b/Examples/Session06/test_random_unitest.py
new file mode 100644
index 00000000..f825be5b
--- /dev/null
+++ b/Examples/Session06/test_random_unitest.py
@@ -0,0 +1,30 @@
+import random
+import unittest
+
+
+class TestSequenceFunctions(unittest.TestCase):
+
+ def setUp(self):
+ self.seq = list(range(10))
+
+ def test_shuffle(self):
+ # make sure the shuffled sequence does not lose any elements
+ random.shuffle(self.seq)
+ self.seq.sort()
+ self.assertEqual(self.seq, list(range(10)))
+
+ # should raise an exception for an immutable sequence
+ self.assertRaises(TypeError, random.shuffle, (1, 2, 3))
+
+ def test_choice(self):
+ element = random.choice(self.seq)
+ self.assertTrue(element in self.seq)
+
+ def test_sample(self):
+ with self.assertRaises(ValueError):
+ random.sample(self.seq, 20)
+ for element in random.sample(self.seq, 5):
+ self.assertTrue(element in self.seq)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Examples/Session07/circle.py b/Examples/Session07/circle.py
deleted file mode 100644
index a6545632..00000000
--- a/Examples/Session07/circle.py
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env python
-"""circle class --
-
-fill this in so it will pass all the tests.
-"""
-import math
-
-
-class Circle(object):
- pass
diff --git a/Examples/Session06/class.py b/Examples/Session07/class.py
similarity index 82%
rename from Examples/Session06/class.py
rename to Examples/Session07/class.py
index 1c131142..ac9a99b4 100644
--- a/Examples/Session06/class.py
+++ b/Examples/Session07/class.py
@@ -1,8 +1,8 @@
-class C(object):
+class C:
x = 5
+
def __init__(self, y):
self.y = y
+
def meth(self, z):
return self.x + self.y + z
-
-
\ No newline at end of file
diff --git a/Examples/Session06/class_demo.py b/Examples/Session07/class_demo.py
similarity index 88%
rename from Examples/Session06/class_demo.py
rename to Examples/Session07/class_demo.py
index 33841a88..e66590d1 100644
--- a/Examples/Session06/class_demo.py
+++ b/Examples/Session07/class_demo.py
@@ -1,5 +1,5 @@
-class C(object):
+class C:
x = 5
def __init__(self, y):
diff --git a/Examples/Session06/html_render/.DS_Store b/Examples/Session07/html_render/.DS_Store
similarity index 100%
rename from Examples/Session06/html_render/.DS_Store
rename to Examples/Session07/html_render/.DS_Store
diff --git a/Examples/Session06/html_render/html_render.py b/Examples/Session07/html_render/html_render.py
similarity index 100%
rename from Examples/Session06/html_render/html_render.py
rename to Examples/Session07/html_render/html_render.py
diff --git a/Examples/Session06/html_render/run_html_render.py b/Examples/Session07/html_render/run_html_render.py
similarity index 77%
rename from Examples/Session06/html_render/run_html_render.py
rename to Examples/Session07/html_render/run_html_render.py
index 74e5c378..d282fdc9 100644
--- a/Examples/Session06/html_render/run_html_render.py
+++ b/Examples/Session07/html_render/run_html_render.py
@@ -7,46 +7,51 @@
"""
-from cStringIO import StringIO
-
+from io import StringIO
# importing the html_rendering code with a short name for easy typing.
import html_render as hr
-reload(hr) # reloading in case you are running this in iPython
- # -- we want to make sure the latest version is used
+# reloading in case you are running this in iPython
+# -- we want to make sure the latest version is used
+import importlib
+importlib.reload(hr)
-## writing the file out:
+# writing the file out:
def render_page(page, filename):
"""
render the tree of elements
- This uses cSstringIO to render to memory, then dump to console and
+ This uses StringIO to render to memory, then dump to console and
write to file -- very handy!
"""
f = StringIO()
page.render(f, " ")
- f.reset()
+ f.seek(0)
- print f.read()
+ print(f.read())
- f.reset()
- open(filename, 'w').write( f.read() )
+ f.seek(0)
+ open(filename, 'w').write(f.read())
-## Step 1
-##########
+# Step 1
+#########
page = hr.Element()
-page.append("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text")
+page.append("Here is a paragraph of text -- there could be more of them, "
+ "but this is enough to show that we can do some text")
page.append("And here is another piece of text -- you should be able to add any number")
render_page(page, "test_html_output1.html")
+# The rest of the steps have been commented out.
+# Uncomment them a you move along with the assignment.
+
# ## Step 2
# ##########
@@ -54,7 +59,8 @@ def render_page(page, filename):
# body = hr.Body()
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text"))
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text"))
# body.append(hr.P("And here is another piece of text -- you should be able to add any number"))
@@ -74,7 +80,8 @@ def render_page(page, filename):
# body = hr.Body()
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text"))
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text"))
# body.append(hr.P("And here is another piece of text -- you should be able to add any number"))
# page.append(body)
@@ -93,7 +100,8 @@ def render_page(page, filename):
# body = hr.Body()
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text",
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
# style="text-align: center; font-style: oblique;"))
# page.append(body)
@@ -112,7 +120,8 @@ def render_page(page, filename):
# body = hr.Body()
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text",
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
# style="text-align: center; font-style: oblique;"))
# body.append(hr.Hr())
@@ -133,7 +142,8 @@ def render_page(page, filename):
# body = hr.Body()
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text",
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
# style="text-align: center; font-style: oblique;"))
# body.append(hr.Hr())
@@ -160,7 +170,8 @@ def render_page(page, filename):
# body.append( hr.H(2, "PythonClass - Class 6 example") )
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text",
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
# style="text-align: center; font-style: oblique;"))
# body.append(hr.Hr())
@@ -199,7 +210,8 @@ def render_page(page, filename):
# body.append( hr.H(2, "PythonClass - Class 6 example") )
-# body.append(hr.P("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text",
+# body.append(hr.P("Here is a paragraph of text -- there could be more of them, "
+# "but this is enough to show that we can do some text",
# style="text-align: center; font-style: oblique;"))
# body.append(hr.Hr())
@@ -221,7 +233,3 @@ def render_page(page, filename):
# page.append(body)
# render_page(page, "test_html_output8.html")
-
-
-
-
diff --git a/Examples/Session06/html_render/sample_html.html b/Examples/Session07/html_render/sample_html.html
similarity index 100%
rename from Examples/Session06/html_render/sample_html.html
rename to Examples/Session07/html_render/sample_html.html
diff --git a/Examples/Session07/html_render/test_html_output1.html b/Examples/Session07/html_render/test_html_output1.html
new file mode 100644
index 00000000..65c2d86c
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output1.html
@@ -0,0 +1,5 @@
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+ And here is another piece of text -- you should be able to add any number
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output2.html b/Examples/Session07/html_render/test_html_output2.html
new file mode 100644
index 00000000..d96afdc0
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output2.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+ And here is another piece of text -- you should be able to add any number
+
+
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output3.html b/Examples/Session07/html_render/test_html_output3.html
new file mode 100644
index 00000000..fcc9f120
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output3.html
@@ -0,0 +1,14 @@
+
+
+
+ PythonClass = Revision 1087:
+
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+ And here is another piece of text -- you should be able to add any number
+
+
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output4.html b/Examples/Session07/html_render/test_html_output4.html
new file mode 100644
index 00000000..dd891d92
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output4.html
@@ -0,0 +1,11 @@
+
+
+
+ PythonClass = Revision 1087:
+
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output5.html b/Examples/Session07/html_render/test_html_output5.html
new file mode 100644
index 00000000..5f160646
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output5.html
@@ -0,0 +1,12 @@
+
+
+
+ PythonClass = Revision 1087:
+
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output6.html b/Examples/Session07/html_render/test_html_output6.html
new file mode 100644
index 00000000..a99822d7
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output6.html
@@ -0,0 +1,15 @@
+
+
+
+ PythonClass = Revision 1087:
+
+
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
+
+ And this is a
+ link
+ to google
+
+
\ No newline at end of file
diff --git a/Examples/Session07/html_render/test_html_output7.html b/Examples/Session07/html_render/test_html_output7.html
new file mode 100644
index 00000000..4f2c23bb
--- /dev/null
+++ b/Examples/Session07/html_render/test_html_output7.html
@@ -0,0 +1,26 @@
+
+
+
+ PythonClass = Revision 1087:
+
+
+
PythonClass - Class 6 example
+
+ Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text
+
"
+
+
+# #Extra credit:
def test_tag_wrapper():
@tag_wrapper('html')
diff --git a/Examples/Session10/timer.py b/Examples/Session10/timer.py
new file mode 100644
index 00000000..3a07bd75
--- /dev/null
+++ b/Examples/Session10/timer.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+"""
+timing context manager
+"""
+
+import sys
+import time
+
+class Timer:
+ def __init__(self, outfile=sys.stdout):
+ self.outfile = outfile
+
+ def __enter__(self):
+ self.start = time.time()
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.outfile.write("elapsed time:{} seconds".format(time.time() - self.start))
\ No newline at end of file
diff --git a/Examples/Session10/timer_context.py b/Examples/Session10/timer_context.py
new file mode 100644
index 00000000..ee327dcd
--- /dev/null
+++ b/Examples/Session10/timer_context.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python3
+
+"""
+Timer context manager
+"""
+import time
+
+
+class Timer:
+ def __init__(self, file_like):
+ self.file_like = file_like
+
+ def __enter__(self):
+ self.start = time.clock()
+
+ def __exit__(self, *args):
+ elapsed = time.clock() - self.start
+ msg = "Elapsed time: {} seconds".format(elapsed)
+ self.file_like.write(msg)
+ return False
diff --git a/Examples/Session10/ICanEatGlass.utf16.txt b/Examples/Suppliments/ICanEatGlass.utf16.txt
similarity index 100%
rename from Examples/Session10/ICanEatGlass.utf16.txt
rename to Examples/Suppliments/ICanEatGlass.utf16.txt
diff --git a/Examples/Session10/ICanEatGlass.utf8.txt b/Examples/Suppliments/ICanEatGlass.utf8.txt
similarity index 100%
rename from Examples/Session10/ICanEatGlass.utf8.txt
rename to Examples/Suppliments/ICanEatGlass.utf8.txt
diff --git a/Examples/Session10/add_book_data.py b/Examples/Suppliments/add_book_data.py
similarity index 100%
rename from Examples/Session10/add_book_data.py
rename to Examples/Suppliments/add_book_data.py
diff --git a/Examples/Session10/add_book_data_flat.py b/Examples/Suppliments/add_book_data_flat.py
similarity index 100%
rename from Examples/Session10/add_book_data_flat.py
rename to Examples/Suppliments/add_book_data_flat.py
diff --git a/Examples/Session10/example.cfg b/Examples/Suppliments/example.cfg
similarity index 100%
rename from Examples/Session10/example.cfg
rename to Examples/Suppliments/example.cfg
diff --git a/Examples/Session10/hello_unicode.py b/Examples/Suppliments/hello_unicode.py
similarity index 100%
rename from Examples/Session10/hello_unicode.py
rename to Examples/Suppliments/hello_unicode.py
diff --git a/Examples/Session10/latin1_test.py b/Examples/Suppliments/latin1_test.py
similarity index 100%
rename from Examples/Session10/latin1_test.py
rename to Examples/Suppliments/latin1_test.py
diff --git a/Examples/Session10/text.utf16 b/Examples/Suppliments/text.utf16
similarity index 100%
rename from Examples/Session10/text.utf16
rename to Examples/Suppliments/text.utf16
diff --git a/Examples/Session10/text.utf32 b/Examples/Suppliments/text.utf32
similarity index 100%
rename from Examples/Session10/text.utf32
rename to Examples/Suppliments/text.utf32
diff --git a/Examples/Session10/text.utf8 b/Examples/Suppliments/text.utf8
similarity index 100%
rename from Examples/Session10/text.utf8
rename to Examples/Suppliments/text.utf8
diff --git a/Examples/Session10/unicode_exception_test.py b/Examples/Suppliments/unicode_exception_test.py
similarity index 100%
rename from Examples/Session10/unicode_exception_test.py
rename to Examples/Suppliments/unicode_exception_test.py
diff --git a/Examples/Session10/unicodify.py b/Examples/Suppliments/unicodify.py
similarity index 100%
rename from Examples/Session10/unicodify.py
rename to Examples/Suppliments/unicodify.py
diff --git a/README.rst b/README.rst
index 835744c3..9274ab3e 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,7 @@ IntroToPython
Introduction to Python: First in the Python Certification series.
-This repository contains the source materials for the first class in the the University of Washington Professional and Continuing Education Program Python Certification Program:
+This repository contains the source materials for the first class in the University of Washington Professional and Continuing Education Program Python Certification Program:
.. _Certificate in Python Programming : http://www.pce.uw.edu/certificates/python-programming.html
diff --git a/Syllabus.rst b/Syllabus.rst
index 1d171f87..86a9b6ef 100644
--- a/Syllabus.rst
+++ b/Syllabus.rst
@@ -15,8 +15,8 @@ The Python Certificate program is a 9-month curriculum divided into three course
First Course: Introduction to Python
=====================================
-Tuesdays 6-9 pm: Oct 6 - Dec 8, 2015 (10 Sessions)
-.....................................................
+Tuesdays 6-9 pm: Sept 27 - Dec 6, 2016 (10 Sessions)
+---------------------------------------------------
NOTE: in the spirit of the dynamic nature of Python, this Syllabus (and the class) will be a dynamic document -- evolving as the class progresses. The general structure is fixed, but the details will change to meet the evolving needs of the class.
@@ -142,7 +142,7 @@ Reading:
There is no assigned text book. However, you may find it beneficial to read other discussions of topics in addition to what I present in class or assign as reading: either to explore a topic more deeply, or to simple get another viewpoint. There are many good books on Python, and many more excellent discussions of individual topics on the web.
-Note that mamny books still cover primarily (or only) Python 2. THey can still be very, very useful, the syntax is only a little different, and the concepts the same.
+Note that many books still cover primarily (or only) Python 2. THey can still be very, very useful, the syntax is only a little different, and the concepts the same.
A few you may want to consider:
@@ -233,7 +233,7 @@ Class Schedule:
Topics of each week
--------------------
-Week 1: Oct 6
+Week 1: September 27
................
General Introduction to Python and the class. Using the command interpreter and development environment.
@@ -245,7 +245,7 @@ Finding and using the documentation. Getting help.
Python 2/3 differences.
-Week 2: Oct 13
+Week 2: October 4
................
Introduction to git and gitHub
@@ -259,7 +259,7 @@ Modules and import
Conditionals and Boolean expressions
-Week 3: Oct 20
+Week 3: October 11
.................
Sequences: Strings, Tuples, Lists
@@ -269,61 +269,62 @@ Iteration, looping and control flow.
String methods and formatting
-Week 4: Oct 27
+Week 4: October 18
................
Dictionaries, Sets and Mutability.
-Exceptions.
-
Files and Text Processing
-Week 5: November 3
+Week 5: October 25
........................
-Advanced Argument passing
-
-Testing
+Exceptions
List and Dict Comprehensions
-Week 6: November 10
-....................
+Week 6: November 1
+..................
-Lambda and Functions as Objects
+Testing
-Object Oriented Programming: classes, instances, and methods
+Advanced Argument passing
-Week 7: November 17
-.......................
-More OO -- Multiple inheritance, Properties, special methods
+**No class Nov 8th for election night**
+Week 7: November 15
+...................
-Week 8: November 24
-....................
+Object Oriented Programming:
-More OO -- Emulating built-in types
+classes, instances, methods, inheritance
-Iterators and Generators
+Week 8: November 22
+...................
+More OO: Multiple inheritance, Properties, Special methods.
-Week 9: December 1
+Emulating built-in types
+
+
+Week 9: November 29
...................
+Lambda
-Decorators
+Functions as Objects
-Context Managers
+Iterators and Generators
-Packages and packaging
+Week 10: December 6
+...................
-Week 10: December 8
-....................
+Decorators
-Unicode
+Context Managers
Wrap Up / Students Code review
diff --git a/exercises/circle_class.html b/exercises/circle_class.html
deleted file mode 100644
index 341bc5c0..00000000
--- a/exercises/circle_class.html
+++ /dev/null
@@ -1,438 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- Circle Class Excercise — Introduction To Python 1.3 documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Both the fibonacci series and the lucas numbers are based on an identical
-formula.
-
Add a third function called sum_series with one required parameter and two
-optional parameters. The required parameter will determine which element in the
-series to print. The two optional parameters will have default values of 0 and
-1 and will determine the first two values for the series to be produced.
-
Calling this function with no optional parameters will produce numbers from the
-fibonacci series. Calling it with the optional arguments 2 and 1 will
-produce values from the lucas numbers. Other values for the optional
-parameters will produce other series.
-
Ensure that your function has a well-formed docstring
Add an if__name__=="__main__": block to the end of your series.py
-module. Use the block to write a series of assert statements that
-demonstrate that your three functions work properly.
-
Use comments in this block to inform the observer what your tests do.
-
Add your new module to your git clone and commit frequently while working on
-your implementation. Include good commit messages that explain concisely both
-what you are doing and why.
-
When you are finished, push your changes to your fork of the class repository
-in GitHub and make a pull request.
One of the points of writing functions is so you can write code that does similar things, but customized to input parameters. So what if we want to be able to print that grid at an arbitrary size?
-
Write a function print_grid(n) that takes one integer argument
-and prints a grid like the picture above, BUT the size of the
-grid is given by the argument.
-
For example, print_grid(11) prints the grid in the above picture.
-
This problem is underspecified. Do something reasonable.
The goal is to create a set of classes to render html pages – in a “pretty printed” way. i.e nicely indented and human readable. We’ll try to get to all the features required to render:
For each step, add the required functionality. There is example code to run your code for each step in: code\session06\run_html_render.py
-
name your file: html_render.py – so it can be imported by run_html_render.py
-
You should be able to run that code at each step, uncommenting each new step in run_html_render.py as you go.
-
It builds up a html tree, and then calls the render() method of your element to render the page.
-
It uses a cStringIO object (like a file, but in memory) to render to memory, then dumps it to the console, and writes a file. Take a look at the code at the end to make sure you understand it.
-
The html generated at each step is in the files: test_html_ouput?.html
-
At each step, your results should look similar that those (maybe not identical...)
Create an Element class for rendering an html element (xml element).
-
It should have class attributes for the tag name (“html” first) and the indentation (spaces to indent for pretty printing)
-
The constructor signature should look like
-
Element(content=None)
-
-
-
where content is a string
-
It should have an append method that can add another string to the content.
-
It should have a render(file_out,ind="") method that renders the tag and the strings in the content.
-
file_out could be any file-like object ( i.e. have a write() method ).
-
ind is a string with the indentation level in it: the amount that the tag should be indented for pretty printing.
-
-
-
This is a little tricky: ind will be the amount that this element should be indented already. It will be from zero (an empty string) to a lot of spaces, depending on how deep it is in the tree.
-
-
-
The amount of indentation should be set by the class attribute: indent
-
NOTE: don’t worry too much about indentation at this stage – the primary goal is to get proper, compliant html. i.e. the opening and closing tags rendered correctly. Worry about cleaning up the indentation once you’ve got that working.
-
You should now be able to render an html tag with text in it as contents.
Create a couple subclasses of Element, for a html, <body>, and <p> tag. All you should have to do is override the tag class attribute (you may need to add a tag class attribute to the Element class first...).
-
Now you can render a few different types of element.
-
Extend the Element.render() method so that it can render other elements inside the tag in addition to strings. Simple recursion should do it. i.e. it can call the render() method of the elements it contains. You’ll need to be smart about setting the ind optional parameter – so that the nested elements get indented correctly.
-
Figure out a way to deal with the fact that the contained elements could be either simple strings or Element s with render methods (there are a few ways to handle that...).
-
You should now be able to render a basic web page with an html tag around
-the whole thing, a <body> tag inside, and multiple <p> tags inside that, with text inside that. And all indented nicely.
-
See test_html_output2.html
-
NOTE: when you run step 2 in run_html_render.py, you will want o comment out step 1 – that way you’ll only get one set of output.
It should override the render method, to render everything on one line – for the simple tags, like:
-
<title> PythonClass - Session 6 example </title>
-
-
-
-
-
Create a Title subclass of OneLineTag class for the title.
-
You should now be able to render an html doc with a head element, with a
-title element in that, and a body element with some <P> elements and some text.
Update the Html element class to render the “<!DOCTYPE html>” tag at the head of the page, before the html element.
-
You can do this by subclassing Element, overriding render(), but then calling the Element render from the new render.
-
Create a subclass of SelfClosingTag for <metacharset="UTF-8"/> (like for <hr/> and <br/> and add the meta element to the beginning of the head element to give your document an encoding.
The very least you need to know about html to do this assigment.
-
If you are familar with html, then this will all make sense to you. If you have
-never seen html before, this might be a bit intimidating, but you really don’t
-need to know much to do this assignment.
-
First of all, sample output from each step is provided. So all you really need
-to do is look at that, and make your code do the same thing. But it does help to
-know a little bit about what you are doing.
HTML is “Hyper Text Markup Language”. Hypertext, because it can contain links
-to other pages, and markup language means that text is “marked up” with
-instructions about how to format the text, etc.
Modern HTML is a particular dialect of XML (eXrensible Markup Language),
-which is itself a special case of SGML (Standard Generalized Markup Language)
-
It inherits from SGML a basic structure: each piece of the document is an element. each element is described by a “tag”. each tag has a different meaning, but they all have the same structure:
-
<some_tag> some content </some_tag>
-
-
-
that is, the tag name is surrounded by < and >, which marks the beginning of
-the element, and the end of the element is indicated by the same tag with a slash.
-
The real power is that these elements can be nested arbitrarily deep. In order to keep that all readable, we often want to indent the content inside the tags, so it’s clear what belongs with what. That is one of the tricky bits of this assignment.
<html> is the core tag indicating the entire document </html>
-
-<p> is a single paragraph of text </p>
-
-<body> is the tag that indicated the text of the document </body>
-
-<head> defines the header of the document -- a place for metadata </head>
-
In addition to the tag name and the content, extra attributes can be attached to a tag. These are added to the “opening tag”, with name=”something”, another_name=”somethign else” format:
There can be all sorts of stuff stored in attributes – some required for specific tags, some extra, like font sizes and colors. Note that since tags can essentially have any attributes, your code will need to support that – doesn’t it kind of look like a dict? And keyword arguments?
The general structure is everything is between and opening and closing tag. But some elements don’t really have content – just attributes. So the slash goes at the end of the tag, after the attributes. We can call these self-closing tags:
-
<metacharset="UTF-8"/>
-
-
-
To make a link, you use an “anchor” tag: <a>. It required attributes to indicate what the link is:
-
<ahref="http://google.com">link</a>
-
-
-
the href attribute is the link (hyper reference).
-
To make a bulleted list, you use a <ul> tag (unordered list), and inside that, you put individual list elements <li>:
-
<ulstyle="line-height:200%"id="TheList">
- <li>
- The first item in a list
- </li>
- <listyle="color: red">
- This is the second item
- </li>
-</ul>
-
-
-
Note that the list itself, and the list items can both take various attributes (all tags can...)
-
Section Headers are created with “h” tags: <h1> is the biggest (highest level), and there is <h2>, <h3>, etc. for sections, sub sections, subsub sections.
As a boy, one of my treats was go to the shops on a Saturday and spend part
-of my allowance on books; for a nine-year old, I had quite a collection of
-Tom Swift and Hardy Boys. Wouldn’t it be great to be able to create
-more and more of these classic books, to be able to generate a new Tom
-Swift adventure on demand?
-
OK, perhaps not. But that won’t stop us trying. I coded up a quick
-program to generate some swash-buckling scientific adventure on demand. It
-came up with:
-
-
... it was in the wind that was what he thought was his companion. I
-think would be a good one and accordingly the ship their situation
-improved. Slowly so slowly that it beat the band! You’d think no one
-was a low voice. “Don’t take any of the elements and the
-inventors of the little Frenchman in the enclosed car or cabin completely
-fitted up in front of the gas in the house and wringing her hands.
-“I’m sure they’ll fall!”
-
She looked up at them. He dug a mass of black vapor which it had
-refused to accept any. As for Mr. Swift as if it goes too high I’ll
-warn you and you can and swallow frequently. That will make the airship was
-shooting upward again and just before the raid wouldn’t have been
-instrumental in capturing the scoundrels right out of jail.”
-
-
Stylistically, it’s Victor Appleton meets Dylan Thomas. Technically,
-it’s all done with trigrams.
-
Trigram analysis is very simple. Look at each set of three adjacent words
-in a document. Use the first two words of the set as a key, and remember
-the fact that the third word followed that key. Once you’ve finished,
-you know the list of individual words that can follow each two word
-sequence in the document. For example, given the input:
This says that the words “I wish” are twice followed by the word
-“I”, the words “wish I” are followed once by
-“may” and once by “might” and so on.
-
To generate new text from this analysis, choose an arbitrary word pair as a
-starting point. Use these to look up a random next word (using the table
-above) and append this new word to the text so far. This now gives you a
-new word pair at the end of the text, so look up a potential next word
-based on these. Add this to the list, and so on. In the previous example,
-we could start with “I may”. The only possible next word is
-“I”, so now we have:
-
I may I
-
-
-
The last two words are “may I”, so the next word is
-“wish”. We then look up “I wish”, and find our choice
-is constrained to another “I”.:
-
I may I wish I
-
-
-
Now we look up “wish I”, and find we have a choice. Let’s
-choose “may”:
-
I may I wish I may
-
-
-
Now we’re back where we started from, with “I may.”
-Following the same sequence, but choosing “might” this time, we
-get:
-
I may I wish I may I wish I might
-
-
-
At this point we stop, as no sequence starts “I might.”
-
Given a short input text, the algorithm isn’t too interesting. Feed
-it a book, however, and you give it more options, so the resulting output
-can be surprising.
-
For this kata, try implementing a trigram algorithm that generates a couple
-of hundred words of text using a book-sized file as input.
-Project Gutenberg is a good source of online
-books (Tom Swift and His Airship is here.)
-
Be warned that these files have DOS line endings (carriage return followed by
-newline).
Kata’s are about trying something many times. In this one, what
-we’re experimenting with is not just the code, but the heuristics of
-processing the text. What do we do with punctuation? Paragraphs? Do we have
-to implement backtracking if we chose a next word that turns out to be a
-dead end?
-
I’ll fire the signal and the fun will commence...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/slides_sources/ToDo.txt b/slides_sources/ToDo.txt
index 0d9e8636..e51851c8 100644
--- a/slides_sources/ToDo.txt
+++ b/slides_sources/ToDo.txt
@@ -1,8 +1,15 @@
Things to do for the UWPCE Intro to Python class:
+Notes about homework:
+
+" ".join(dict.keys())
+
+no need to make a list first.
+
+NEED to do more with iterators vs iterables vs sequences.
+
Future Sessions:
-Sorting!
add pathlib examples
diff --git a/slides_sources/build_gh_pages.sh b/slides_sources/build_gh_pages.sh
index a29edd82..6eccd8fb 100755
--- a/slides_sources/build_gh_pages.sh
+++ b/slides_sources/build_gh_pages.sh
@@ -3,14 +3,26 @@
# simple script to build and push to gh-pages
# designed to be run from master
+# To use this script you need another copy of the repo, right next this
+# one, but named "IntroToPython.gh-pages"
+# this script builds the docs, then copies them to the other repo
+# then pushes that to gitHub
+
+GHPAGESDIR=../../IntroToPython.gh-pages/
+
+# make sure the Gh pages repo is there and in the right branch
+pushd $GHPAGESDIR
+git checkout gh-pages
+popd
+
# make the docs
make html
-
# copy to other repo (on the gh-pages branch)
-cp -R build/html/ ../../IntroToPython.gh-pages
+cp -R build/html/ $GHPAGESDIR
-cd ../../IntroToPython.gh-pages
-git checkout gh-pages
+pushd $GHPAGESDIR
git add * # in case there are new files added
git commit -a -m "updating presentation materials"
+git pull -s ours --no-edit
git push
+
diff --git a/slides_sources/source/conf.py b/slides_sources/source/conf.py
index db0a95c6..ace14544 100644
--- a/slides_sources/source/conf.py
+++ b/slides_sources/source/conf.py
@@ -35,12 +35,16 @@
'sphinx.ext.todo',
'sphinx.ext.coverage',
# 'sphinx.ext.pngmath',
- #'sphinx.ext.mathjax',
+ 'sphinx.ext.mathjax',
+ #'sphinx.ext.jsmath',
'sphinx.ext.ifconfig',
'IPython.sphinxext.ipython_console_highlighting',
'IPython.sphinxext.ipython_directive',
]
+# this doesn't work.
+jsmath_path = "../../../jsMath-3.6e/easy/load.js" # needed for jsmath
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
diff --git a/slides_sources/source/exercises/args_kwargs_lab.rst b/slides_sources/source/exercises/args_kwargs_lab.rst
new file mode 100644
index 00000000..2adffc00
--- /dev/null
+++ b/slides_sources/source/exercises/args_kwargs_lab.rst
@@ -0,0 +1,71 @@
+.. _exercise_args_kwargs_lab:
+
+*******************
+args and kwargs Lab
+*******************
+
+Learning about ``args`` and ``kwargs``
+======================================
+
+Goal:
+-----
+
+Develop an understanding of using advanced argument passing and parameter definitons.
+
+If this is all confusing -- you may want to review this:
+
+http://stupidpythonideas.blogspot.com/2013/08/arguments-and-parameters.html
+
+Procedure
+---------
+
+**Keyword arguments:**
+
+* Write a function that has four optional parameters (with defaults):
+
+ - `fore_color`
+ - `back_color`
+ - `link_color`
+ - `visited_color`
+
+* Have it print the colors (use strings for the colors)
+
+* Call it with a couple different parameters set
+
+ - using just positional arguments:
+
+ - ``func('red', 'blue', 'yellow', 'chartreuse')``
+
+ - using just keyword arguments:
+
+ - ``func(link_color='red', back_color='blue')``
+
+ - using a combination of positional and keyword
+
+ - ````func('purple', link_color='red', back_color='blue')``
+
+ - using ``*some_tuple`` and/or ``**some_dict``
+
+ - ``regular = ('red', 'blue')``
+
+ - ``links = {'link_color': 'chartreuse'}``
+
+ - ``func(*regular, *links)``
+
+.. nextslide::
+
+**Generic parameters:**
+
+* Write a the same function with the parameters as:
+
+``*args`` and ``**kwags``
+
+* Have it print the colors (use strings for the colors)
+
+* Call it with the same various combinations of arguments used above.
+
+* Also have it print `args` and `kwargs` directly, so you can be sure you understand what's going on.
+
+* Note that in general, you can't know what will get passed into ``**kwargs`` So maybe adapt your function to be able to do something reasonable with any keywords.
+
+
diff --git a/slides_sources/source/exercises/circle_class.rst b/slides_sources/source/exercises/circle_class.rst
index a8c3edd2..51a3f8ee 100644
--- a/slides_sources/source/exercises/circle_class.rst
+++ b/slides_sources/source/exercises/circle_class.rst
@@ -26,9 +26,7 @@ Other abilities of a Circle instance:
.. nextslide::
-This exercise should use "new style classes" i.e. inherit from ``object``
-
-You will also use:
+You will use:
- properties
- a classmethod
diff --git a/slides_sources/source/exercises/comprehensions_lab.rst b/slides_sources/source/exercises/comprehensions_lab.rst
new file mode 100644
index 00000000..fb9152e5
--- /dev/null
+++ b/slides_sources/source/exercises/comprehensions_lab.rst
@@ -0,0 +1,250 @@
+.. _exercise_comprehensions:
+
+******************
+Comprehensions Lab
+******************
+
+Playing with Comprehensions
+============================
+
+
+.. rst-class:: large left
+
+ Goal:
+
+.. rst-class:: medium left
+
+ Getting Familiar with list, set and dict comprehensions
+
+
+List comprehensions
+--------------------
+
+Note: this is a bit of a "backwards" exercise --
+we show you code, you figure out what it does.
+
+As a result, not much to submit -- don't worry about it -- you'll have
+a chance to use these in other exercises.
+
+.. code-block:: python
+
+ >>> feast = ['lambs', 'sloths', 'orangutans',
+ 'breakfast cereals', 'fruit bats']
+
+ >>> comprehension = [delicacy.capitalize() for delicacy in feast]
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> comprehension[0]
+ ???
+
+ >>> comprehension[2]
+ ???
+
+(figure it out before you try it)
+
+Filtering lists with list comprehensions
+----------------------------------------
+
+.. code-block:: python
+
+ >>> feast = ['spam', 'sloths', 'orangutans', 'breakfast cereals',
+ 'fruit bats']
+
+ >>> comp = [delicacy for delicacy in feast if len(delicacy) > 6]
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> len(feast)
+ ???
+
+ >>> len(comp)
+ ???
+
+(figure it out first!)
+
+
+Unpacking tuples in list comprehensions
+---------------------------------------
+
+.. code-block:: python
+
+ >>> list_of_tuples = [(1, 'lumberjack'), (2, 'inquisition'), (4, 'spam')]
+
+ >>> comprehension = [ skit * number for number, skit in list_of_tuples ]
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> comprehension[0]
+ ???
+
+ >>> len(comprehension[2])
+ ???
+
+Double list comprehensions
+---------------------------
+.. code-block:: python
+
+ >>> eggs = ['poached egg', 'fried egg']
+
+ >>> meats = ['lite spam', 'ham spam', 'fried spam']
+
+ >>> comprehension = \
+ [ '{0} and {1}'.format(egg, meat) for egg in eggs for meat in meats]
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> len(comprehension)
+ ???
+
+ >>> comprehension[0]
+ ???
+
+Set comprehensions
+------------------
+
+.. code-block:: python
+
+ >>> comprehension = { x for x in 'aabbbcccc'}
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> comprehension
+ ???
+
+Dictionary comprehensions
+-------------------------
+
+.. code-block:: python
+
+ >>> dict_of_weapons = {'first': 'fear',
+ 'second': 'surprise',
+ 'third':'ruthless efficiency',
+ 'forth':'fanatical devotion',
+ 'fifth': None}
+ >>> dict_comprehension = \
+ { k.upper(): weapon for k, weapon in dict_of_weapons.items() if weapon}
+
+What is the output of:
+
+.. code-block:: python
+
+ >>> 'first' in dict_comprehension
+ ???
+ >>> 'FIRST' in dict_comprehension
+ ???
+ >>> len(dict_of_weapons)
+ ???
+ >>> len(dict_comprehension)
+ ???
+
+Other resources
+---------------
+
+
+See also:
+
+https://github.com/gregmalcolm/python_koans
+
+Specifically (for comprehensions):
+
+https://github.com/gregmalcolm/python_koans/blob/master/python3/koans/about_comprehension.py
+
+
+Count Even Numbers
+------------------
+
+This is from CodingBat "count_evens" (http://codingbat.com/prob/p189616)
+
+*Using a list comprehension*, return the number of even integers in the given array.
+
+Note: the % "mod" operator computes the remainder, e.g. ``5 % 2`` is 1.
+
+.. code-block:: python
+
+ count_evens([2, 1, 2, 3, 4]) == 3
+
+ count_evens([2, 2, 0]) == 3
+
+ count_evens([1, 3, 5]) == 0
+
+
+.. code-block:: python
+
+ def count_evens(nums):
+ one_line_comprehension_here
+
+
+``dict`` and ``set`` comprehensions
+------------------------------------
+
+Revisiting the dict/set lab -- see how much you can do with
+comprehensions instead.
+
+(:ref:`exercise_dict_lab`)
+
+Specifically, look at these:
+
+First a slightly bigger, more interesting (or at least bigger..) dict:
+
+.. code-block:: python
+
+ food_prefs = {"name": "Chris",
+ "city": "Seattle",
+ "cake": "chocolate",
+ "fruit": "mango",
+ "salad": "greek",
+ "pasta": "lasagna"}
+
+.. nextslide:: Working with this dict:
+
+1. Print the dict by passing it to a string format method, so that you
+get something like:
+
+ "Chris is from Seattle, and he likes chocolate cake, mango fruit,
+ greek salad, and lasagna pasta"
+
+2. Using a list comprehension, build a dictionary of numbers from zero
+to fifteen and the hexadecimal equivalent (string is fine).
+(the ``hex()`` function gives you the hexidecimal representation of a number.)
+
+3. Do the previous entirely with a dict comprehension -- should be a one-liner
+
+4. Using the dictionary from item 1: Make a dictionary using the same
+keys but with the number of 'a's in each value. You can do this either
+by editing the dict in place, or making a new one. If you edit in place,
+make a copy first!
+
+.. nextslide::
+
+5. Create sets s2, s3 and s4 that contain numbers from zero through twenty,
+divisible 2, 3 and 4.
+
+ a. Do this with one set comprehension for each set.
+
+ b. What if you had a lot more than 3? -- Don't Repeat Yourself (DRY).
+
+ - create a sequence that holds all the divisors you might want --
+ could be 2,3,4, or could be any other arbitrary divisors.
+
+ - loop through that sequence to build the sets up -- so no repeated code.
+ you will end up with a list of sets -- one set for each divisor in your
+ sequence.
+
+ - The idea here is that when you see three (Or more!) lines of code that
+ are almost identical, then you you want to find a way to generalize
+ that code and have it act on a set of inputs, so the actual code is
+ only written once.
+
+ c. Extra credit: do it all as a one-liner by nesting a set comprehension
+ inside a list comprehension. (OK, that may be getting carried away!)
diff --git a/slides_sources/source/exercises/dict_lab.rst b/slides_sources/source/exercises/dict_lab.rst
new file mode 100644
index 00000000..fe4ce49c
--- /dev/null
+++ b/slides_sources/source/exercises/dict_lab.rst
@@ -0,0 +1,89 @@
+.. _exercise_dict_lab:
+
+**********************
+Dictionary and Set Lab
+**********************
+
+Learning about dictionaries and sets
+====================================
+
+Goal:
+-----
+
+Learn the basic ins and outs of Python dictionaries and sets.
+
+Procedure
+---------
+
+In your student dir in the IntroPython2015 repo, create a ``session04`` dir and put in a new ``dict_lab.py`` file.
+
+The file should be an executable python script. That is to say that one
+should be able to run the script directly like so:
+
+.. code-block:: bash
+
+ $ ./dict_lab.py
+
+(At least on OS-X and Linux)
+
+-- you do that with this command:
+
+.. code-block:: bash
+
+ $ chmod +x dict_lab.py
+
+(The +x means make this executable)
+
+.. nextslide::
+
+Add the file to your clone of the repository and commit changes frequently
+while working on the following tasks. When you are done, push your changes to
+GitHub and issue a pull request.
+
+(if you are struggling with git -- just write the code for now)
+
+When the script is run, it should accomplish the following four series of
+actions:
+
+.. nextslide:: Dictionaries 1
+
+* Create a dictionary containing "name", "city", and "cake" for "Chris" from "Seattle" who likes "Chocolate".
+
+* Display the dictionary.
+
+* Delete the entry for "cake".
+
+* Display the dictionary.
+
+* Add an entry for "fruit" with "Mango" and display the dictionary.
+
+ - Display the dictionary keys.
+ - Display the dictionary values.
+ - Display whether or not "cake" is a key in the dictionary (i.e. False) (now).
+ - Display whether or not "Mango" is a value in the dictionary (i.e. True).
+
+
+.. nextslide:: Dictionaries 2
+
+
+* Using the dictionary from item 1: Make a dictionary using the same keys but
+ with the number of 't's in each value as the value. (upper and lower case?).
+
+.. nextslide:: Sets
+
+* Create sets s2, s3 and s4 that contain numbers from zero through twenty,
+ divisible 2, 3 and 4.
+
+* Display the sets.
+
+* Display if s3 is a subset of s2 (False)
+
+* and if s4 is a subset of s2 (True).
+
+.. nextslide:: Sets 2
+
+* Create a set with the letters in 'Python' and add 'i' to the set.
+
+* Create a frozenset with the letters in 'marathon'
+
+* display the union and intersection of the two sets.
diff --git a/slides_sources/source/exercises/exceptions_lab.rst b/slides_sources/source/exercises/exceptions_lab.rst
new file mode 100644
index 00000000..5ff5b16b
--- /dev/null
+++ b/slides_sources/source/exercises/exceptions_lab.rst
@@ -0,0 +1,25 @@
+.. _exercise_exceptions_lab:
+
+**************
+Exceptions Lab
+**************
+
+Learning Exceptions
+===================
+
+Just a little bit for the basics.
+
+Exceptions Lab
+---------------
+
+Improving ``input``
+
+* The ``input()`` function can generate two exceptions: ``EOFError``
+ or ``KeyboardInterrupt`` on end-of-file(EOF) or canceled input.
+
+* Create a wrapper function, perhaps ``safe_input()`` that returns ``None``
+ rather rather than raising these exceptions, when the user enters ``^C`` for Keyboard Interrupt, or ``^D`` (``^Z`` on Windows) for End Of File.
+
+* Update your mailroom program to use exceptions (and IBAFP) to handle
+ malformed numeric input
+
diff --git a/slides_sources/source/exercises/fib_and_lucas.rst b/slides_sources/source/exercises/fib_and_lucas.rst
index 0181536c..86343f60 100644
--- a/slides_sources/source/exercises/fib_and_lucas.rst
+++ b/slides_sources/source/exercises/fib_and_lucas.rst
@@ -73,6 +73,14 @@ Calling this function with no optional parameters will produce numbers from the
produce values from the *lucas numbers*. Other values for the optional
parameters will produce other series.
+**Note:** While you *could* check the input arguments, and then call one
+of the functions you wrote, the idea of this exercise is to make a general
+function, rather than one specialized. So you should re-impliment the code
+in this function.
+
+In fact, you could go back and re-impliment your fibonacci and lucas
+functions to call this one with particular arguments.
+
Ensure that your function has a well-formed ``docstring``
Tests...
diff --git a/slides_sources/source/exercises/file_lab.rst b/slides_sources/source/exercises/file_lab.rst
new file mode 100644
index 00000000..44b3f656
--- /dev/null
+++ b/slides_sources/source/exercises/file_lab.rst
@@ -0,0 +1,55 @@
+.. _exercise_file_lab:
+
+********
+File LAB
+********
+
+A bit of practice with files
+============================
+
+Goal:
+-----
+
+Get a little bit of practice with handling files and parsing simple text.
+
+
+Paths and File Processing
+--------------------------
+
+* write a program which prints the full path to all files in the current
+ directory, one per line
+
+* write a program which copies a file from a source, to a destination
+ (without using shutil, or the OS copy command)
+
+ - advanced: make it work for any size file: i.e. don't read the entire
+ contents of the file into memory at once.
+
+ - This should work for any kind of file, so you need to open
+ the files in binary mode: ``open(filename, 'rb')`` (or ``'wb'`` for
+ writing). Note that for binary files, you can't use ``readline()`` --
+ lines don't have any meaning for binary files.
+
+ - Test it with both text and binrary files (maybe jpeg or??)
+
+
+File reading and parsing
+------------------------
+
+
+In the class repo, in:
+
+``Examples/Session01/students.txt``
+
+You will find the list I generated in the first class of all the students in the class, and what programming languages they have used in the past.
+
+Write a little script that reads that file, and generates a list of all
+the languages that have been used.
+
+Extra credit: keep track of how many students specified each language.
+
+If you've got git set up right, ``git pull upstream master`` should update
+your repo. Otherwise, you can get it from gitHub:
+
+https://github.com/UWPCE-PythonCert/IntroPython2016/blob/master/Examples/Session01/students.txt
+
diff --git a/slides_sources/source/exercises/fizz_buzz.rst b/slides_sources/source/exercises/fizz_buzz.rst
index 63409276..d6f19fe1 100644
--- a/slides_sources/source/exercises/fizz_buzz.rst
+++ b/slides_sources/source/exercises/fizz_buzz.rst
@@ -17,7 +17,7 @@ The Classic Fizz Buzz Problem
(http://c2.com/cgi/wiki?FizzBuzzTest)
- Now that I've psyced you out -- it's really pretty straightforward.
+ Now that I've psyched you out -- it's really pretty straightforward.
Goal:
-----
@@ -35,11 +35,35 @@ Hint:
* Look up the ``%`` operator. What do these do?
- * ``10 % 7 == 3``
- * ``14 % 7 == 0``
+ * ``10 % 7``
+ * ``14 % 7``
(try that in iPython)
-* Do try to write it without looking it up -- there are a million nifty solutions posted on the web.
+* **Do** try to write a solution *before* looking it up -- there are a million nifty solutions posted on the web, but you'll learn a lot more if you figure it out on your own first.
+
+Results:
+--------
+
+Running your code should result in something like::
+
+ 1
+ 2
+ Fizz
+ 4
+ Buzz
+ Fizz
+ 7
+ 8
+ Fizz
+ Buzz
+ 11
+ Fizz
+ 13
+ 14
+ FizzBuzz
+ 16
+ ....
+
diff --git a/slides_sources/source/exercises/grid_printer.rst b/slides_sources/source/exercises/grid_printer.rst
index 42e3ea17..41e43f57 100644
--- a/slides_sources/source/exercises/grid_printer.rst
+++ b/slides_sources/source/exercises/grid_printer.rst
@@ -122,13 +122,44 @@ Make it a function
One of the points of writing functions is so you can write code that does similar things, but customized to input parameters. So what if we want to be able to print that grid at an arbitrary size?
Write a function ``print_grid(n)`` that takes one integer argument
-and prints a grid like the picture above, BUT the size of the
+and prints a grid just like before, BUT the size of the
grid is given by the argument.
For example, ``print_grid(11)`` prints the grid in the above picture.
-This problem is underspecified. Do something reasonable.
+``print_grid(3)`` would print a smaller grid::
+
+ + - + - +
+ | | |
+ + - + - +
+ | | |
+ + - + - +
+
+.. nextslide::
+
+``print_grid(15)`` prints a larger grid::
+
+ + - - - - - - - + - - - - - - - +
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ + - - - - - - - + - - - - - - - +
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ | | |
+ + - - - - - - - + - - - - - - - +
+
+.. nextslide::
+This problem is underspecified. Do something reasonable.
Part 3:
=======
@@ -138,8 +169,54 @@ Even more general...
A function with two parameters
-------------------------------
-Write a function that draws a similar grid with three rows and three columns.
+Write a function that draws a similar grid with a specified number of rows and columns, and each cell a given size.
+
+for example, ``print_grid2(3,4)`` results in::
+
+ + - - - - + - - - - + - - - - +
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ + - - - - + - - - - + - - - - +
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ + - - - - + - - - - + - - - - +
+ | | | |
+ | | | |
+ | | | |
+ | | | |
+ + - - - - + - - - - + - - - - +
+
+.. nextslide::
+
+What to do about rounding? -- you decide.
+
+Another example: ``print_grid2(5,3)``::
+
+ + - - - + - - - + - - - + - - - + - - - +
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ + - - - + - - - + - - - + - - - + - - - +
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ + - - - + - - - + - - - + - - - + - - - +
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ + - - - + - - - + - - - + - - - + - - - +
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ + - - - + - - - + - - - + - - - + - - - +
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ + - - - + - - - + - - - + - - - + - - - +
+
-(what to do about rounding?)
-And while you are at it -- n rows and columns...
diff --git a/slides_sources/source/exercises/html_renderer.rst b/slides_sources/source/exercises/html_renderer.rst
index 8074e47b..d38dc6e0 100644
--- a/slides_sources/source/exercises/html_renderer.rst
+++ b/slides_sources/source/exercises/html_renderer.rst
@@ -1,38 +1,66 @@
.. _exercise_html_renderer:
-=======================
+======================
HTML Renderer Exercise
-=======================
+======================
HTML Renderer
=============
+Ever need to generate some HTML?
+
+And not want to write all those tags yourself?
+
Goal:
------
-The goal is to create a set of classes to render html pages -- in a "pretty printed" way. i.e nicely indented and human readable. We'll try to get to all the features required to render:
+The goal is to create a set of classes to render html pages -- in a "pretty printed" way.
+
+i.e. nicely indented and human readable.
+
+We'll try to get to all the features required to render:
:download:`sample_html.html <./sample_html.html>`
+Take a look at it with "view source" in your browser -- or open in a text editor -- it's also in the Examples dir.
+
+If you don't know html -- just look at the example and copy that....
+
The exercise is broken down into a number of steps -- each requiring a few more OO concepts in Python.
General Instructions:
---------------------
-For each step, add the required functionality. There is example code to run your code for each step in: ``code\session06\run_html_render.py``
+For each step, add the required functionality. There is example code to run your code for each step in: ``Examples\session07\run_html_render.py``
-name your file: ``html_render.py`` -- so it can be imported by ``run_html_render.py``
+Name your file: ``html_render.py`` -- so it can be imported by ``run_html_render.py``
You should be able to run that code at each step, uncommenting each new step in ``run_html_render.py`` as you go.
-It builds up a html tree, and then calls the ``render()`` method of your element to render the page.
+It builds up an html tree, and then calls the ``render()`` method of your element to render the page.
-It uses a ``cStringIO`` object (like a file, but in memory) to render to memory, then dumps it to the console, and writes a file. Take a look at the code at the end to make sure you understand it.
+It uses a ``StringIO`` object (like a file, but in memory) to render to memory, then dumps it to the console, and writes a file. Take a look at the code at the end to make sure you understand it.
-The html generated at each step is in the files: ``test_html_ouput?.html``
+The html generated at each step will be in the files: ``test_html_ouput?.html``
At each step, your results should look similar that those (maybe not identical...)
+Unit tests
+------------
+
+Use "test driven development":
+
+In addition to checking if the output is what you expect with the running script -- you should also write unit tests as you go.
+
+Each new line of code should have a test that will run it -- *before* you write that code.
+
+That is:
+
+ 1. write a test that exercises the next step in your process
+ 2. run the tests -- the new test will fail
+ 3. write your code...
+ 4. run the tests. If it still fails, go back to step 3...
+
Step 1:
-------
@@ -41,29 +69,39 @@ Create an ``Element`` class for rendering an html element (xml element).
It should have class attributes for the tag name ("html" first) and the indentation (spaces to indent for pretty printing)
-The constructor signature should look like
+The initializer signature should look like
.. code-block:: python
Element(content=None)
-where ``content`` is a string
+where ``content`` is expected to be a string
It should have an ``append`` method that can add another string to the content.
+So your class will need a way to store the content in a way that you can keep adding more to it.
+
+.. nextslide::
+
It should have a ``render(file_out, ind = "")`` method that renders the tag and the strings in the content.
``file_out`` could be any file-like object ( i.e. have a ``write()`` method ).
-.. nextslide::
-
``ind`` is a string with the indentation level in it: the amount that the tag should be indented for pretty printing.
- This is a little tricky: ``ind`` will be the amount that this element should be indented already. It will be from zero (an empty string) to a lot of spaces, depending on how deep it is in the tree.
-The amount of indentation should be set by the class attribute: ``indent``
+The amount of each level of indentation should be set by the class attribute: ``indent``
-NOTE: don't worry too much about indentation at this stage -- the primary goal is to get proper, compliant html. i.e. the opening and closing tags rendered correctly. Worry about cleaning up the indentation once you've got that working.
+NOTE: don't worry too much about indentation at this stage -- the primary goal is to get proper, compliant html. i.e. the opening and closing tags rendered correctly. Worry about cleaning up the indentation once you've got that working. See "Note on indentation" below for more explaination.
+
+.. nextslide::
+
+So this ``render()`` method takes a file-like object, and calls its ``write()`` method, writing the html for a tag. Something like::
+
+
+ Some content. Some more content.
+ <\html>
You should now be able to render an html tag with text in it as contents.
@@ -72,25 +110,26 @@ See: step 1. in ``run_html_render.py``
Step 2:
--------
-Create a couple subclasses of ``Element``, for a ``html``, ````, and ``
`` tag. All you should have to do is override the ``tag`` class attribute (you may need to add a ``tag`` class attribute to the Element class first...).
+Create a couple subclasses of ``Element``, for each of ````, ``
``, and ``
`` tags. All you should have to do is override the ``tag`` class attribute (you may need to add a ``tag`` class attribute to the ``Element`` class first, if you haven't already).
Now you can render a few different types of element.
-Extend the ``Element.render()`` method so that it can render other elements inside the tag in addition to strings. Simple recursion should do it. i.e. it can call the ``render()`` method of the elements it contains. You'll need to be smart about setting the ``ind`` optional parameter -- so that the nested elements get indented correctly.
+Extend the ``Element.render()`` method so that it can render other elements inside the tag in addition to strings. Simple recursion should do it. i.e. it can call the ``render()`` method of the elements it contains. You'll need to be smart about setting the ``ind`` optional parameter -- so that the nested elements get indented correctly. (again, this is a secondary concern...)
-Figure out a way to deal with the fact that the contained elements could be either simple strings or ``Element`` s with render methods (there are a few ways to handle that...).
+Figure out a way to deal with the fact that the contained elements could be either simple strings or ``Element`` s with render methods (there are a few ways to handle that...). Think about "Duck Typing" and EAFP. See the section 'Notes on handling "duck typing"' and the end of the Exercise for more.
-You should now be able to render a basic web page with an html tag around
-the whole thing, a ``
`` tag inside, and multiple ``
`` tags inside that, with text inside that. And all indented nicely.
+.. nextslide::
+
+You should now be able to render a basic web page with an ```` tag around the whole thing, a ``
`` tag inside, and multiple ``
`` tags inside that, with text inside that. And all indented nicely.
See ``test_html_output2.html``
-NOTE: when you run step 2 in ``run_html_render.py``, you will want o comment out step 1 -- that way you'll only get one set of output.
+NOTE: when you run step 2 in ``run_html_render.py``, you will want to comment out step 1 -- that way you'll only get one set of output.
Step 3:
--------
-Create a ``
`` element -- simple subclass.
+Create a ```` element -- a simple subclass.
Create a ``OneLineTag`` subclass of ``Element``:
@@ -109,13 +148,13 @@ Step 4:
--------
Extend the ``Element`` class to accept a set of attributes as keywords to the
-constructor, ie. (``run_html_render.py``)
+constructor, e.g. ``run_html_render.py``
.. code-block:: python
Element("some text content", id="TheList", style="line-height:200%")
-( remember ``**kwargs``? )
+html elements can take essentially any attributes -- so you can't hard-code these particular ones. ( remember ``**kwargs``? )
The render method will need to be extended to render the attributes properly.
@@ -123,6 +162,42 @@ You can now render some ``
`` tags (and others) with attributes
See ``test_html_output4.html``
+.. nextslide:: the "class" attribute.
+
+NOTE: if you do "proper" CSS+html, then you wouldn't specify style directly in element attributes.
+
+Rather you would set the "class" attribute::
+
+
+ This is my recipe for making curry purely with chocolate
+
+
+However, if you try this as a keywork argument in Python:
+
+.. code-block:: ipython
+
+ In [1]: P("some content", class="intro")
+ File "", line 1
+ P("some content", class="intro")
+ ^
+ SyntaxError: invalid syntax
+
+Huh?
+
+"class" is a reserved work in Python -- for making classes.
+So it can't be used as a keywork argument.
+
+But it's a fine key in a dict, so you can put it in a dict, and pass it in with ``**``:
+
+.. code-block:: python
+
+ attrs = {'class': 'intro'}
+ P("some content", **attrs)
+
+You could also special-case this in your code -- so your users could use "clas"
+with one s, and you could tranlate it in the generated html.
+
+
Step 5:
--------
@@ -135,20 +210,24 @@ attributes, if any.
Create a couple subclasses of ``SelfClosingTag`` for and and
+Note that you now have a couple render methods -- is there repeated code in them?
+
+Can you refactor the common parts into a separate method that all the render methods can call? And do all your tests still pass (you do have tests for everything, don't you?) after refactoring?
+
See ``test_html_output5.html``
Step 6:
-------
-Create a ``A`` class for an anchor (link) element. Its constructor should look like::
+Create an ``A`` class for an anchor (link) element. Its constructor should look like::
A(self, link, content)
-where link is the link, and content is what you see. It can be called like so::
+where ``link`` is the link, and ``content`` is what you see. It can be called like so::
- A(u"http://google.com/", u"link to google")
+ A("http://google.com/", "link to google")
-You should be able to subclass from ``Element``, and only override the ``__init__`` --- Calling the ``Element`` ``__init__`` from the ``A __init__``
+You should be able to subclass from ``Element``, and only override the ``__init__`` --- calling the ``Element`` ``__init__`` from the ``A __init__``
You can now add a link to your web page.
@@ -192,21 +271,197 @@ new tags, etc....
See ``test_html_output8.html``
+Note on indentation
+===================
+
+Indentation is not stricly required for html -- html ignores most whitespace.
+
+But it can make it much easier to read for humans, and it's a nice excercise to see how one might make it nice.
+
+There is also more than one way to indent html -- so you have a bit of flexibility here.
+
+So:
+
+* You probably ``ind`` to be an optional argument to render -- so it will not indent if nothing is passed in. And that lets you write the code without indentation first if you like.
+
+* But ultimately, you want your code to USE the ind parameter -- it is supposed to indicate how much this entire tag is already indented.
+
+* When this one gets rendered, you don't know where it is in a potentially deeply nested hierarchy -- it could be at the top level or ten levels deep. passing ``ind`` into the render method is how this is communicated.
+
+* You have (at least) two options for how to indicate level of indentation:
+
+ - It could be a integer indicating number of levels of indentation
+ - It could, more simply, be a bunch of spaces.
+
+* You want to have the amount of spaces per indentation defined as a class attribute of the base class (the ``Element`` class). That way, you could change it in one place, and it would change everywhere an remain consistent.
+
+
+
+Notes on handling "duck typing"
+===============================
+
+.. rst-class:: left
+
+ In this exercise, we need to deal with the fact that XML (and thus HTML) allows *either* plain text *or* other tags to be the content of a tag. Our code also needs to handle the fact that there are two possible types that we need to be able to render.
+
+ There are two primary ways to address this (and multiple ways to actually write the code for each of these).
+
+ 1) Make sure that the content only has renderable objects in it.
+
+ 2) Make sure the render() method can handle either type on the fly
+
+ The difference is where you handle the multiple types -- in the render method itself, or ahead of time.
+
+The ahead of time option:
+-------------------------
+
+You can handle it ahead of time by creating a simple object that wraps a string and gives it a render method. As simple as:
+
+.. code-block:: python
+
+ class TextWrapper:
+ """
+ A simple wrapper that creates a class with a render method
+ for simple text
+ """
+ def __init__(self, text):
+ self.text = text
+
+ def render(self, file_out, current_ind=""):
+ file_out.write(current_ind)
+ file_out.write(self.text)
+
+.. nextslide::
+
+You could require your users to use the wrapper, so instead of just appending a string, they would do:
+
+.. code-block:: python
+
+ an_element.append(TextWRapper("the string they want to add"))
+
+But this is not very Pythonic style -- it's OO heavy. Strings for text are so common you want to be able to simply use them:
+
+.. code-block:: python
+
+ an_element.append("the string they want to add")
+
+So much easier.
+
+To accomplish this, you can update the ``append()`` method to put this wrapper around plain strings when something new is added.
+
+
+Checking if it's the right type
+-------------------------------
+
+How do you decide if the wrapper is required?
+
+**Checking it it's an instance of Element:**
+
+You could check and see if the object being appended is an Element:
+
+.. code-block:: python
+
+ if isinstance(content, Element):
+ self.content.append(content)
+ else:
+ self.content.append(TextWrapper(content))
+
+This would work well, but closes the door to using any other type that may not be a strict subclsss of Element, but can render itself. Not too bad in this case, but in general, frowned upon in Python.
+
+.. nextslide::
+
+Alternatively, you could check for the string type:
+
+.. code-block:: python
+
+ if isinstance(content, str):
+ self.content.append(TextWrapper(content))
+ else:
+ self.content.append(content)
+
+I think this is a little better -- strings are a pretty core type in python, it's not likely that anyone is going to need to use a "string-like" object.
+
+Duck Typing
+-----------
+
+The Python model of duck typing is: If quacks like a duck, then treat it like a duck.
+
+But in this case, we're not actually rendering the object at this stage, so calling the method isn't appropriate.
+
+**Checking for an attribute**
+
+Instead of calling the method, see if it's there. You can do that with ``hasattr()``
+
+check if the passed-in object has a ``render()`` attribute:
+
+.. code-block:: python
+
+ if hasattr(content, 'render'):
+ self.content.append(content)
+ else:
+ self.content.append(TextWrapper(str(content))
+
+
+Note that I added a ``str()`` call too -- so you can pass in anything -- it will get stringified -- this will be ugly for many objects, but will work fine for numbers and other simple objects.
+
+This is my favorite. ``html_render_wrap.py`` in Solutions demonstrates some core bits of this approach.
+
+
+Duck Typing on the Fly
+----------------------
+
+The other option is to simply put both elements and text in the content list, and figure out what to do in the ``render()`` method.
+
+Again, you could type check -- but I prefer the duck typing approach, and EAFP:
+
+.. code-block:: python
+
+ try:
+ content.render(out_file)
+ except AttributeError:
+ outfile.write(content)
+
+If content is a simple string then it won't have a render method, and an ``AttributeError`` will be raised.
+
+You can catch that, and simply write the content directly instead.
+
+.. nextslide::
+
+You may want to turn it into a string, first::
+
+ outfile.write(str(content))
+
+Then you could write just about anything -- numbers, etc.
+
+
+Where did the Exception come from?
+----------------------------------
+
+**Caution**
+
+If the object doesn't have a ``render`` method, then an AttributeError will be raised. But what if it does have a render method, but that method is broken?
+
+Depending on what's broken, it could raise any number of exceptions. Most will not get caught by the except clause, and will halt the program.
+
+But if, just by bad luck, it has an bug that raises an ``AttributeError`` -- then this could catch it, and try to simply write it out instead. So you may get something like: ```` in the middle of your html.
+
+**The beauty of testing**
+
+If you have a unit test that calls every render method in your code -- then it should catch that error, and in the unit test it will be clear where it is coming from.
+
HTML Primer
============
.. rst-class:: medium
- The very least you need to know about html to do this assigment.
+ The very least you need to know about html to do this assignment.
-If you are familar with html, then this will all make sense to you. If you have
-never seen html before, this might be a bit intimidating, but you really don't
-need to know much to do this assignment.
+.. rst-class:: left
-First of all, sample output from each step is provided. So all you really need
-to do is look at that, and make your code do the same thing. But it does help to
-know a little bit about what you are doing.
+ If you are familiar with html, then this will all make sense to you. If you have never seen html before, this might be a bit intimidating, but you really don't need to know much to do this assignment.
+
+ First of all, sample output from each step is provided. So all you really need to do is look at that, and make your code do the same thing. But it does help understand a little bit about what you trying to do.
HTML
----
@@ -221,23 +476,28 @@ http://www.w3schools.com/html/html_basic.asp
And there are countless others online.
+As html is XML -- the XML intro is a good source of the XML syntax, too:
+
+http://www.w3schools.com/xml/default.asp
+
But here is a tiny intro of just what you need to know for this project.
Elements
--------
-Modern HTML is a particular dialect of XML (eXrensible Markup Language),
+Modern HTML is a particular dialect of XML (eXtensible Markup Language),
which is itself a special case of SGML (Standard Generalized Markup Language)
-It inherits from SGML a basic structure: each piece of the document is an element. each element is described by a "tag". each tag has a different meaning, but they all have the same structure::
+It inherits from SGML a basic structure: each piece of the document is an element. Each element is described by a "tag". Each tag has a different meaning, but they all have the same structure::
some content
-that is, the tag name is surrounded by < and >, which marks the beginning of
+That is, the tag name is surrounded by < and >, which marks the beginning of
the element, and the end of the element is indicated by the same tag with a slash.
The real power is that these elements can be nested arbitrarily deep. In order to keep that all readable, we often want to indent the content inside the tags, so it's clear what belongs with what. That is one of the tricky bits of this assignment.
+
Basic tags
----------
@@ -258,28 +518,31 @@ In addition to the tag name and the content, extra attributes can be attached to
.. code-block:: html
-
+
There can be all sorts of stuff stored in attributes -- some required for specific tags, some extra, like font sizes and colors. Note that since tags can essentially have any attributes, your code will need to support that -- doesn't it kind of look like a dict? And keyword arguments?
Special Elements
----------------
-The general structure is everything is between and opening and closing tag. But some elements don't really have content -- just attributes. So the slash goes at the end of the tag, after the attributes. We can call these self-closing tags:
+The general structure is everything in between the opening and closing tag. But some elements don't really have content -- just attributes. So the slash goes at the end of the tag, after the attributes. We can call these self-closing tags:
.. code-block:: html
-To make a link, you use an "anchor" tag: ````. It required attributes to indicate what the link is:
+To make a link, you use an "anchor" tag: ````. It requires attributes to indicate what the link is:
.. code-block:: html
- link
+ link
-the ``href`` attribute is the link (hyper reference).
+The ``href`` attribute is the link (hyper reference).
-To make a bulleted list, you use a
tag (unordered list), and inside that, you put individual list elements
:
+lists
+-----
+
+To make a bulleted list, you use a
tag (unordered list), and inside that, you put individual list items
:
.. code-block:: html
@@ -292,17 +555,12 @@ To make a bulleted list, you use a
tag (unordered list), and inside that, y
-Note that the list itself, and the list items can both take various attributes (all tags can...)
+Note that the list itself *and* the list items can both take various attributes (all tags can...)
-Section Headers are created with "h" tags:
is the biggest (highest level), and there is
,
, etc. for sections, sub sections, subsub sections.
+Section Headers are created with "h" tags:
is the biggest (highest level), and there is
,
, etc. for sections, sub sections, subsub sections...
.. code-block:: html
-
PythonClass - Class 6 example
+
PythonClass - Class 7 example
I think that's all you need to know!
-
-
-
-
-
diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst
index d047a2b2..78ac470e 100644
--- a/slides_sources/source/exercises/index.rst
+++ b/slides_sources/source/exercises/index.rst
@@ -1,12 +1,77 @@
-Homework Materials
-======================
+=========
+Exercises
+=========
+Contents:
+=========
+
+.. rst-class:: left
+
+
+Session 2:
+----------
.. toctree::
:maxdepth: 1
grid_printer
fizz_buzz
fib_and_lucas
+
+Session 3:
+----------
+.. toctree::
+ :maxdepth: 1
+
+ slicing
+ list_lab
+ string_formatting
+ rot13
+ mailroom
+
+Session 4:
+----------
+.. toctree::
+ :maxdepth: 1
+
+ dict_lab
+ file_lab
kata_fourteen
+
+Session 5:
+----------
+.. toctree::
+ :maxdepth: 1
+
+ exceptions_lab
+ comprehensions_lab
+
+Session 6:
+----------
+.. toctree::
+ :maxdepth: 1
+
+ args_kwargs_lab
+
+
+Session 7:
+----------
+.. toctree::
+ :maxdepth: 1
+
html_renderer
+
+Session 8:
+-----------
+.. toctree::
+ :maxdepth: 1
+
circle_class
+ sparse_array
+
+Session 9:
+----------
+.. toctree::
+ :maxdepth: 1
+
+ lambda_magic
+ trapezoid
\ No newline at end of file
diff --git a/slides_sources/source/exercises/lambda_magic.rst b/slides_sources/source/exercises/lambda_magic.rst
new file mode 100644
index 00000000..edcde112
--- /dev/null
+++ b/slides_sources/source/exercises/lambda_magic.rst
@@ -0,0 +1,61 @@
+.. _exercise_lambda_magic:
+
+************************
+lambda and keyword Magic
+************************
+
+Goals
+=====
+
+.. rst-class:: left
+
+ * A bit of lambda
+ * functions as objects
+ * keyword evaluation
+
+
+Task
+----
+
+Write a function that returns a list of n functions,
+such that each one, when called, will return the input value,
+incremented by an increasing number.
+
+Use a for loop, ``lambda``, and a keyword argument
+
+**Extra credit:**
+
+Do it with a list comprehension, instead of a for loop
+
+Not clear? here's what you should get...
+
+Example calling code
+---------------------
+
+.. code-block:: ipython
+
+ In [96]: the_list = function_builder(4)
+ ### so the_list should contain n functions (callables)
+ In [97]: the_list[0](2)
+ Out[97]: 2
+ ## the zeroth element of the list is a function that add 0
+ ## to the input, hence called with 2, returns 2
+ In [98]: the_list[1](2)
+ Out[98]: 3
+ ## the 1st element of the list is a function that adds 1
+ ## to the input value, thus called with 2, returns 3
+ In [100]: for f in the_list:
+ print(f(5))
+ .....:
+ 5
+ 6
+ 7
+ 8
+ ### If you loop through them all, and call them, each one adds one more
+ to the input, 5... i.e. the nth function in the list adds n to the input.
+
+.. nextslide::
+
+See the test code in Examples/Session09
+
+
diff --git a/slides_sources/source/exercises/list_lab.rst b/slides_sources/source/exercises/list_lab.rst
new file mode 100644
index 00000000..36943bfb
--- /dev/null
+++ b/slides_sources/source/exercises/list_lab.rst
@@ -0,0 +1,113 @@
+.. _exercise_list_lab:
+
+********
+List Lab
+********
+
+Learning about lists
+====================
+
+After:
+
+http://www.upriss.org.uk/python/session5.html
+
+Goal:
+-----
+
+Learn the basic ins and outs of Python lists.
+
+hint
+----
+
+to query the user for info at the command line, you use:
+
+.. code-block:: python
+
+ response = input("a prompt for the user > ")
+
+``response`` will be a string of whatever the user types (until a ).
+
+
+Procedure
+---------
+
+In your student dir in the IntroPython2015 repo, create a ``session03`` dir and put in a new ``list_lab.py`` file.
+
+The file should be an executable python script. That is to say that one
+should be able to run the script directly like so:
+
+.. code-block:: bash
+
+ $ ./list_lab.py
+
+(At least on OS-X and Linux)
+
+-- you do that with this command:
+
+.. code-block:: bash
+
+ $ chmod +x list_lab.py
+
+(The +x means make this executable)
+
+The file will also need this on the first line::
+
+ #!/usr/bin/env python3
+
+This is known as the "she-bang" line -- it tells the shell how to execute that file -- in this case, with ``python3``
+
+NOTE: on Windows, there is a python launcher which, if everything is configured correctly look at that line to know you want python3 if there is more than one python on your system.
+
+.. nextslide::
+
+Add the file to your clone of the repository and commit changes frequently
+while working on the following tasks. When you are done, push your changes to
+GitHub and issue a pull request.
+
+(if you are struggling with git -- just write the code for now)
+
+When the script is run, it should accomplish the following four series of
+actions:
+
+.. nextslide:: Series 1
+
+- Create a list that contains "Apples", "Pears", "Oranges" and "Peaches".
+- Display the list.
+- Ask the user for another fruit and add it to the end of the list.
+- Display the list.
+- Ask the user for a number and display the number back to the user and the
+ fruit corresponding to that number (on a 1-is-first basis).
+- Add another fruit to the beginning of the list using "+" and display the
+ list.
+- Add another fruit to the beginning of the list using insert() and display the list.
+- Display all the fruits that begin with "P", using a for loop.
+
+
+.. nextslide:: Series 2
+
+Using the list created in series 1 above:
+
+- Display the list.
+- Remove the last fruit from the list.
+- Display the list.
+- Ask the user for a fruit to delete and find it and delete it.
+- (Bonus: Multiply the list times two. Keep asking until a match is found. Once found, delete all occurrences.)
+
+.. nextslide:: Series 3
+
+Again, using the list from series 1:
+
+- Ask the user for input displaying a line like "Do you like apples?"
+- for each fruit in the list (making the fruit all lowercase).
+- For each "no", delete that fruit from the list.
+- For any answer that is not "yes" or "no", prompt the user to answer with one
+ of those two values (a while loop is good here):
+- Display the list.
+
+.. nextslide:: Series 4
+
+Once more, using the list from series 1:
+
+- Make a copy of the list and reverse the letters in each fruit in the copy.
+- Delete the last item of the original list. Display the original list and the
+ copy.
diff --git a/slides_sources/source/exercises/mailroom-oo.rst b/slides_sources/source/exercises/mailroom-oo.rst
new file mode 100644
index 00000000..17f0dacb
--- /dev/null
+++ b/slides_sources/source/exercises/mailroom-oo.rst
@@ -0,0 +1,167 @@
+.. _exercise_mailroom_oo:
+
+********
+Mailroom
+********
+
+Making Mailroom Object Oriented
+
+A complete program
+==================
+
+It was quite resonable to build the simple MailRoom program using a
+single module, a simple data structure, and functions that manipulate
+that data structure.
+
+But if one were to expand the program with additional functionality, it
+would start to get a bit unwieldy and hard to maintain.
+
+So it's a pretty good candidate for an object-oriented approach.
+
+Goal:
+-----
+
+Refactor the mailroom program usined a couple classes to help organise the code.
+
+
+
+
+The program
+-----------
+
+Write a small command-line script called ``mailroom.py``. This script should be executable. The script should accomplish the following goals:
+
+* It should have a data structure that holds a list of your donors and a
+ history of the amounts they have donated. This structure should be populated
+ at first with at least five donors, with between 1 and 3 donations each
+
+* The script should prompt the user (you) to choose from a menu of 3 actions:
+ 'Send a Thank You' or 'Create a Report' or 'quit')
+
+Sending a Thank You
+-------------------
+
+* If the user (you) selects 'Send a Thank You', prompt for a Full Name.
+
+ * If the user types 'list', show them a list of the donor names and re-prompt
+ * If the user types a name not in the list, add that name to the data structure and use it.
+ * If the user types a name in the list, use it.
+ * Once a name has been selected, prompt for a donation amount.
+ * Turn the amount into a number -- it is OK at this point for the program to crash if someone types a bogus amount.
+ * Once an amount has been given, add that amount to the donation history of
+ the selected user.
+ * Finally, use string formatting to compose an email thanking the donor for
+ their generous donation. Print the email to the terminal and return to the
+ original prompt.
+
+**It is fine to forget new donors once the script quits running.**
+
+Creating a Report
+------------------
+
+* If the user (you) selected 'Create a Report' print a list of your donors,
+ sorted by total historical donation amount.
+
+ - Include Donor Name, total donated, number of donations and average donation amount as values in each row. You do not need to print out all their donations, just the summary info.
+ - Using string formatting, format the output rows as nicely as possible. The end result should be tabular (values in each column should align with those above and below)
+ - After printing this report, return to the original prompt.
+
+* At any point, the user should be able to quit their current task and return
+ to the original prompt.
+
+* From the original prompt, the user should be able to quit the script cleanly
+
+
+Your report should look something like this::
+
+ Donor Name | Total Given | Num Gifts | Average Gift
+ ------------------------------------------------------------------
+ William Gates, III $ 653784.49 2 $ 326892.24
+ Mark Zuckerberg $ 16396.10 3 $ 5465.37
+ Jeff Bezos $ 877.33 1 $ 877.33
+ Paul Allen $ 708.42 3 $ 236.14
+
+Guidelines
+----------
+
+First, factor your script into separate functions. Each of the above
+tasks can be accomplished by a series of steps. Write discreet functions
+that accomplish individual steps and call them.
+
+Second, use loops to control the logical flow of your program. Interactive
+programs are a classic use-case for the ``while`` loop.
+
+Of course, ``input()`` will be useful here.
+
+Put the functions you write into the script at the top.
+
+Put your main interaction into an ``if __name__ == '__main__'`` block.
+
+Finally, use only functions and the basic Python data types you've learned
+about so far. There is no need to go any farther than that for this assignment.
+
+Submission
+----------
+
+As always, put the new file in your student directory in a ``session03``
+directory, and add it to your clone early. Make frequent commits with
+good, clear messages about what you are doing and why.
+
+When you are done, push your changes and make a pull request.
+
+.. _exercise_mailroom_plus:
+
+Adding dicts...
+---------------
+
+
+For the next week (after Session04)
+
+You should have been able to do all that with the basic data types:
+
+numbers, strings, lists and tuples.
+
+But once you've learned about dictionaries (Session04) you may be able to re-write it a bit more simply and efficiently.
+
+ * Update mailroom from last week to:
+
+ - Use dicts where appropriate
+ - Write a full set of letters to everyone to individual files on disk
+ - See if you can use a dict to switch between the users selections
+ - Try to use a dict and the .format() method to do the letter as one
+ big template -- rather than building up a big string in parts.
+
+Example:
+
+.. code-block:: ipython
+
+ In [3]: d
+ Out[3]: {'first_name': 'Chris', 'last_name': 'Barker'}
+
+
+ In [5]: "My name is {first_name} {last_name}".format(**d)
+ Out[5]: 'My name is Chris Barker'
+
+Don't worry too much about the "**" -- we'll get into the details later, but for now, it means, more or less -- pass this whole dict in as a bunch of keyword arguments.
+
+
+.. _exercise_mailroom_exeptions:
+
+Adding Exceptions
+-----------------
+
+**After Session05:**
+
+* Exceptions:
+
+Now that you've learned about Exception handling, you can update your code to handle errors better -- like when a user inputs bad data.
+
+* Comprehensions:
+
+Can you use comprehensions to clean up your code a bit?
+
+* Tests
+
+Add some tests..
+
+
diff --git a/slides_sources/source/exercises/mailroom.rst b/slides_sources/source/exercises/mailroom.rst
new file mode 100644
index 00000000..656e5c19
--- /dev/null
+++ b/slides_sources/source/exercises/mailroom.rst
@@ -0,0 +1,158 @@
+.. _exercise_mailroom:
+
+********
+Mailroom
+********
+
+A complete program
+==================
+
+Using basic data types and logic for a full program
+
+Goal:
+-----
+
+You work in the mail room at a local charity. Part of your job is to write
+incredibly boring, repetitive emails thanking your donors for their generous
+gifts. You are tired of doing this over an over again, so you've decided to
+let Python help you out of a jam.
+
+The program
+-----------
+
+Write a small command-line script called ``mailroom.py``. This script should be executable. The script should accomplish the following goals:
+
+* It should have a data structure that holds a list of your donors and a
+ history of the amounts they have donated. This structure should be populated
+ at first with at least five donors, with between 1 and 3 donations each
+
+* The script should prompt the user (you) to choose from a menu of 3 actions:
+ 'Send a Thank You' or 'Create a Report' or 'quit')
+
+Sending a Thank You
+-------------------
+
+* If the user (you) selects 'Send a Thank You', prompt for a Full Name.
+
+ * If the user types 'list', show them a list of the donor names and re-prompt
+ * If the user types a name not in the list, add that name to the data structure and use it.
+ * If the user types a name in the list, use it.
+ * Once a name has been selected, prompt for a donation amount.
+ * Turn the amount into a number -- it is OK at this point for the program to crash if someone types a bogus amount.
+ * Once an amount has been given, add that amount to the donation history of
+ the selected user.
+ * Finally, use string formatting to compose an email thanking the donor for
+ their generous donation. Print the email to the terminal and return to the
+ original prompt.
+
+**It is fine to forget new donors once the script quits running.**
+
+Creating a Report
+------------------
+
+* If the user (you) selected 'Create a Report' print a list of your donors,
+ sorted by total historical donation amount.
+
+ - Include Donor Name, total donated, number of donations and average donation amount as values in each row. You do not need to print out all their donations, just the summary info.
+ - Using string formatting, format the output rows as nicely as possible. The end result should be tabular (values in each column should align with those above and below)
+ - After printing this report, return to the original prompt.
+
+* At any point, the user should be able to quit their current task and return
+ to the original prompt.
+
+* From the original prompt, the user should be able to quit the script cleanly
+
+
+Your report should look something like this::
+
+ Donor Name | Total Given | Num Gifts | Average Gift
+ ------------------------------------------------------------------
+ William Gates, III $ 653784.49 2 $ 326892.24
+ Mark Zuckerberg $ 16396.10 3 $ 5465.37
+ Jeff Bezos $ 877.33 1 $ 877.33
+ Paul Allen $ 708.42 3 $ 236.14
+
+Guidelines
+----------
+
+First, factor your script into separate functions. Each of the above
+tasks can be accomplished by a series of steps. Write discreet functions
+that accomplish individual steps and call them.
+
+Second, use loops to control the logical flow of your program. Interactive
+programs are a classic use-case for the ``while`` loop.
+
+Of course, ``input()`` will be useful here.
+
+Put the functions you write into the script at the top.
+
+Put your main interaction into an ``if __name__ == '__main__'`` block.
+
+Finally, use only functions and the basic Python data types you've learned
+about so far. There is no need to go any farther than that for this assignment.
+
+Submission
+----------
+
+As always, put the new file in your student directory in a ``session03``
+directory, and add it to your clone early. Make frequent commits with
+good, clear messages about what you are doing and why.
+
+When you are done, push your changes and make a pull request.
+
+.. _exercise_mailroom_plus:
+
+Adding dicts...
+---------------
+
+
+For the next week (after Session04)
+
+You should have been able to do all that with the basic data types:
+
+numbers, strings, lists and tuples.
+
+But once you've learned about dictionaries (Session04) you may be able to re-write it a bit more simply and efficiently.
+
+ * Update mailroom from last week to:
+
+ - Use dicts where appropriate
+ - Write a full set of letters to everyone to individual files on disk
+ - See if you can use a dict to switch between the users selections
+ - Try to use a dict and the .format() method to do the letter as one
+ big template -- rather than building up a big string in parts.
+
+Example:
+
+.. code-block:: ipython
+
+ In [3]: d
+ Out[3]: {'first_name': 'Chris', 'last_name': 'Barker'}
+
+
+ In [5]: "My name is {first_name} {last_name}".format(**d)
+ Out[5]: 'My name is Chris Barker'
+
+Don't worry too much about the "**" -- we'll get into the details later, but for now, it means, more or less -- pass this whole dict in as a bunch of keyword arguments.
+
+
+.. _exercise_mailroom_exeptions:
+
+Adding Exceptions
+-----------------
+
+**After Session05:**
+
+* Exceptions:
+
+Now that you've learned about Exception handling, you can update your code to handle errors better -- like when a user inputs bad data.
+
+* Comprehensions:
+
+Can you use comprehensions to clean up your code a bit?
+
+* Tests
+
+Add some tests..
+
+
diff --git a/slides_sources/source/exercises/rot13.rst b/slides_sources/source/exercises/rot13.rst
new file mode 100644
index 00000000..0763adf4
--- /dev/null
+++ b/slides_sources/source/exercises/rot13.rst
@@ -0,0 +1,48 @@
+.. _exercise_rot13:
+
+*****
+ROT13
+*****
+
+Goal
+----
+
+Get used to working with the number values for characters
+
+Get a bit of practice with string methods and string processing
+
+
+ROT13 encryption
+-----------------
+
+The ROT13 encryption scheme is a simple substitution cypher where each letter
+in a text is replace by the letter 13 away from it (imagine the alphabet as a
+circle, so it wraps around).
+
+The task
+--------
+
+Add a python module named ``rot13.py`` to the session03 dir in your student dir. This module should provide at least one function called ``rot13`` that takes any amount of text and returns that same text encrypted by ROT13.
+
+This function should preserve whitespace, punctuation and capitalization.
+
+Your module should include an ``if __name__ == '__main__':`` block with tests (asserts) that demonstrate that your ``rot13`` function and any helper functions you add work properly.
+
+
+.. nextslide:: A bit more
+
+There is a "short-cut" available that will help you accomplish this task. Some
+spelunking in `the documentation for strings`_ should help you to find it. If
+you do find it, using it is completely fair game.
+
+As usual, add your new file to your local clone right away. Make commits
+early and often and include commit messages that are descriptive and concise.
+
+When you are done, if you want me to review it, push your changes to github
+and issue a pull request.
+
+try decrypting this:
+
+"Zntargvp sebz bhgfvqr arne pbeare"
+
+.. _the documentation for strings: https://docs.python.org/3/library/stdtypes.html#string-methods
diff --git a/slides_sources/source/exercises/slicing.rst b/slides_sources/source/exercises/slicing.rst
new file mode 100644
index 00000000..61747eb8
--- /dev/null
+++ b/slides_sources/source/exercises/slicing.rst
@@ -0,0 +1,25 @@
+.. _exercise_slicing:
+
+***********
+Slicing Lab
+***********
+
+Goal
+====
+
+Get the basics of sequence slicing down
+
+Tasks
+-----
+
+Write some functions that:
+
+* return a sequence with the first and last items exchanged.
+* return a sequence with every other item removed
+* return a sequence with the first and last 4 items removed, and every other item in between
+* return a sequence reversed (just with slicing)
+* return a sequence with the middle third, then last third, then the first third in the new order
+
+NOTE:
+these should work with ANY sequence -- but you can use strings to test, if you like.
+
diff --git a/slides_sources/source/exercises/sparse_array.rst b/slides_sources/source/exercises/sparse_array.rst
new file mode 100644
index 00000000..f627c82a
--- /dev/null
+++ b/slides_sources/source/exercises/sparse_array.rst
@@ -0,0 +1,86 @@
+.. _exercise_sparse_array:
+
+======================
+Sparse Array Exercise
+======================
+
+Sparse Array
+============
+
+.. rst-class:: medium
+
+ Goal:
+
+Learn how to emulate a built-in class.
+
+Sparse Array:
+-------------
+
+Oftentimes, at least in computation programming, we have large arrays of data that hold mostly zeros.
+
+These are referred to as "sparse" as the information in them is widely scattered, or sparse.
+
+Since they are mostly zeros, it can be memory and computationally efficient to store only the value that are non-zero.
+
+But you want it to look like a regular array in user code.
+
+In the real world, these are usually 2 dimensional arrays. But to keep it a bit simpler, we'll make a 1 dimensional sparse array in this class.
+
+(feel free to make it 2d for an extra challenge!)
+
+A Sparse array class
+--------------------
+
+A spare array class should present to the user the same interface as a regular list.
+
+Some ideas of how to do that:
+
+* Internally, it can store the values in a dict, with the index as the keys. So that only the indexes with non-zero values will be stored.
+
+* It should take a sequence of values as an initializer:
+
+.. code-block:: python
+
+ sa = SparseArray([1,2,0,0,0,0,3,0,0,4])
+
+* you should be able to tell how long it is:
+
+.. code-block:: python
+
+ len(my_array)
+
+ This will give its "virtual" length -- with the zeros
+
+.. nextslide::
+
+* It should support getting and setting particular elements via indexing:
+
+.. code-block:: python
+
+ sa[5] = 12
+ sa[3] = 0 # the zero won't get stored!
+ val = sa[13] # it should get a zero if not set
+
+* It should support deleting an element by index:
+
+.. code-block:: python
+
+ del sa[4]
+
+* It should raise an ``IndexError`` if you try to access an index beyond the end.
+
+* it should have an append() method
+
+.. nextslide::
+
+* Can you make it support slicing?
+
+* How else can you make it like a list?
+
+.. code-block:: ipython
+
+ In [10]: my_array = SparseArray( (1,0,0,0,2,0,0,0,5) )
+ In [11]: my_array[4]
+ Out[11]: 2
+ In [12]: my_array[2]
+ Out[12]: 0
diff --git a/slides_sources/source/exercises/string_formatting.rst b/slides_sources/source/exercises/string_formatting.rst
new file mode 100644
index 00000000..f01556d9
--- /dev/null
+++ b/slides_sources/source/exercises/string_formatting.rst
@@ -0,0 +1,111 @@
+.. _exercise_string_formatting:
+
+*********************
+String Formatting Lab
+*********************
+
+Building up strings
+===================
+
+.. rst-class:: left
+
+For reference:
+
+The official reference docs:
+
+https://docs.python.org/3/library/string.html#string-formatting
+
+And a more human-readable intro:
+
+https://pyformat.info/
+
+And a nice "Cookbook":
+
+https://mkaz.tech/python-string-format.html
+
+
+A Couple Exercises
+------------------
+
+* Write a format string that will take:
+
+ ``( 2, 123.4567, 10000)``
+
+ and produce:
+
+ ``'file_002 : 123.46, 1.00e+04'``
+
+**Note:** the idea behind the "file_002" is that if you have a bunch of files that you want to name with numbers that can be sorted, you need to "pad" the numbers with zeros to get the right sort order.
+
+.. nextslide::
+
+For example:
+
+.. code-block:: ipython
+
+ In [10]: fnames = ['file1', 'file2', 'file10', 'file11']
+ In [11]: fnames.sort()
+ In [12]: fnames
+ Out[12]: ['file1', 'file10', 'file11', 'file2']
+
+That is probably not what you want. However:
+
+.. code-block:: ipython
+
+ In [1]: fnames = ['file001', 'file002', 'file010', 'file011']
+ In [3]: sorted(fnames)
+ Out[3]: ['file001', 'file002', 'file010', 'file011']
+
+That works!
+
+So you want to find a string formatting operator that will "pad" the number with zeros for you.
+
+Dynamically Building up format strings
+--------------------------------------
+
+* Rewrite:
+
+``"the 3 numbers are: {:d}, {:d}, {:d}".format(1,2,3)``
+
+to take an arbitrary number of values.
+
+Trick: You can pass in a tuple of values to a function with a ``*``:
+
+.. code-block:: ipython
+
+ In [52]: t = (1,2,3)
+
+ In [53]: "the 3 numbers are: {:d}, {:d}, {:d}".format(*t)
+ Out[53]: 'the 3 numbers are: 1, 2, 3'
+
+.. nextslide::
+
+The idea here is that you may have a tuple of three numbers, but might also have 4 or 5 or....
+
+So you can dynamically build up the format string to accommodate the length of the tuple.
+
+The string object has the ``format()`` method, so you can call it with a string that is bound to a name, not just a string literal. For example:
+
+.. code-block:: ipython
+
+ In [16]: fstring = "{:d}, {:d}"
+
+ In [17]: nums = (34, 56)
+
+ In [18]: fstring.format(*nums)
+ Out[18]: '34, 56'
+
+So how would you make an fstring that was the right length for an arbitrary tuple?
+
+.. nextslide::
+
+Put your code in a function that will return the formatted string like so:
+
+.. code-block:: ipython
+
+ In [20]: formatter((2,3,5))
+ Out[20]: 'the 3 numbers are: 2, 3, 5'
+
+ In [21]: formatter((2,3,5,7,9))
+ Out[21]: 'the 5 numbers are: 2, 3, 5, 7, 9'
+
diff --git a/slides_sources/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst
new file mode 100644
index 00000000..bf6c97ef
--- /dev/null
+++ b/slides_sources/source/exercises/trapezoid.rst
@@ -0,0 +1,295 @@
+.. _exercise_trapezoidal_rule:
+
+*****************
+Trapezoidal Rule
+*****************
+
+Passing functions around
+=========================
+
+
+.. rst-class:: medium left
+
+ Goal:
+
+.. rst-class:: left
+
+ Making use of functions as objects -- functions that act on functions.
+
+
+Trapezoidal rule
+----------------
+
+The "trapezoidal rule":
+
+https://en.wikipedia.org/wiki/Trapezoidal_rule
+
+Is one of the easiest "quadrature" methods.
+
+Otherwise known as computing a definite integral, or, simply,
+
+Computing the area under a curve.
+
+The task
+--------
+
+Your task is to write a ``trapz()`` function that will compute the area under an arbitrary function, using the trapezoidal rule.
+
+The function will take another function as an argument, as well as the start and end points to compute, and return the area under the curve.
+
+Example:
+--------
+
+.. code-block:: python
+
+ def line(x):
+ '''a very simple straight horizontal line at y = 5'''
+ return 5
+
+ area = trapz(line, 0, 10)
+
+ area
+ 50
+
+About the simplest "curve" you can have is a horizontal straight line, in this case, at y = 5. The area under that line from 0 to 10 is a rectangle that is 10 wide and 5 high, so with an area of 50.
+
+Of course in this case, it's easiest to simply multiply the height times the width, but we want a function that will work for **Any** curve.
+
+HINT: this simple example could be a good test case!
+
+The Solution:
+-------------
+
+Your function definition should look like:
+
+.. code-block:: python
+
+ def trapz(fun, a, b):
+ """
+ Compute the area under the curve defined by
+ y = fun(x), for x between a and b
+
+ :param fun: the function to evaluate
+ :type fun: a function that takes a single parameter
+
+ :param a: the start point for teh integration
+ :type a: a numeric value
+
+ :param b: the end point for the integration
+ :type b: a numeric value
+ """
+ pass
+
+.. nextslide::
+
+In the function, you want to compute the following equation:
+
+.. math::
+
+ area = \frac{b-a}{2N}(f(x_0) + 2f(x_1) + 2f(x_2) + \dotsb + 2f(x_{N-1}) + f(x_N))
+
+So you will need to:
+
+ - create a list of x values from a to b (maybe 100 or so values to start)
+
+ - compute the function for each of those values and double them
+
+ - add them all up
+
+ - multiply by the half of the difference between a and b divided by the number of steps.
+
+.. nextslide::
+
+Note that the first and last values are not doubled, so it may be more efficient to rearrange it like this:
+
+.. math::
+
+ area = \frac{b-a}{N} \left( \frac{f(x_0) + f(x_{N})}{2} + \sum_{i=1}^{N-1} f(x_i) \right)
+
+Can you use comprehensions for this?
+
+NOTE: ``range()`` only works for integers -- how can you deal with that?
+
+.. nextslide::
+
+Once you have that, it should work for any function that can be evaluated between a and b.
+
+Try it for some built-in math functions, like ``math.sin``
+
+tests
+-----
+
+Do this using test-drive development.
+
+A few examples of analytical solutions you can use for tests:
+
+A simple horizontal line -- see above.
+
+.. nextslide::
+
+A sloped straight line:
+
+.. math::
+
+ \int_a^b y = mx + B = \frac{1}{2} m (b^2-a^2) + B (b-a)
+
+The quadratic:
+
+.. math::
+
+ \int_a^b y = Ax^2 + Bx + C = \frac{A}{3} (b^3-a^3) + \frac{B}{2} (b^2-a^2) + C (b-a)
+
+
+The sine function:
+
+.. math::
+
+ \int_a^b \sin(x) = \cos(a) - \cos(b)
+
+Computational Accuracy
+----------------------
+
+In the case of the linear functions, the result should theoretically be exact. But with the vagaries of floating point math may not be.
+
+And for non-linear functions, the result will certainly not be exact.
+
+So you want to check if the answer is *close* to what you expect.
+
+In py3.5 -- there is an ``isclose()`` function (PEP485)
+
+https://www.python.org/dev/peps/pep-0485/
+
+In earlier pythons -- you'll need your own. There is one in:
+
+``Examples/Session09/test_trapz.py``
+
+
+
+Stage two:
+----------
+
+Some functions need extra parameters to do their thing. But the above will only handle a single parameter. For example, a quadratic function:
+
+.. math::
+
+ y = A x^2 + Bx + C
+
+Requires values for A, B, and C in order to compute y from an given x.
+
+You could write a specialized version of this function for each A, B, and C:
+
+.. code-block:: python
+
+ def quad1(x):
+ return 3 * x**2 + 2*x + 4
+
+But then you need to write a new function for any value of these parameters you might need.
+
+.. nextslide::
+
+Instead, you can pass in A, B and C each time:
+
+.. code-block:: python
+
+ def quadratic(x, A=0, B=0, C=0):
+ return A * x**2 + B * x + C
+
+Nice and general purpose.
+
+But how would we compute the area under this function?
+
+The function we wrote above only passes x in to the function it is integrating.
+
+Passing arguments through:
+--------------------------
+
+Update your trapz() function so that you can give it a function that takes arbitrary extra arguments, either positional or keyword, after the x.
+
+So you can do:
+
+.. code-block:: python
+
+ trapz(quadratic, 2, 20, A=1, B=3, C=2)
+
+or
+
+.. code-block:: python
+
+ trapz(quadratic, 2, 20, 1, 3, C=2)
+
+or
+
+.. code-block:: python
+
+ coef = {'A':1, 'B':3, 'C': 2}
+ trapz(quadratic, 2, 20, **coef)
+
+.. nextslide::
+
+**NOTE:** Make sure this will work with ANY function, with **ANY** additional positional or keyword arguments -- not just this particular function.
+
+This is pretty conceptually challenging -- but it's very little code!
+
+If you are totally lost -- look at the lecture notes from previous classes -- how can you both accept and pass arbitrary arguments to/from a function?
+
+.. nextslide::
+
+You want your trapz function to take ANY function that can take ANY arbitrary extra arguments -- not just the quadratic function, and not just ``A,B, and C``. So good to test with another example.
+
+The generalized sine function is:
+
+.. math::
+
+ A \sin(\omega t)
+
+where :math:`A` is the amplitude, and :math:`\omega` is the frequency of the function. In this case, the area under the curve from a to b is:
+
+.. math::
+
+ \frac{A}{\omega} \left( \cos(\omega a) - \cos(\omega b) \right)
+
+The test code has a test for this one, too.
+
+Currying
+--------
+
+Another way to solve the above problem is to use the original ``trapz``, and create a custom version of the quadratic() function instead.
+
+Write a function that takes ``A, B, and C`` as arguments, and returns a function that evaluates the quadratic for those particular coefficients.
+
+Try passing the results of this into your ``trapz()`` and see if you get the same answer.
+
+partial
+-------
+
+Do the above with ``functools.partial`` as well.
+
+Extra credit
+------------
+
+This isn't really the point of the exercise, but see if you can make it dynamically accurate.
+
+How accurate it is depends on how small the chunks are that you break the function up into.
+
+See if you can think of a way to dynamically determine how small a step you should use.
+
+This is one for the math and computational programming geeks!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/slides_sources/source/extra_topics.rst b/slides_sources/source/extra_topics.rst
new file mode 100644
index 00000000..39062e0a
--- /dev/null
+++ b/slides_sources/source/extra_topics.rst
@@ -0,0 +1,131 @@
+.. _extra_topics:
+
+************
+Extra Topics
+************
+
+Here are some extra topics that we didn't have time for in the regular class sessions:
+
+==============================
+Closures and function Currying
+==============================
+
+Defining specialized functions on the fly
+
+Closures
+--------
+
+"Closures" and "Currying" are cool CS terms for what is really just defining functions on the fly.
+
+you can find a "proper" definition here:
+
+https://en.wikipedia.org/wiki/Closure_(computer_programming)
+
+but I even have trouble following that.
+
+So let's go straight to an example:
+
+.. nextslide::
+
+.. code-block:: python
+
+ def counter(start_at=0):
+ count = [start_at]
+ def incr():
+ count[0] += 1
+ return count[0]
+ return incr
+
+What's going on here?
+
+We have stored the ``start_at`` value in a list.
+
+Then defined a function, ``incr`` that adds one to the value in the list, and returns that value.
+
+[ Quiz: why is it: ``count = [start_at]``, rather than just ``count=start_at`` ]
+
+.. nextslide::
+
+So what type of object do you get when you call ``counter()``?
+
+.. code-block:: ipython
+
+ In [37]: c = counter(start_at=5)
+
+ In [38]: type(c)
+ Out[38]: function
+
+So we get a function back -- makes sense. The ``def`` defines a function, and that function is what's getting returned.
+
+Being a function, we can, of course, call it:
+
+.. code-block:: ipython
+
+ In [39]: c()
+ Out[39]: 6
+
+ In [40]: c()
+ Out[40]: 7
+
+Each time is it called, it increments the value by one.
+
+.. nextslide::
+
+But what happens if we call ``counter()`` multiple times?
+
+.. code-block:: ipython
+
+ In [41]: c1 = counter(5)
+
+ In [42]: c2 = counter(10)
+
+ In [43]: c1()
+ Out[43]: 6
+
+ In [44]: c2()
+ Out[44]: 11
+
+So each time ``counter()`` is called, a new function is created. And that function has its own copy of the ``count`` object. This is what makes in a "closure" -- it carries with it the scope in which is was created.
+
+the returned ``incr`` function is a "curried" function -- a function with some parameters pre-specified.
+
+``functools.partial``
+---------------------
+
+The ``functools`` module in the standard library provides utilities for working with functions:
+
+https://docs.python.org/3.5/library/functools.html
+
+Creating a curried function turns out to be common enough that the ``functools.partial`` function provides an optimized way to do it:
+
+What functools.partial does is:
+
+ * Makes a new version of a function with one or more arguments already filled in.
+ * The new version of a function documents itself.
+
+Example:
+
+.. code-block:: python
+
+ def power(base, exponent):
+ """returns based raised to the give exponent"""
+ return base ** exponent
+
+Simple enough. but what if we wanted a specialized ``square`` and ``cube`` function?
+
+We can use ``functools.partial`` to *partially* evaluate the function, giving us a specialized version:
+
+square = partial(power, exponent=2)
+cube = partial(power, exponent=3)
+
+Reading:
+--------
+
+http://www.pydanny.com/python-partials-are-fun.html
+
+https://pymotw.com/3/functools/
+
+http://www.programiz.com/python-programming/closure
+
+https://www.clear.rice.edu/comp130/12spring/curry/
+
diff --git a/slides_sources/source/include.rst b/slides_sources/source/include.rst
new file mode 100644
index 00000000..607ee281
--- /dev/null
+++ b/slides_sources/source/include.rst
@@ -0,0 +1,6 @@
+
+.. |instructor_1_name| replace:: Christopher Barker
+.. |instructor_1_email| replace:: PythonCHB@gmail.com
+
+.. |instructor_2_name| replace:: Maria McKinley
+.. |instructor_2_email| replace:: maria@mariakathryn.net
diff --git a/slides_sources/source/index.rst b/slides_sources/source/index.rst
index 3d08ed33..040d3dfe 100644
--- a/slides_sources/source/index.rst
+++ b/slides_sources/source/index.rst
@@ -51,6 +51,7 @@ In This Course
.. toctree::
:maxdepth: 2
+ extra_topics
exercises/index
supplements/index
diff --git a/slides_sources/source/session01.rst b/slides_sources/source/session01.rst
index bd4a7d46..a282e32a 100644
--- a/slides_sources/source/session01.rst
+++ b/slides_sources/source/session01.rst
@@ -1,3 +1,5 @@
+.. include:: include.rst
+
**************************
Session One: Introductions
**************************
@@ -44,16 +46,16 @@ Your instructors
.. rst-class:: center medium
-| Christopher Barker
-| (PythonCHB at gmail dot com)
+| |instructor_1_name|
+| |instructor_1_email|
|
.. nextslide::
.. rst-class:: center medium
-| Maria McKinley
-| (maria at mariakathryn dot net)
+| |instructor_2_name|
+| |instructor_2_email|
|
Who are you?
@@ -65,14 +67,16 @@ Who are you?
* name
* programming background: what languages have you used?
-* what do you hope to get from this class
+* neighbor's name
+* neighbor's favorite coffee shop or bar
+
Introduction to This Class
==========================
.. rst-class:: center large
-Intro to Python
+ Introduction to Python
Course Materials Online
@@ -82,19 +86,19 @@ A rendered HTML copy of the slides for this course may be found online at:
http://uwpce-pythoncert.github.io/IntroToPython
-Also there are some excercise descriptions and supplemental materials.
+Also there are some exercise descriptions and supplemental materials.
The source of these materials are in the class gitHub repo:
https://github.com/UWPCE-PythonCert/IntroToPython
-Class email list: We will be using this list to communicate for this class:
+We also have a bunch of supplemental resources for the program here:
-programming-in-python@googlegroups.com
+http://uwpce-pythoncert.github.io/PythonResources/index.html
-You should have (or will soon) received and email invitation to join
-the mailing list.
+The source for those is here:
+https://github.com/UWPCE-PythonCert/PythonResources
Class Structure
---------------
@@ -114,7 +118,7 @@ Interrupt me with questions -- please!
Homework:
---------
-* Most homework will be reading, and the occasional Video
+* Homework will be reading, exercises, and the occasional Video
* Exercises will be started in class -- but you can finish them at home.
@@ -122,28 +126,41 @@ Homework:
* You can do a gitHub "pull request" if you want us to review your work.
- - We'll review how to do that in the second Session
+ - We'll show you how to do that in the second session
-Mailing list and Office Hours
-------------------------------
+Communication
+-------------
**Mailing list:**
-We've set up a google group -- you will all be invited to join:
+We've set up a Google Group for this class:
+
+programming-in-python@googlegroups.com
+
+We will be using this list to communicate with you. You should have (or will soon) received an email invitation to join the mailing list.
-``programming-in-python@googlegroups.com``
+Slack: We have set up a slack channel for discussions. Anything python related is fair game.
-**Office Hours:**
+https://python2016fall.slack.com/
-I generally will hold "office hours" at a coffee shop for a couple hours
+We highly encourage you to work together. You will learn at a much deeper level if you work together,
+and it gets you ready to collaborate with colleagues.
+
+
+Office Hours
+------------
+
+We will generally will hold "office hours" at a coffee shop for a couple hours
each weekend.
-Maria can do some as well.
+Please feel free to attend even if you do not have a specific question.
+It is an opportunity to work with the instructors and fellow students,
+and learn from each other.
What are good times for you?
-
+And what locations?
Lightning Talks
----------------
@@ -153,14 +170,12 @@ Lightning Talks
* 5 minutes each (including setup) - no kidding!
* Every student will give one
* Purposes: introduce yourself, share interests, show Python applications
- * Any topic you like, that is related to Python -- according to you!
-
+ * Any topic you like that is related to Python -- according to you!
Python Ecosystem
================
-
What is Python?
---------------
@@ -271,16 +286,16 @@ Python 3.x ("py3k")
.. nextslide::
-This class uses Python 3.4 -- not Python 2.*
-
-.. rst-class:: build
+This class uses Python 3 -- not Python 2
* Adoption of Python 3 is growing fast
* Almost all key packages now supported (https://python3wos.appspot.com/)
- * But most code in the wild is still 2.x
+ * But much code in the wild is still 2.x
+
+* If you find yourself needing to work with Python 2 and 3, there are ways to write compatible code:
-* If you find yourself needing to work with Python 2 and 3, there are ways to write compatible code: https://wiki.python.org/moin/PortingPythonToPy3k
+https://wiki.python.org/moin/PortingPythonToPy3k
* We will cover that more later in the program. Also: a short intro to the differences you really need to know about up front later this session.
@@ -307,9 +322,7 @@ Having some facility on the command line is important
We won't cover this much in class, so if you are not comfortable,
please bone up at home.
-I suggest running through the **cli** tutorial at "learn code the hard way":
-
-http://cli.learncodethehardway.org/book/
+We have some resources here: `PythonResources--command line `_
**Windows:**
@@ -319,38 +332,102 @@ Windows provides the "DOS" command line, which is OK, but pretty old and limited
If you are comfortable with either of these -- go for it.
-If not, you can use the "git Bash" shell -- which is much like the bash shell on OS-X and Linux.
+If not, you can use the "git Bash" shell -- which is much like the bash shell on OS-X and Linux. Or, on Windows 10, look into the "bash shell for Windows" otherwise known as the "Linux system" - - more info here: `PythonResources--Windows Bash `_
-Your Interpreter
-----------------
-Python comes with a built-in interpreter.
+LAB: Getting set up
+-------------------
+
+Before we move on -- we need to get all of us on the same page, with the tools we need for class.
-You see it when you type ``python`` at the command line:
+You will find instructions for how to get python, etc, up and running on your machine here:
-.. code-block:: python
+**Windows:**
+
+http://uwpce-pythoncert.github.io/PythonResources/Installing/python_for_windows.html
+
+**OS-X:**
+
+http://uwpce-pythoncert.github.io/PythonResources/Installing/python_for_mac.html
+
+**Linux:**
+
+http://uwpce-pythoncert.github.io/PythonResources/Installing/python_for_linux.html
+
+We'll run through some of that together.
+
+If you already have a working environment, please feel free to help your neighbor or look at the Python Resources pages, particularly reviewing/learning the shell and git.
+
+http://uwpce-pythoncert.github.io/PythonResources
+
+Our Class Environment
+---------------------
+
+We are going to work from a common environment in this class.
+
+We will take the time here in class to get this going.
+
+This helps to ensure that you will be able to work.
+
+
+Step 1: Python 3
+------------------
+
+.. rst-class:: medium
+
+ Do you already have this??
+
+.. code-block:: bash
$ python
- Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19)
+ Python 3.6.1 (v3.5.2:4def2a2901a5, Jun 26 2016, 10:47:25)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
+ >>>
-That last thing you see, ``>>>`` is the "Python prompt".
+If not, or you have an older version -- let's install it!
-This is where you type code.
+If you're all ready to go -- take this time to get started on a tutorial:
-LAB: Getting set up
--------------------
+http://uwpce-pythoncert.github.io/PythonResources/GeneralPython/learning.html#getting-started-tutorials
-BEfore we move on -- we need to get all of us on tehsame page, with teh tools we need for class.
-You will find instructions for how to get python, etc, up and running on your machine here:
+Step 2: Pip
+-----------
-**Windows:** ::ref:`python_for_windows`
+Python comes with quite a bit ("batteries included").
-**OS-X:** ::ref:`python_for_mac`
+Sometimes you need a bit more.
+
+Pip allows you to install Python packages to expand your system.
+
+The previous instructions include pip as well - make sure it's working.
+
+Once you've installed pip, you use it to install Python packages by name:
+
+.. code-block:: bash
+
+ $ python -m pip install foobar
+ ...
+
+To find packages (and their proper names), you can search the python
+package index (PyPI):
+
+https://pypi.python.org/pypi
+
+
+Step 3: Install iPython
+------------------------
+
+As this is an intro class, we are going to use almost entirely features
+of the standard library. But there are a couple things you may want:
+
+**iPython** is an "enhanced python shell" -- it make s it easier to work with python interactively.
+
+.. code-block:: bash
+
+ $ python -m pip install ipython[all]
-**Linux:** ::ref:`python_for_linux`
Python in the Interpreter
-------------------------
@@ -427,8 +504,7 @@ Your Editor
Typing code in an interpreter is great for exploring.
-But for anything "real", you'll want to save the work you are doing in a more permanent
-fashion.
+But for anything "real", you'll want to save the work you are doing in a more permanent fashion.
This is where an Editor fits in.
@@ -440,7 +516,7 @@ MS Word is **not** a text editor.
Nor is *TextEdit* on a Mac.
-``Notepad`` is a text editor -- but a crappy one.
+``Notepad`` on Windows is a text editor -- but a crappy one.
You need a real "programmers text editor"
@@ -449,7 +525,6 @@ characters hidden behind the scenes.
.. nextslide:: Minimum Requirements
-
At a minimum, your editor should have:
.. rst-class:: build
@@ -463,16 +538,19 @@ In addition, great features to add include:
* Tab completion
* Code linting
-* Jump-to-definition
Have an editor that does all this? Feel free to use it.
-If not, I suggest ``SublimeText``:
-
-http://www.sublimetext.com/
+If not, I suggest ``SublimeText``: http://www.sublimetext.com/
(Use version 3, even though it's "beta")
+http://uwpce-pythoncert.github.io/PythonResources/DevEnvironment/sublime_as_ide.html
+
+"Atom" is another good open source option.
+
+And, of course, vi or Emacs on Linux, if you are familiar with those.
+
Why No IDE?
-----------
@@ -493,83 +571,6 @@ Particularly when you are first learning, you don't want too much done for you.
YAGNI
-Setting Up Your Environment
-===========================
-
-.. rst-class:: centered large
-
-Shared setup means reduced complications.
-
-
-Our Class Environment
----------------------
-
-We are going to work from a common environment in this class.
-
-We will take the time here in class to get this going.
-
-This helps to ensure that you will be able to work.
-
-
-Step 1: Python 3.4
-------------------
-
-.. rst-class:: large
-
-Do you already have this??
-
-.. code-block:: bash
-
-
- $ python
- Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19)
- [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
- Type "help", "copyright", "credits" or "license" for more information.
- >>> ^D
-
-If not:
-
- * `For the mac <./supplements/python_for_mac.html>`_
-
- * `For linux <./supplements/python_for_linux.html>`_
-
- * `For windows <./supplements/python_for_windows.html>`_
-
-Step 2: Pip
------------
-
-Python comes with quite a bit ("batteries included").
-
-Sometimes you need a bit more.
-
-Pip allows you to install Python packages to expand your system.
-
-The previous instructions include pip as well - make sure it's working.
-
-Once you've installed pip, you use it to install Python packages by name:
-
-.. code-block:: bash
-
- $ python -m pip install foobar
- ...
-
-To find packages (and their proper names), you can search the python
-package index (PyPI):
-
-https://pypi.python.org/pypi
-
-
-Step 3: Install iPython
-------------------------
-
-As this is an intro class, we are going to use almost entirely features
-of standard library. But there are a couple things you may want:
-
-**iPython** is an "enhanced python shell" -- it make s it easier to work with python interatively.
-
-.. code-block:: bash
-
- $ python -m pip install ipython
Introduction to iPython
=======================
@@ -588,8 +589,8 @@ Specifically, you'll want to pay attention to the information about
`Using iPython for Interactive Work`_.
.. _iPython: http://ipython.org
-.. _official documentation: http://ipython.org/ipython-doc/stable/index.html
-.. _Using iPython for Interactive Work: http://ipython.org/ipython-doc/stable/interactive/index.html
+.. _official documentation: http://ipython.readthedocs.io/en/stable/
+.. _Using iPython for Interactive Work: http://ipython.readthedocs.io/en/stable/interactive/index.html
.. ifslides::
@@ -678,7 +679,19 @@ To run it, you have a couple options:
$ python the_name_of_the_script.py
-2) run ``iPython``, and run it from within iPython with the ``run`` command
+2) On *nix (linux, OS-X, Windows bash), you can make the file "executable"::
+
+ chmod +x the_file.py
+
+ And make sur it has a "shebang" line at the top::
+
+ #!/usr/bin/env python
+
+ Then you can run it directly::
+
+ ./the_file.py
+
+3) run ``iPython``, and run it from within iPython with the ``run`` command
.. code-block:: ipython
@@ -698,7 +711,7 @@ Basic Python Syntax
.. rst-class:: center mlarge
-| Values, Types, and Symbols
+| Values, Types, and Names
|
| Expressions and Statements
@@ -708,15 +721,13 @@ Values
All of programming is really about manipulating values.
-.. rst-class:: build
-
-* Values are pieces of unnamed data: ``42, 'Hello, world',``
+* Values are pieces of unnamed data: ``42``, ``'Hello, world'``
* In Python, all values are objects
- Try ``dir(42)`` - lots going on behind the curtain!
-* Every value belongs to a type
+* Every value has a type
- Try ``type(42)`` - the type of a value determines what it can do
@@ -743,8 +754,10 @@ Boolean values:
- ``True``
- ``False``
-(There are intricacies to all of these that we'll get into later)
+The nothing object:
+ - ``None``
+(There are intricacies to all of these that we'll get into later)
Code structure
--------------
@@ -785,7 +798,7 @@ It's kind of obvious, but handy when playing with code:
.. code-block:: ipython
- In [1]: print ("something")
+ In [1]: print("something")
something
You can print multiple things:
@@ -889,6 +902,7 @@ Make sure your editor is set to use spaces only --
Even when you hit the key
+[Python itself allows any number of spaces (and tabs), but you are just going to confuse yourself and others if you do anything else]
Expressions
------------
@@ -904,8 +918,7 @@ An *expression* is made up of values and operators.
* Integer vs. float arithmetic
* (Python 3 smooths this out)
- * Always use ``/`` when you want float results, ``//`` when you want
- floored (integer) results
+ * Always use ``/`` when you want division with float results, ``//`` when you want floored (integer) results (no remainder).
* Type conversions
@@ -920,31 +933,31 @@ An *expression* is made up of values and operators.
[demo]
-Symbols
+Names
-------
-Symbols are how we give names to values (objects).
+Names are how we give names to values (objects) -- hence "names"
.. rst-class:: build
-* Symbols must begin with an underscore or letter
-* Symbols can contain any number of underscores, letters and numbers
+* Names must begin with an underscore or letter
+* Names can contain any number of underscores, letters and numbers
- * this_is_a_symbol
+ * this_is_a_name
* this_is_2
* _AsIsThis
* 1butThisIsNot
* nor-is-this
-* Symbols don't have a type; values do
+* Names don't have a type; values do
* This is why python is "Dynamic"
-Symbols and Type
+Names and Type
----------------
-Evaluating the type of a *symbol* will return the type of the *value* to which
+Evaluating the type of a *name* will return the type of the *value* to which
it is bound.
.. code-block:: ipython
@@ -967,11 +980,12 @@ it is bound.
In [26]: type(a)
Out[26]: float
+*wait!* a has a different type?!? -- yes, because it's the type of teh value: "3.14", names don't actually have a type, they can refer to any type.
Assignment
----------
-A *symbol* is **bound** to a *value* with the assignment operator: ``=``
+A *name* is **bound** to a *value* with the assignment operator: ``=``
.. rst-class:: build
@@ -979,7 +993,6 @@ A *symbol* is **bound** to a *value* with the assignment operator: ``=``
* A value can have many names (or none!)
* Assignment is a statement, it returns no value
-
.. nextslide::
Evaluating the name will return the value to which it is bound
@@ -1006,7 +1019,7 @@ Variables?
.. rst-class:: build
-* In most languages, what I'm calling symbols, or names, are called "variables".
+* In most languages, what I'm calling names are called "variables".
* In fact, I'll probably call them variables in this class.
@@ -1092,12 +1105,12 @@ Using this feature, we can swap values between two names in one statement:
In [55]: j
Out[55]: 4
-Multiple assignment and symbol swapping can be very useful in certain contexts
+Multiple assignment and name swapping can be very useful in certain contexts
Deleting
--------
-You can't actually delete anything in python...
+You can't actually directly delete values in python...
``del`` only deletes a name (or "unbinds" the name...)
@@ -1148,7 +1161,7 @@ Identity
Every value in Python is an object.
Every object is unique and has a unique *identity*, which you can inspect with
-the ``id`` *builtin*:
+the ``id`` *builtin* function:
.. code-block:: ipython
@@ -1167,7 +1180,7 @@ the ``id`` *builtin*:
Testing Identity
----------------
-You can find out if the values bound to two different symbols are the **same
+You can find out if the values bound to two different names are the **same
object** using the ``is`` operator:
.. code-block:: ipython
@@ -1190,6 +1203,7 @@ object** using the ``is`` operator:
[demo]
+**NOTE:** checking the id of an object, or using "is" to check if two objects are the same is rarely used except for debugging and understanding what's going on under the hood. They are not used regularly in production code.
Equality
--------
@@ -1210,12 +1224,34 @@ You can test for the equality of certain values with the ``==`` operator
In [81]: val1 == val3
Out[84]: False
+A string is never equal to a number!
+
.. ifslides::
.. rst-class:: centered
[demo]
+For the numerical values, there is also::
+
+ >, <, >=, <=, !=
+
+Singletons
+----------
+
+Python has three "singletons" -- value fro which there is only one instance:
+
+ ``True``, ``False``, and ``None``
+
+To check if a name is bound to one of these, you use ``is``::
+
+ a is True
+
+ b is False
+
+ x is None
+
+Note that in contrast to english -- "is" is asking a question, not making an assertion -- ``a is True`` means "is a the True value?"
Operator Precedence
-------------------
@@ -1226,8 +1262,11 @@ Operator Precedence determines what evaluates first:
4 + 3 * 5 != (4 + 3) * 5
-To force statements to be evaluated out of order, use parentheses.
+To force statements to be evaluated out of order, use parentheses -- expressions in parentheses are always evaluated first:
+ (4 + 3) * 5 != 4 + (3 * 5)
+
+Python follows the "usual" rules of algebra.
Python Operator Precedence
--------------------------
@@ -1289,7 +1328,7 @@ String Literals
A "string" is a chunk of text.
-You define a ``string`` value by writing a string *literal*:
+You define a "string" value by writing a string *literal*:
.. code-block:: ipython
@@ -1322,6 +1361,10 @@ You define a ``string`` value by writing a string *literal*:
In [7]: r'a "raw" string, the \n comes through as a \n'
Out[7]: 'a "raw" string, the \\n comes through as a \\n'
+Python3 strings are fully support Unicode, which means that it can suport literally all the languages in the world (and then some -- Klingon, anyone?)
+
+Because Unicode is native, you can get very far without even thinking about it. Anything you can type in your editor will work fine.
+
Keywords
--------
@@ -1330,7 +1373,7 @@ Python defines a number of **keywords**
These are language constructs.
-You *cannot* use these words as symbols.
+You *cannot* use these words as names.
::
@@ -1345,7 +1388,7 @@ You *cannot* use these words as symbols.
.. nextslide::
-If you try to use any of the keywords as symbols, you will cause a
+If you try to use any of the keywords as names, you will cause a
``SyntaxError``:
.. code-block:: ipython
@@ -1370,7 +1413,7 @@ If you try to use any of the keywords as symbols, you will cause a
__builtins__
------------
-Python also has a number of pre-bound symbols, called **builtins**
+Python also has a number of pre-bound names, called **builtins**
Try this:
@@ -1384,14 +1427,13 @@ Try this:
'BaseException',
'BufferError',
...
- 'unicode',
'vars',
'xrange',
'zip']
.. nextslide::
-You are free to rebind these symbols:
+You are free to rebind these names:
.. code-block:: ipython
@@ -1408,7 +1450,7 @@ You are free to rebind these symbols:
TypeError: 'str' object is not callable
-In general, this is a **BAD IDEA**.
+In general, this is a **BAD IDEA** -- hopefully your editor will warn you.
Exceptions
@@ -1422,9 +1464,12 @@ There are several exceptions that you are likely to see a lot of:
.. rst-class:: build
-* ``NameError``: indicates that you have tried to use a symbol that is not bound to a value.
+* ``NameError``: indicates that you have tried to use a name that is not bound to a value.
+
* ``TypeError``: indicates that you have tried to use the wrong kind of object for an operation.
+
* ``SyntaxError``: indicates that you have mis-typed something.
+
* ``AttributeError``: indicates that you have tried to access an attribute or
method that an object does not have (this often means you have a different
type of object than you expect)
@@ -1442,7 +1487,7 @@ A function is a self-contained chunk of code
You use them when you need the same code to run multiple times,
or in multiple parts of the program.
-(DRY)
+(DRY) -- "Don't Repeat Yourself"
Or just to keep the code clean
@@ -1450,22 +1495,23 @@ Functions can take and return information
.. nextslide::
-Minimal Function does nothing
+The minimal Function has at least one statement
.. code-block:: python
- def ():
-
+ def a_name():
+ a_statement
.. nextslide::
-Pass Statement (Note the indentation!)
+Pass Statement does nothing (Note the indentation!)
.. code-block:: python
def minimal():
pass
+This, or course, is not useful -- you will generally have multiple statements in a function.
Functions: ``def``
------------------
@@ -1477,6 +1523,7 @@ Functions: ``def``
* it is executed
* it creates a local name
* it does *not* return a value
+
.. nextslide::
function defs must be executed before the functions can be called:
@@ -1510,17 +1557,20 @@ You **call** a function using the function call operator (parens):
In [2]: type(simple)
Out[2]: function
+
In [3]: simple
Out[3]:
+
In [4]: simple()
I am a simple function
Calling a function is how you run the code in that function.
+
Functions: Call Stack
---------------------
-functions call functions -- this makes an execution stack -- that's all a trace back is
+functions can call other functions -- this makes an execution stack -- that's what a "trace back" is:
.. code-block:: ipython
@@ -1565,7 +1615,7 @@ Functions: Tracebacks
ZeroDivisionError: integer division or modulo by zero
-
+The error occurred in the ``doer`` function -- but the traceback shows you where that was called from. In a more complex system, this can be VERY useful -- learn to read tracebacks!
Functions: ``return``
---------------------
@@ -1581,7 +1631,7 @@ This is actually the simplest possible function:
.. nextslide::
-if you don't explicilty put ``return`` there, Python will:
+if you don't explicitly put ``return`` there, Python will return None for you:
.. code-block:: ipython
@@ -1593,7 +1643,7 @@ if you don't explicilty put ``return`` there, Python will:
In [12]: print(result)
None
-note that the interpreter eats ``None`` -- ou need to call ``print()`` to see it.
+note that the interpreter eats ``None`` -- you need to call ``print()`` to see it.
.. nextslide::
@@ -1624,7 +1674,7 @@ However, functions *can* return multiple results:
.. code-block:: ipython
In [16]: def fun():
- ....: return (1, 2, 3)
+ ....: return 1, 2, 3
....:
In [17]: fun()
Out[17]: (1, 2, 3)
@@ -1636,7 +1686,7 @@ Remember multiple assignment?
.. code-block:: ipython
- In [18]: x,y,z = fun()
+ In [18]: x, y, z = fun()
In [19]: x
Out[19]: 1
In [20]: y
@@ -1672,7 +1722,20 @@ When you call a function, you pass values to the function parameters as
In [23]: fun(3, 4, 5)
3 4 5 12
-The values you pass in are *bound* to the symbols inside the function and used.
+The values you pass in are *bound* to the names inside the function and used.
+
+The name used outside the object is separete from the name used inside teh function:
+
+.. code-block:: python
+
+ x = 5
+
+ def fun(a):
+ print(a)
+
+ fun(x)
+
+The "a" printed inside the function is the *same* object as the "x" outside the function.
The ``if`` Statement
---------------------
@@ -1681,7 +1744,7 @@ In order to do anything interesting at all, you need to be able to make a decisi
.. nextslide::
-.. code-block:: python
+.. code-block:: ipython
In [12]: def test(a):
....: if a == 5:
@@ -1727,7 +1790,7 @@ Python 2-3 Differences
Much of the example code you'll find online is Python2, rather than Python3
-For the most part, they are the same -- so you can sue those examples to learn from.
+For the most part, they are the same -- so you can use those examples to learn from.
There are a lot of subtle differences that you don't need to concern yourself with just yet.
@@ -1736,7 +1799,7 @@ But a couple that you'll need to know right off the bat:
print()
-------
-In python2, ``print`` is a "statement", rather than a function. That means it didn't require parenthes around what you want printed::
+In python2, ``print`` is a "statement", rather than a function. That means it didn't require parentheses around what you want printed::
print something, something_else
@@ -1757,19 +1820,19 @@ So -- if you get this error, simply add the parentheses::
.. nextslide:: division
-In python 3, the divsion operator is "smart" when you divide integers::
+In python 3, the division operator is "smart" when you divide integers::
In [17]: 1 / 2
Out[17]: 0.5
-However in python2, if you use integers, is will give you an integer result::
+However in python2, integer division, will give you an integer result::
In [1]: 1/2
Out[1]: 0
In both versions, you can get "integer division" if you want it with a double slash::
- In [1]: 1/2
+ In [1]: 1//2
Out[1]: 0
And in python2, you can get the behavior of py3 with "true division"::
@@ -1805,7 +1868,7 @@ Task 1
Make sure you have the basics of command line usage down:
Work through the supplemental tutorials on setting up your
-Command Line (::ref:`shell_customization`) for good development support.
+Command Line (http://uwpce-pythoncert.github.io/PythonResources/DevEnvironment/shell.html) for good development support.
Make sure you've got your editor set up productively -- at the very very
least, make sure it does Python indentation and syntax coloring well.
@@ -1816,7 +1879,7 @@ least, make sure it does Python indentation and syntax coloring well.
If you are using SublimeText, here are some notes to make it super-nifty:
-::ref:`sublime_as_ide`
+http://uwpce-pythoncert.github.io/PythonResources/DevEnvironment/sublime_as_ide.html
At the end, your editor should support tab completion and pep8 and pyflakes
linting.
@@ -1875,22 +1938,23 @@ Every one of you has a different backgrond and learning style.
So take a bit of time to figure out which resource works for you.
-::ref:`python_learning_resources` provides some options. Do look it over.
+http://uwpce-pythoncert.github.io/PythonResources/GeneralPython/learning.html
+
+provides some options. Do look it over.
But here are few to get you started this week:
-*Think Python:* Chapters 1–7 (http://greenteapress.com/thinkpython/)
+*Think Python:* Chapters 1–7 (http://greenteapress.com/wp/think-python-2e/)
*Dive Into Python:* Chapters 1–2 (http://www.diveintopython3.net/)
*LPTHW:* ex. 1–10, 18-21 (http://learnpythonthehardway.org/book/)
- **NOTE:** LPTHW is python 2 -- you will need to add parentheses to all yoru print calls!
-Or follow this excellent introductory tutorial:
+*NOTE:* LPTHW is python 2 -- you will need to add parentheses to all your print calls!
-http://pyvideo.org/video/1850/a-hands-on-introduction-to-python-for-beginning-p
+Or follow this excellent introductory tutorial:
-(also python2 -- so same thing with the print function...)
+https://www.youtube.com/watch?v=MirG-vJOg04
You should be comfortable with working with variables, numbers, strings, and basic functions.
@@ -1911,7 +1975,7 @@ Next Class
Next week, we'll:
- * get set up with git
+ * get set up with git and gitHub
* Some more basic Python
* More on Functions
* Boolean Expressions
@@ -1921,13 +1985,11 @@ Next week, we'll:
Office Hours
------------
-I'll do office hours on either Saturday or Sunday from 12:00 -- 3:00
+We will have office hours on either Saturday or Sunday from 10:00 to noon.
-Probably in Wallingford or Fremont
- - is a coffeeshop OK?
+Preferences?
-Do you have a preference?
+Locations?
-Maria's office hours??
diff --git a/slides_sources/source/session02.rst b/slides_sources/source/session02.rst
index 703f5a95..a5199d68 100644
--- a/slides_sources/source/session02.rst
+++ b/slides_sources/source/session02.rst
@@ -1,3 +1,5 @@
+.. include:: include.rst
+
****************************************************
Session Two: gitHub, Functions, Booleans and Modules
****************************************************
@@ -23,15 +25,15 @@ Lightning Talks Today:
.. rst-class: medium
+
|
+| David E Tobey
|
-| Brendan Fogarty
-|
-| Bruce Bauman
-|
-| Michelle Yu
+| Sharmila Muralidharan
|
+| Shu A Latif
|
+| Spencer G McGhin
Class Outline
-------------
@@ -78,7 +80,7 @@ What is git?
That last one is a bit tricky, and is not necessary to understand right out of the gate. When you are ready, you can look at this supplement to gain a better understanding:
- :ref:`supplement_git_states`
+ :ref: http://uwpce-pythoncert.github.io/PythonResources/DevEnvironment/git_overview.html
Setting up git
--------------
@@ -93,8 +95,18 @@ You should have git installed on your machine and accessible from the command li
Editor
------
-You will never have to use an editor with git for anything extensive, so a simple editor is fine. Unfortunately, the default, VI, is not intuitive to new users. So, let's set up a different editor, before you find yourself accidentally stuck in VI land.
-Nano is a very straight-forward, simple editor, available without installing anything on Macs and Linux boxes, but needs to be installed on Windows (or you can use sublime or Notepad++ as shown below). To install nano on Windows: :ref:`supplement_install_nano_win`
+* git needs an editor occasionally
+* default is VI, which is not very intuitive to non-Unix Geeks
+* Nano is simple, easy solution for Macs and Linux
+* Nano no longer available for windows, use Sublime or Notepad++
+
+
+For Windows users:
+ http://uwpce-pythoncert.github.io/PythonResources/Installing/git_editor_windows.html
+
+.. nextslide::
+
+Once you have chosen/installed an editor, configure git to use it:
nano
``$ git config --global core.editor "nano -w"``
@@ -116,7 +128,7 @@ A repository is just a collection of files that 'belong together'.
Since ``git`` is a *distributed* versioning system, there is no **central**
repository that serves as the one to rule them all. This simply means that all repositories should look the same.
-However, to keep things sane, there is generally one repository chosen that users check with for changes, for us this is GitHub.
+However, to keep things sane, there is generally one "central" repository chosen that users check with for changes, for us this is GitHub.
Working with Remotes
@@ -132,11 +144,12 @@ With git, you work with *local* repositories and *remotes* that they are connect
.. code-block:: bash
$ git remote -v
- origin https://github.com/UWPCE-PythonCert/IntroToPython.git (fetch)
- origin https://github.com/UWPCE-PythonCert/IntroToPython.git (push)
+ origin https://github.com/UWPCE-PythonCert/IntroPython2016.git (fetch)
+ origin https://github.com/UWPCE-PythonCert/IntroPython2016.git (push)
This shows that the local repo on my machine *originated* from the one in
- the UWPCE-PythonCert gitHub account (don't worry that it shows it twice, they should be the same)
+ the UWPCE-PythonCert gitHub account (it shows up twice, because I there is
+ a shortcut for both fetch from and push to this remote)
.. nextslide::
@@ -161,8 +174,32 @@ Our class materials reside in a repository on *Github* in the *UWPCE-PythonCert*
.. nextslide::
+Note that this is not the same repository as the class materials.
+
+It will be a repository that is created just for this class, and will be called IntroPython*quarter*.
+
+In examples below it is called IntroToPython, so replace that in your head with the name of this year's repository. :)
+
+We will create this repository now.
+
+.. nextslide::
+
+This new repository will include examples and we will add relevant materials (and exercise solutions) to it throughout the quarter.
+
+There will be a folder called students at the top level, and everyone will create their own directory within it.
+
+So, everyone will commit to this repository, and everyone will have access to everyone's code.
+
+This will make it easier to collaborate.
+
+We will do a live demo of setting up a machine now.
+
+.. nextslide::
+
We will now create a fork of the class repository from the ``UWPCE-PythonCert``
-account on GitHub into your personal account:
+account on GitHub into your personal account. This is done on the GitHub website.
+
+Let's pause now to let you all create a gitHub account if you don't have one already.
.. figure:: /_static/remotes_fork.png
:width: 50%
@@ -170,8 +207,7 @@ account on GitHub into your personal account:
.. nextslide::
-The next step is to make a *clone* of your fork on your own computer, which means that
-**your fork** in github is the *origin*:
+The next step is to make a *clone* of your fork on your own computer, which means that **your fork** in github is the *origin* (Demo):
.. figure:: /_static/remotes_clone.png
:width: 50%
@@ -179,21 +215,25 @@ The next step is to make a *clone* of your fork on your own computer, which mean
.. nextslide::
-We will now set up our individual folders and include a README in this folder. In your terminal, cd
-into the students directory in the directory created when we made the clone, which may or may not be called IntroPython2015.
+We will now set up our individual folders and include a README in this folder.
+
.. rst-class:: build
.. container::
.. code-block:: bash
- $ cd IntroPython2015/students
+ $ cd IntroPythonXXXX
$ git status
.. code-block:: bash
$ git pull origin master
+ .. code-block:: bash
+
+ $ cd students
+
.. code-block:: bash
$ mkdir maria_mckinley
@@ -229,17 +269,30 @@ into the students directory in the directory created when we made the clone, whi
$ git commit -m 'added a readme file'
- Push your changes:
- .. code-block:: bash
+.. nextslide::
+
+Push your changes:
+
+ .. code-block:: bash
+
+ $ git push origin master
+
+ origin is the default name given by git refering to the server you cloned
+ (in this case your github repository)
- $ git push origin master
+ master is the branch that you are currently pushing to that server
+
+ Go onto GitHub, and make a pull request!
+
+ (This will be a pull request from a fork rather than from a branch)
+
+ https://help.github.com/articles/creating-a-pull-request-from-a-fork/
- Go onto GitHub, and make a pull request!
.. nextslide::
-You've pushed your own changes to that fork, and then issued pull requests to have that worked merged back to the ``UWPCE-PythonCert`` original.
+You've pushed your own changes to that fork, and then issued pull requests to have that work merged back to the ``UWPCE-PythonCert`` original.
.. rst-class:: build
.. container::
@@ -254,26 +307,23 @@ You've pushed your own changes to that fork, and then issued pull requests to ha
You can add *remotes* at will, to connect your *local* repository to other
copies of it in different remote locations.
-When you add a *remote* (existing git repository), it creates a directory with the name of the repository at the top level.
-
.. rst-class:: build
.. container::
This allows you to grab changes made to the repository in these other
locations.
- For our class, we will add an *upstream* remote to our local copy that points
- to the original copy of the material in the ``UWPCE-PythonCert`` account.
+ For our class, we will add an *upstream* remote to our local copy that points to the original copy of the material in the ``UWPCE-PythonCert`` account, and we will call it, appropriately, "upstream"
.. code-block:: bash
- $ git remote add upstream https://github.com/UWPCE-PythonCert/IntroToPython.git
+ $ git remote add upstream https://github.com/UWPCE-PythonCert/IntroPython2015.git
$ git remote -v
- origin https://github.com/PythonCHB/IntroToPython.git (fetch)
- origin https://github.com/PythonCHB/IntroToPython.git (push)
- upstream https://github.com/UWPCE-PythonCert/IntroToPython.git (fetch)
- upstream https://github.com/UWPCE-PythonCert/IntroToPython.git (push)
+ origin https://github.com/PythonCHB/IntroPython2015.git (fetch)
+ origin https://github.com/PythonCHB/IntroPython2015.git (push)
+ upstream https://github.com/UWPCE-PythonCert/IntroPython2015.git (fetch)
+ upstream https://github.com/UWPCE-PythonCert/IntroPython2015.git (push)
.. nextslide::
@@ -315,20 +365,16 @@ Start by making sure you are on your own master branch:
$ git checkout master
-This is **really really** important. Take the time to ensure you are where you
-think you are.
+This is **really really** important. Take the time to ensure you are where you think you are, iow, not on a remote. Use git status to find out where you are, if necesary.
.. nextslide:: Merging Upstream Changes
-Then, fetch the upstream master branch and merge it into your master:
+Then, fetch the upstream master branch and merge it into your master.
+You can do this in one step with:
.. code-block:: bash
- $ git fetch upstream master
- From https://github.com/UWPCE-PythonCert/IntroToPython
- * branch master -> FETCH_HEAD
-
- $ git merge upstream/master
+ $ git pull upstream master
Updating 3239de7..9ddbdbb
Fast-forward
Examples/README.rst | 4 ++++
@@ -336,15 +382,11 @@ Then, fetch the upstream master branch and merge it into your master:
create mode 100644 Examples/README.rst
...
-NOTE: you can do that in one step with:
-
-.. code-block:: bash
-
- $ git pull upstream master
.. nextslide:: Pushing to Origin
Now all the changes from *upstream* are present in your local clone.
+You should do this pull everytime you start to work on code.
In order to preserve them in your fork on GitHub, you'll have to push:
@@ -365,14 +407,33 @@ In order to preserve them in your fork on GitHub, you'll have to push:
You can incorporate this into your daily workflow: ::
+ [make sure you are on correct branch]
$ git checkout master
+ [get any changes from class repository]
$ git pull upstream master
- $ git push
- [do some work]
- $ git commit -a
+ [make sure you are in your student directory, do work]
+ [verify you are happy with changes]
+ $ git status
+ [add your changes to what will be committed]
+ $ git add .
[add a good commit message]
+ $ git commit -m 'I wrote some Python.'
+ [push your changes to your remote github account]
$ git push
- [make a pull request]
+ [make a pull request on the GitHub website]
+
+
+.. nextslide:: Note
+
+Because of the way we have set up the class, you will be able
+to see all work submitted to us from everyone in the class in
+the students directory on your machine. This is not a bad thing.
+And the files tend to be small.
+
+We encourage sharing of knowledge in this class. Helping your
+fellow students will also help you to better understand. Share
+your code, and get use to giving/receiving feedback on how to
+improve your code, if you are not already.
LAB: Grid Printer
@@ -395,8 +456,8 @@ Start by putting a python file in your clone of the class gitHub project:
.. code-block:: bash
$ cd my_personal_directory
- $ mkdir session_01
- $ cd session_01
+ $ mkdir session_02
+ $ cd session_02
$ touch grid_printer.py
$ git add grid_printer.py
@@ -405,7 +466,7 @@ Then put your code in grid_printer.py
Committing your code
--------------------
-When it does something useful, you can commit it.
+When your code does something useful, you can commit it.
First check the status:
@@ -413,19 +474,21 @@ First check the status:
$ git status
-If it's what you expect, you can push:
+If it's what you expect, you can commit and push:
.. code-block:: bash
$ git commit -a -m "first version"
+ $ git push
-And when you want us to take a look, push it to gitHub:
+And when you want us to take a look, you can go to gitHub and do a "Pull Request"
+(make sure you commit and push first)
-.. code-block:: bash
- $ git push
+Committing your code
+--------------------
-Then you can go to gitHub and do a "Pull Request"
+Commit early and often.
Lightning Talk:
@@ -433,7 +496,7 @@ Lightning Talk:
.. rst-class:: center medium
-Brendan Fogarty
+David E Tobey
Beyond Printing
@@ -460,13 +523,13 @@ Making a Decision
.. code-block:: python
if a:
- print 'a'
+ print('a')
elif b:
- print 'b'
+ print('b')
elif c:
- print 'c'
+ print('c')
else:
- print 'that was unexpected'
+ print('that was unexpected')
.. nextslide:: if
@@ -476,15 +539,15 @@ What's the difference between these two?
.. code-block:: python
if a:
- print 'a'
+ print('a')
elif b:
- print 'b'
+ print('b')
## versus...
if a:
- print 'a'
+ print('a')
if b:
- print 'b'
+ print('b')
@@ -540,7 +603,8 @@ When you need to do something to all the objects in a sequence
5
-.. nextslide:: ``range()`` and for
+``range()`` and for
+-------------------
``range`` builds sequences of numbers automatically
@@ -584,7 +648,7 @@ Lightning Talk:
.. rst-class:: center medium
-Bruce Bauman
+Sharmila Muralidharan
More on Functions
@@ -887,8 +951,7 @@ Recursion is especially useful for a particular set of problems.
For example, take the case of the *factorial* function.
-In mathematics, the *factorial* of an integer is the result of multiplying that
-integer by every integer smaller than it down to 1.
+In mathematics, the *factorial* of an integer is the result of multiplying that integer by every integer smaller than it down to 1.
::
@@ -941,7 +1004,7 @@ Lightning Talk:
.. rst-class:: center medium
-Michelle Yu
+Shu A Latif
Boolean Expressions
@@ -1176,6 +1239,14 @@ And you can even do math with them (though it's a bit odd to do so):
(demo)
+Lightning Talk:
+---------------
+
+.. rst-class:: center medium
+
+Spencer G McGhin
+
+
LAB: Booleans
=============
@@ -1187,7 +1258,7 @@ LAB: Booleans
Experiment with ``locals`` by adding this statement one of the functions you wrote today::
- print locals()
+ print(locals())
Code Structure, Modules, and Namespaces
@@ -1223,7 +1294,7 @@ You can put a one-liner after the colon:
.. code-block:: ipython
In [167]: x = 12
- In [168]: if x > 4: print x
+ In [168]: if x > 4: print(x)
12
But this should only be done if it makes your code **more** readable.
@@ -1368,8 +1439,8 @@ It must be explicitly reloaded to be re-run
.. code-block:: python
- import modulename
- reload(modulename)
+ import importlib
+ importlib.reload(modulename)
.. ifslides::
@@ -1392,7 +1463,7 @@ There are a few ways to do this:
* ``In [149]: run hello.py`` -- at the IPython prompt -- running a module brings its names into the interactive namespace
-.. nextslide:: Running a Module
+.. nextslide
Like importing, running a module executes all statements at the module level.
@@ -1470,9 +1541,9 @@ Experiment with importing different ways:
.. code-block:: python
import sys
- print sys.path
+ print(sys.path)
import os
- print os.path
+ print(os.path)
You wouldn't want to import * those!
@@ -1494,9 +1565,12 @@ Next Class
* Strings and String Formatting
* Lightning talks by:
- - Eric Rosko
- - Michael Waddle
- - Robert Stevens Alford
+
+ - Beatrice He
+ - Bradley I Baumel
+ - Jerry Bearer
+ - Sheree Pena
+
Office hours: Sunday 10:00 -- 12:00
diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst
index 9d0ed8fc..bd58f2ff 100644
--- a/slides_sources/source/session03.rst
+++ b/slides_sources/source/session03.rst
@@ -1,3 +1,5 @@
+.. include:: include.rst
+
*********************************************************
Session Three: Sequences, Iteration and String Formatting
*********************************************************
@@ -8,12 +10,9 @@ Review/Questions
Review of Previous Session
--------------------------
-.. rst-class:: build
-
* Functions
- recursion
-
- optional arguments
* Booleans
@@ -28,8 +27,6 @@ Homework Review
* FizzBuzz
-* Ackerman
-
* Series
.. rst-class:: center large
@@ -41,20 +38,18 @@ git
.. rst-class:: center large
- OK -- I'll answer git questions...
+ OK -- we'll answer git questions...
Lightning Talks Today:
----------------------
.. rst-class:: mlarge
- James Brent Nunn
-
- Lauren Fries
-
- Lesley D Reece
-
- Michel Claessens
+|
+| Beatrice He
+| Bradley I Baumel
+| Jerry Bearer
+| Sheree Pena
Sequences
@@ -86,26 +81,27 @@ A *sequence* can be considered as anything that supports
Sequence Types
--------------
-There are seven builtin types in Python that are *sequences*:
+There are eight builtin types in Python that are *sequences*:
-* strings
-* Unicode strings
-* lists
-* tuples
-* bytearrays
-* buffers
-* array.arrays
-* xrange objects (almost)
+* string
+* list
+* tuple
+* bytes
+* bytearray
+* buffer
+* array.array
+* range object (almost)
-For this class, you won't see much beyond the string types, lists, tuples -- the rest are pretty special purpose.
+For this class, you won't see much beyond string, lists, and tuples --
+the rest are pretty special purpose.
-But what we say today applies to all sequences (with minor caveats)
+But what we learn today applies to all sequences (with minor caveats)
Indexing
--------
-Items in a sequence may be looked up by *index* using the subscription
+Items in a sequence may be looked up by *index* using the indexing
operator: ``[]``
Indexing in Python always starts at zero.
@@ -159,7 +155,7 @@ Slicing
Slicing a sequence creates a new sequence with a range of objects from the
original sequence.
-It also uses the subscription operator (``[]``), but with a twist.
+It also uses the indexing operator (``[]``), but with a twist.
``sequence[start:finish]`` returns all sequence[i] for which start <= i < finish:
@@ -215,11 +211,11 @@ Why start from zero?
Python indexing feels 'weird' to some folks -- particularly those that don't come with a background in the C family of languages.
-Why is the "first" item indexed with zero?
+Why is the "first" item indexed with **zero**?
Why is the last item in the slice **not** included?
-Because these lead to some nifty properties::
+*Because* these lead to some nifty properties::
len(seq[a:b]) == b-a
@@ -274,7 +270,7 @@ Indexing past the end of a sequence will raise an error, slicing will not:
In [131]: s[10:20]
Out[131]: ' words'
In [132]: s[20:30]
- Out[132]: "
+ Out[132]: ''
(demo)
@@ -321,13 +317,12 @@ Using ``+`` or ``*`` on sequences will *concatenate* them:
.. code-block:: ipython
- In [25]: s1 = "left"
- In [26]: s2 = "right"
- In [27]: s1 + s2
- Out[27]: 'leftright'
- In [28]: (s1 + s2) * 3
- Out[28]: 'leftrightleftrightleftright'
-
+ In [18]: l1 = [1,2,3,4]
+ In [19]: l2 = [5,6,7,8]
+ In [20]: l1 + l2
+ Out[20]: [1, 2, 3, 4, 5, 6, 7, 8]
+ In [21]: (l1+l2) * 2
+ Out[21]: [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8]
.. nextslide:: Multiplying and Slicing
@@ -362,8 +357,7 @@ All sequences have a length. You can get it with the ``len`` builtin:
In [37]: len(s)
Out[37]: 25
-Remember, Python sequences are zero-indexed, so the last index in a sequence is
-``len(s) - 1``:
+Remember: Sequences are 0-indexed, so the last index is ``len(s)-1``:
.. code-block:: ipython
@@ -397,11 +391,11 @@ All sequences also support the ``min`` and ``max`` builtins:
Why are those the answers you get? (hint: ``ord('a')``)
+Of course this works with numbers, too!
.. nextslide:: Index
-All sequences also support the ``index`` method, which returns the index of the
-first occurence of an item in the sequence:
+All sequences also support the ``index`` method, which returns the index of the first occurence of an item in the sequence:
.. code-block:: ipython
@@ -443,35 +437,30 @@ This does not raise an error if the item you seek is not present:
Iteration
---------
-.. rst-class:: center large
+.. rst-class:: center mlarge
-More on this in a while.
+ All sequences are "iterables" --
-LAB
-====
+ More on this in a while.
-Slicing Lab
+Slicing LAB
+===========
-Slicing Lab
-------------
-Write some functions that:
+.. rst-class:: center medium
-* return a string with the first and last characters exchanged.
-* return a string with every other character removed
-* return a string with the first and last 4 characters removed, and every other char in between
-* return a string reversed (just with slicing)
-* return a string with the middle, then last, then first third in the new order
+ Let's practice Slicing!
+
+ :ref:`exercise_slicing`
-NOTE: these should work with ANY sequence -- not just strings!
Lightning Talks
----------------
|
-| James Brent Nunn
+| Beatrice He
|
|
-| Lauren Fries
+| Bradley Baumel
|
@@ -480,7 +469,7 @@ Lists, Tuples...
.. rst-class:: center large
-The *other* sequence types.
+The *primary* sequence types.
Lists
-----
@@ -507,6 +496,7 @@ Or by using the ``list`` type object as a constructor:
In [8]: list('abc')
Out[8]: ['a', 'b', 'c']
+It will take any "iterable"
.. nextslide:: List Elements
@@ -570,14 +560,13 @@ But they *do* need commas...!
In [156]: t = ( 3 )
In [157]: type(t)
Out[157]: int
- In [158]: t = (3,)
+ In [158]: t = ( 3, )
In [160]: type(t)
Out[160]: tuple
.. nextslide:: Converting to Tuple
-You can also use the ``tuple`` type object to convert any sequence into a
-tuple:
+You can also use the ``tuple`` type object to convert any iterable(sequence) into a tuple:
.. code-block:: ipython
@@ -605,7 +594,7 @@ multiple names (or no name)
In [25]: a = (1, 2, name)
In [26]: b = (3, 4, other)
In [27]: for i in range(3):
- ....: print a[i] is b[i],
+ ....: print(a[i] is b[i], end=' ')
....:
False False True
@@ -613,7 +602,7 @@ multiple names (or no name)
.. rst-class:: center large
-So Why Have Both?
+ So Why Have Both?
Mutability
@@ -642,18 +631,18 @@ Objects which are mutable may be *changed in place*.
Objects which are immutable may not be changed.
+Ever.
.. nextslide:: The Types We Know
-========= =======
+========= ===========
Immutable Mutable
-========= =======
-Unicode List
-String
-Integer
+========= ===========
+String List
+Integer Dictionary
Float
Tuple
-========= =======
+========= ===========
.. nextslide:: Lists Are Mutable
@@ -760,7 +749,7 @@ So, what is going to be in ``bins`` now?
We multiplied a sequence containing a single *mutable* object.
-We got a list containing five pointers to a single *mutable* object.
+We got a list containing five references to a single *mutable* object.
.. nextslide:: Mutable Default Argument
@@ -790,12 +779,12 @@ used to change the list.
You can find all these in the Standard Library Documentation:
-http://www.python.org/2/library/stdtypes.html#mutable-sequence-types
+https://docs.python.org/3/library/stdtypes.html#typesseq-mutable
Assignment
-----------
-Yo've already seen changing a single element of a list by assignment.
+You've already seen changing a single element of a list by assignment.
Pretty much the same as "arrays" in most languages:
@@ -936,9 +925,7 @@ Consider this common pattern:
if should_be_removed(x):
somelist.remove(x)
-This looks benign enough, but changing a list while you are iterating over it
-can be the cause of some pernicious bugs.
-
+This looks benign enough, but changing a list while you are iterating over it can be the cause of some pernicious bugs.
.. nextslide:: The Problem
@@ -946,14 +933,14 @@ For example:
.. code-block:: ipython
- In [121]: list = range(10)
- In [122]: list
- Out[122]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- In [123]: for x in list:
- .....: list.remove(x)
- .....:
- In [124]: list
- Out[124]: [1, 3, 5, 7, 9]
+ In [27]: l = list(range(10))
+ In [28]: l
+ Out[28]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ In [29]: for item in l:
+ ....: l.remove(item)
+ ....:
+ In [30]: l
+ Out[30]: [1, 3, 5, 7, 9]
Was that what you expected?
@@ -963,12 +950,13 @@ Iterate over a copy, and mutate the original:
.. code-block:: ipython
- In [126]: list = range(10)
- In [127]: for x in list[:]:
- .....: list.remove(x)
- .....:
- In [128]: list
- Out[128]: []
+ In [33]: l = list(range(10))
+
+ In [34]: for item in l[:]:
+ ....: l.remove(item)
+ ....:
+ In [35]: l
+ Out[35]: []
.. nextslide:: Just Say It, Already
@@ -982,17 +970,16 @@ You can iterate over a sequence.
for element in sequence:
do_something(element)
+which is what we mean when we say a sequence is an "iterable".
-Again, we'll touch more on this in a short while, but first a few more words
-about Lists and Tuples.
+Again, we'll touch more on this in a short while, but first a few more words about Lists and Tuples.
Miscellaneous List Methods
--------------------------
-These methods change a list in place and are not available on immutable
-sequence types.
+These methods change a list in place and are not available on immutable sequence types.
``.reverse()``
@@ -1011,16 +998,14 @@ sequence types.
In [133]: food
Out[133]: ['eggs', 'ham', 'spam']
-Because these methods mutate the list in place, they have a return value of
-``None``
+Because these methods mutate the list in place, they have a return value of ``None``
.. nextslide:: Custom Sorting
``.sort()`` can take an optional ``key`` parameter.
-It should be a function that takes one parameter (list items one at a time) and
-returns something that can be used for sorting:
+It should be a function that takes one parameter (list items one at a time) and returns something that can be used for sorting:
.. code-block:: ipython
@@ -1039,7 +1024,7 @@ List Performance
.. rst-class:: build
* indexing is fast and constant time: O(1)
-* x in s proportional to n: O(n)
+* ``x in l`` is proportional to n: O(n)
* visiting all is proportional to n: O(n)
* operating on the end of list is fast and constant time: O(1)
@@ -1047,8 +1032,8 @@ List Performance
* operating on the front (or middle) of the list depends on n: O(n)
- * pop(0), insert(0, v)
- * But, reversing is fast. Also, collections.deque
+ * ``pop(0)``, ``insert(0, v)``
+ * But, reversing is fast. ``Also, collections.deque``
http://wiki.python.org/moin/TimeComplexity
@@ -1067,7 +1052,9 @@ Here are a few guidelines on when to choose a list or a tuple:
Otherwise ... taste and convention
-.. nextslide:: Convention
+Convention
+-----------
+
Lists are Collections (homogeneous):
-- contain values of the same type
@@ -1078,7 +1065,8 @@ tuples are mixed types:
-- Kind of like simple C structs.
-.. nextslide:: Other Considerations
+Other Considerations
+--------------------
.. rst-class:: build
@@ -1108,91 +1096,51 @@ More Documentation
For more information, read the list docs:
-http://docs.python.org/2/library/stdtypes.html#mutable-sequence-types
+https://docs.python.org/3.5/library/stdtypes.html#mutable-sequence-types
(actually any mutable sequence....)
-LAB
-====
-
-List Lab
----------
-
-List Lab (after http://www.upriss.org.uk/python/session5.html)
-
-In your student folder, create a new file called ``list_lab.py``.
-
-The file should be an executable python script. That is to say that one
-should be able to run the script directly like so:
-
-.. code-block:: bash
-
- $ ./list_lab.py
-
-Add the file to your clone of the repository and commit changes frequently
-while working on the following tasks. When you are done, push your changes to
-GitHub and issue a pull request.
-
-(if you are struggling with git -- just write the code for now)
+One Last Trick
+---------------
-When the script is run, it should accomplish the following four series of
-actions:
+.. rst-class:: left
-.. nextslide:: Series 1
+For some of the exercises, you'll need to interact with a user at the
+command line.
-- Create a list that contains "Apples", "Pears", "Oranges" and "Peaches".
-- Display the list.
-- Ask the user for another fruit and add it to the end of the list.
-- Display the list.
-- Ask the user for a number and display the number back to the user and the
- fruit corresponding to that number (on a 1-is-first basis).
-- Add another fruit to the beginning of the list using "+" and display the
- list.
-- Add another fruit to the beginning of the list using insert() and display the
- list.
-- Display all the fruits that begin with "P", using a for loop.
+There's a nice built in function to do this - ``input``:
+.. code-block:: ipython
-.. nextslide:: Series 2
+ In [85]: fred = input('type something-->')
+ type something-->I've typed something
-Using the list created in series 1 above:
+ In [86]: print(fred)
+ I've typed something
-- Display the list.
-- Remove the last fruit from the list.
-- Display the list.
-- Ask the user for a fruit to delete and find it and delete it.
-- (Bonus: Multiply the list times two. Keep asking until a match is found. Once
- found, delete all occurrences.)
+This will display a prompt to the user, allowing them to input text and
+allowing you to bind that input to a symbol.
-.. nextslide:: Series 3
+LAB
+====
-Again, using the list from series 1:
+List Lab
+---------
-- Ask the user for input displaying a line like "Do you like apples?"
-- for each fruit in the list (making the fruit all lowercase).
-- For each "no", delete that fruit from the list.
-- For any answer that is not "yes" or "no", prompt the user to answer with one
- of those two values (a while loop is good here):
-- Display the list.
+Let's play a bit with Python lists...
-.. nextslide:: Series 4
+:ref:`exercise_list_lab`
-Once more, using the list from series 1:
-- Make a copy of the list and reverse the letters in each fruit in the copy.
-- Delete the last item of the original list. Display the original list and the
- copy.
Lightning Talks
-----------------
+---------------
|
-| Lesley D Reece
+| Jerry Bearer
|
+| Sheree Pena
|
-| Michel Claessens
-|
-
Iteration
=========
@@ -1210,7 +1158,7 @@ We've seen simple iteration over a sequence with ``for ... in``:
.. code-block:: ipython
In [170]: for x in "a string":
- .....: print x
+ .....: print(x)
.....:
a
s
@@ -1227,7 +1175,7 @@ Contrast this with other languages, where you must build and use an ``index``:
.. code-block:: javascript
- for(var i=0; i 50:
.....: break
.....:
@@ -1305,11 +1269,11 @@ allow iteration to continue:
.....: break
.....: if i < 25:
.....: continue
- .....: print i,
+ .....: print(i, end=' ')
.....:
25 26 27 28 29 ... 41 42 43 44 45 46 47 48 49 50
-.. nextslide:: Else
+.. nextslide:: else
For loops can also take an optional ``else`` block.
@@ -1321,14 +1285,14 @@ Executed only when the loop exits normally (not via break):
.....: if x == 11:
.....: break
.....: else:
- .....: print 'finished'
+ .....: print('finished')
finished
In [148]: for x in range(10):
.....: if x == 5:
- .....: print x
+ .....: print(x)
.....: break
.....: else:
- .....: print 'finished'
+ .....: print('finished')
5
This is a really nice unique Python feature!
@@ -1360,7 +1324,7 @@ potential error -- infinite loops:
i = 0;
while i < 5:
- print i
+ print(i)
.. nextslide:: Terminating a while Loop
@@ -1373,7 +1337,7 @@ Use ``break``:
.....: i += 1
.....: if i > 10:
.....: break
- .....: print i
+ .....: print(i)
.....:
1 2 3 4 5 6 7 8 9 10
@@ -1387,7 +1351,7 @@ Set a flag:
In [157]: keep_going = True
In [158]: while keep_going:
.....: num = random.choice(range(5))
- .....: print num
+ .....: print(num)
.....: if num == 3:
.....: keep_going = False
.....:
@@ -1401,7 +1365,7 @@ Use a condition:
In [161]: while i < 10:
.....: i += random.choice(range(4))
- .....: print i
+ .....: print(i)
.....:
0 0 2 3 4 6 8 8 8 9 12
@@ -1419,7 +1383,7 @@ loop terminates normally (no ``break``)
String Features
-=================
+================
.. rst-class:: center large
@@ -1450,6 +1414,13 @@ You can also use ``str()``
(demo)
+String Methods
+===============
+
+String objects have a lot of methods.
+
+Here are just a few:
+
String Manipulations
---------------------
@@ -1465,7 +1436,8 @@ String Manipulations
Out[170]: 'comma|separated|values'
-.. nextslide:: Case Switching
+Case Switching
+--------------
.. code-block:: ipython
@@ -1480,7 +1452,8 @@ String Manipulations
Out[175]: 'A Long String Of Words'
-.. nextslide:: Testing
+Testing
+--------
.. code-block:: ipython
@@ -1495,6 +1468,7 @@ String Manipulations
In [186]: fancy.isalnum()
Out[186]: False
+
String Literals
-----------------
@@ -1509,20 +1483,20 @@ Common Escape Sequences::
\ooo Character with octal value ooo
\xhh Character with hex value hh
-for example -- for tab-separted values:
+for example -- for tab-separated values:
.. code-block:: ipython
In [25]: s = "these\tare\tseparated\tby\ttabs"
- In [26]: print s
+ In [26]: print(s)
these are separated by tabs
-
-http://docs.python.org/release/2.5.2/ref/strings.html
+https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
+https://docs.python.org/3/library/stdtypes.html#string-methods
Raw Strings
--------------
+------------
Add an ``r`` in front of the string literal:
@@ -1530,10 +1504,10 @@ Escape Sequences Ignored
.. code-block:: ipython
- In [408]: print "this\nthat"
+ In [408]: print("this\nthat")
this
that
- In [409]: print r"this\nthat"
+ In [409]: print(r"this\nthat")
this\nthat
**Gotcha**
@@ -1553,20 +1527,20 @@ Characters in strings are stored as numeric values:
* "ASCII" values: 1-127
-* "ANSI" values: 1-255
+* Unicode values -- 1 - 1,114,111 (!!!)
To get the value:
.. code-block:: ipython
In [109]: for i in 'Chris':
- .....: print ord(i),
+ .....: print(ord(i), end=' ')
67 104 114 105 115
In [110]: for i in (67,104,114,105,115):
- .....: print chr(i),
- C h r i s
+ .....: print(chr(i), end='')
+ Chris
-(these days, stick with ASCII, or use Unicode: more on that in a few weeks)
+(these days, stick with ASCII, or use full Unicode: more on that in a few weeks)
Building Strings
@@ -1578,66 +1552,88 @@ You can, but please don't do this:
'Hello ' + name + '!'
+(I know -- we did that in the grid_printing excercise)
+
Do this instead:
.. code-block:: python
- 'Hello %s!' % name
+ 'Hello {}!'.format(name)
It's much faster and safer, and easier to modify as code gets complicated.
-http://docs.python.org/library/stdtypes.html#string-formatting-operations
+https://docs.python.org/3/library/string.html#string-formatting
+
+Old and New string formatting
+-----------------------------
+
+back in early python days, there was the string formatting operator: ``%``
+
+.. code-block:: python
+
+ " a string: %s and a number: %i "%("text", 45)
+
+This is very similar to C-style string formatting (`sprintf`).
+It's still around, and handy --- but ...
+
+The "new" ``format()`` method is more powerful and flexible, so we'll focus on that in this class.
.. nextslide:: String Formatting
-The string format operator: ``%``
+The string ``format()`` method:
.. code-block:: ipython
- In [261]: "an integer is: %i" % 34
- Out[261]: 'an integer is: 34'
- In [262]: "a floating point is: %f" % 34.5
- Out[262]: 'a floating point is: 34.500000'
- In [263]: "a string is: %s" % "anything"
- Out[263]: 'a string is: anything'
+ In [62]: "A decimal integer is: {:d}".format(34)
+ Out[62]: 'A decimal integer is: 34'
+
+ In [63]: "a floating point is: {:f}".format(34.5)
+ Out[63]: 'a floating point is: 34.500000'
+
+ In [64]: "a string is the default: {}".format("anything")
+ Out[64]: 'a string is the default: anything'
-.. nextslide:: More Placeholders
Multiple placeholders:
+-----------------------
.. code-block:: ipython
- In [264]: "the number %s is %i" % ('five', 5)
- Out[264]: 'the number five is 5'
- In [266]: "the first 3 numbers are: %i, %i, %i" % (1,2,3)
- Out[266]: 'the first 3 numbers are: 1, 2, 3'
+ In [65]: "the number is {} is {}".format('five', 5)
+ Out[65]: 'the number is five is 5'
+
+ In [66]: "the first 3 numbers are {}, {}, {}".format(1,2,3)
+ Out[66]: 'the first 3 numbers are 1, 2, 3'
The counts must agree:
.. code-block:: ipython
- In [187]: "string with %i formatting %s" % (1, )
+ In [67]: "string with {} formatting {}".format(1)
---------------------------------------------------------------------------
- ...
- TypeError: not enough arguments for format string
+ IndexError Traceback (most recent call last)
+ in ()
+ ----> 1 "string with {} formatting {}".format(1)
+ IndexError: tuple index out of range
-.. nextslide::
Named placeholders:
+-------------------
.. code-block:: ipython
- In [191]: "Hello, %(name)s, whaddaya know?" % {'name': "Joe"}
- Out[191]: 'Hello, Joe, whaddaya know?'
+
+ In [69]: "Hello, {name}, whaddaya know?".format(name="Joe")
+ Out[69]: 'Hello, Joe, whaddaya know?'
You can use values more than once, and skip values:
.. code-block:: ipython
- In [193]: "Hi, %(name)s. Howzit, %(name)s?" % {'name': "Bob", 'age': 27}
- Out[193]: 'Hi, Bob. Howzit, Bob?'
+ In [73]: "Hi, {name}. Howzit, {name}?".format(name='Bob')
+ Out[73]: 'Hi, Bob. Howzit, Bob?'
.. nextslide::
@@ -1645,82 +1641,32 @@ The format operator works with string variables, too:
.. code-block:: ipython
- In [45]: s = "%i / %i = %i"
+ In [80]: s = "{:d} / {:d} = {:f}"
- In [46]: a, b = 12, 3
+ In [81]: a, b = 12, 3
- In [47]: s%(a, b, a/b)
- Out[47]: '12 / 3 = 4'
+ In [82]: s.format(a, b, a/b)
+ Out[82]: '12 / 3 = 4.000000'
So you can dynamically build a format string
+Complex Formatting
+------------------
-.. nextslide:: New Formatting
-
-In more recent versions of Python (2.6+) this is `being phased out`_ in favor of the ``.format()`` method on strings.
-
-.. code-block:: ipython
-
- In [194]: "Hello, {}, how's your {}".format("Bob", "wife")
- Out[194]: "Hello, Bob, how's your wife"
- In [195]: "Hi, {name}. How's your {relation}?".format(name='Bob', relation='wife')
- Out[195]: "Hi, Bob. How's your wife?"
-
-
-.. nextslide:: Complex Formatting
-
-For both of these forms of string formatting, there is a complete syntax for
-specifying all sorts of options.
+There is a complete syntax for specifying all sorts of options.
It's well worth your while to spend some time getting to know this
`formatting language`_. You can accomplish a great deal just with this.
-.. _formatting language: https://docs.python.org/2/library/string.html#format-specification-mini-language
-
-.. _being phased out: https://docs.python.org/2/library/stdtypes.html#str.format
-
-
-
-
-One Last Trick
----------------
-
-.. rst-class:: left
-
-For some of the exercises, you'll need to interact with a user at the
-command line.
-
-There's a nice built in function to do this - ``input``:
-
-.. code-block:: ipython
-
- In [196]: fred = input('type something-->')
- type something-->;alksdjf
- In [197]: fred
- Out[197]: ';alksdjf'
-
-This will display a prompt to the user, allowing them to input text and
-allowing you to bind that input to a symbol.
+.. _formatting language: https://docs.python.org/3/library/string.html#format-specification-mini-language
String Formatting LAB
=====================
-.. rst-class:: left
-
- * Rewrite: ``the first 3 numbers are: %i, %i, %i"%(1,2,3)``
-
- for an arbitrary number of numbers...
+Let's play with these a bit:
- * Write a format string that will take:
-
- ``( 2, 123.4567, 10000)``
-
- and produce:
-
- ``'file_002 : 123.46, 1e+04'``
-
- * Then do these with the format() method...
+:ref:`exercise_string_formatting`
Homework
========
@@ -1739,39 +1685,7 @@ Task 2
ROT13
-The ROT13 encryption scheme is a simple substitution cypher where each letter
-in a text is replace by the letter 13 away from it (imagine the alphabet as a
-circle, so it wraps around).
-
-Add a python module named ``rot13.py`` to the session03 dir in your student dir.
-This module should provide at least one function called ``rot13`` that takes
-any amount of text and returns that same text encrypted by ROT13.
-
-This function should preserve whitespace, punctuation and capitalization.
-
-Your module should include an ``if __name__ == '__main__':`` block with tests
-that demonstrate that your ``rot13`` function and any helper functions you add
-work properly.
-
-
-.. nextslide:: A bit more
-
-There is a "short-cut" available that will help you accomplish this task. Some
-spelunking in `the documentation for strings`_ should help you to find it. If
-you do find it, using it is completely fair game.
-
-As usual, add your new file to your local clone right away. Make commits
-early and often and include commit messages that are descriptive and concise.
-
-When you are done, if you want me to review it, push your changes to github
-and issue a pull request.
-
-try decrypting this:
-
-"Zntargvp sebz bhgfvqr arne pbeare"
-
-.. _the documentation for strings: https://docs.python.org/2/library/stdtypes.html#string-methods
-
+:ref:`exercise_rot13`
Task 3
------
@@ -1780,79 +1694,16 @@ Task 3
Mail Room
-You work in the mail room at a local charity. Part of your job is to write
-incredibly boring, repetitive emails thanking your donors for their generous
-gifts. You are tired of doing this over an over again, so yo've decided to
-let Python help you out of a jam.
-
-Write a small command-line script called ``mailroom.py``. As with Task 1,
-This script should be executable. The script should accomplish the
-following goals:
-
-* It should have a data structure that holds a list of your donors and a
- history of the amounts they have donated. This structure should be populated
- at first with at least five donors, with between 1 and 3 donations each
-* The script should prompt the user (you) to choose from a menu of 2 actions:
- 'Send a Thank You' or 'Create a Report'.
-
-.. nextslide:: Sending a Thank You
-
-* If the user (you) selects 'Send a Thank You', prompt for a Full Name.
+:ref:`exercise_mailroom`
- * If the user types 'list', show them a list of the donor names and re-prompt
- * If the user types a name not in the list, add that name to the data
- structure and use it.
- * If the user types a name in the list, use it.
- * Once a name has been selected, prompt for a donation amount.
- * Verify that the amount is in fact a number, and re-prompt if it isn't.
- * Once an amount has been given, add that amount to the donation history of
- the selected user.
- * Finally, use string formatting to compose an email thanking the donor for
- their generous donation. Print the email to the terminal and return to the
- original prompt.
-
-**It is fine to forget new donors once the script quits running.**
-
-.. nextslide:: Creating a Report
-
-* If the user (you) selected 'Create a Report' Print a list of your donors,
- sorted by total historical donation amount.
-
- - Include Donor Name, total donated, number of donations and average donation
- amount as values in each row.
- - Using string formatting, format the output rows as nicely as possible. The
- end result should be tabular (values in each column should align with those
- above and below)
- - After printing this report, return to the original prompt.
-
-* At any point, the user should be able to quit their current task and return
- to the original prompt.
-
-* From the original prompt, the user should be able to quit the script cleanly
-
-.. nextslide:: Guidelines
-
-First, factor your script into separate functions. Each of the above
-tasks can be accomplished by a series of steps. Write discreet functions
-that accomplish individual steps and call them.
-
-Second, use loops to control the logical flow of your program. Interactive
-programs are a classic use-case for the ``while`` loop.
-
-Put the functions you write into the script at the top.
-
-Put your main interaction into an ``if __name__ == '__main__'`` block.
-
-Finally, use only functions and the basic Python data types you've learned
-about so far. There is no need to go any farther than that for this assignment.
+Reading
+-------
-.. nextslide:: Submission
+Think Python: Chapters 11, 13, 14
-As always, put the new file in your student directory in a ``session03``
-directory, and add it to your clone early. Make frequent commits with
-good, clear messages about what you are doing and why.
+Learn Python the Hard way: 15-17, 39
-When you are done, push your changes and make a pull request.
+Dive Into Python3: Sections 2.6, 2.7, 11
Next Week:
===========
@@ -1861,27 +1712,16 @@ Next Week:
**Lightning talks next week:**
-Benjamin C Mier
-
-Robert W Perkins
-
-Vinay Gupta
+Abdishu Hagi
-Wayne R Fukuhara
-
-
-Homework
-========
+Enrique R Silva
-Review and/or finish reading these class notes.
+Isaac Cowhey
-Finish any labs from class....
+Paul G Anderson
-**Reading:**
-Think Python, chapters 11, 13, 14
-Learn Python the Hard way:
diff --git a/slides_sources/source/session04.rst b/slides_sources/source/session04.rst
index 13e6be7d..77ee3d1d 100644
--- a/slides_sources/source/session04.rst
+++ b/slides_sources/source/session04.rst
@@ -1,10 +1,8 @@
-.. Foundations 2: Python slides file, created by
- Chris Barker: May 12, 2014.
-
-*******************************************************
-Session Four: Dictionaries, Sets, Exceptions, and Files
-*******************************************************
+.. include:: include.rst
+*******************************************
+Session Four: Dictionaries, Sets, and Files
+*******************************************
================
Review/Questions
@@ -34,15 +32,15 @@ Any questions?
Lightning Talks Today:
----------------------
-.. rst-class:: mlarge
+.. rst-class:: medium
- Benjamin C Mier
+ Abdishu Hagi
- Robert W Perkins
+ Enrique R Silva
- Lesley D Reece
+ Isaac Cowhey
- Wayne R Fukuhara
+ Paul G Anderson
==============================
@@ -71,7 +69,7 @@ You can do that in a for loop, also:
In [4]: l = [(1, 2), (3, 4), (5, 6)]
In [5]: for i, j in l:
- print "i:%i, j:%i"%(i, j)
+ print("i:{}, j:{}".format(i, j))
i:1, j:2
i:3, j:4
@@ -80,8 +78,8 @@ You can do that in a for loop, also:
(Mailroom example)
-Looping through two loops at once:
-----------------------------------
+Looping through two iterables at once:
+--------------------------------------
.. rst-class:: mlarge
@@ -94,8 +92,8 @@ Looping through two loops at once:
In [11]: l2 = [3, 4, 5]
In [12]: for i, j in zip(l1, l2):
- ....: print "i:%i, j:%i"%(i, j)
- ....:
+ print("i:{}, j:{}".format(i, j))
+
i:1, j:3
i:2, j:4
i:3, j:5
@@ -119,7 +117,7 @@ Need the index and the item?
In [2]: l = ['this', 'that', 'the other']
In [3]: for i, item in enumerate(l):
- ...: print "the %ith item is: %s"%(i, item)
+ ...: print("the {:d}th item is: {:s}".format(i, item))
...:
the 0th item is: this
the 1th item is: that
@@ -165,7 +163,34 @@ You can put a mutable item in an immutable object!
| Deleting from list (list_lab)
|
-.. nextslide::
+__main__
+--------
+
+What is this::
+
+ if __name__ == __main__
+
+about?
+
+Every module has a __name__
+
+If the module is loaded by ``import`` then it's name is the filename.
+
+If the module is run at the command line, like:
+
+.. code-block:: bash
+
+ python3 the_module.py
+
+Then it's ``__name__`` will be "__main__"
+
+This can be used to run code only when a module is run as a command,
+but not when it is imported.
+
+(demo)
+
+assert
+------
What is ``assert`` for?
@@ -178,10 +203,33 @@ in operational code should be::
if m < 0:
raise ValueError
-I'll cover Exceptions later this class...
+I'll cover more next week ...
(Asserts get ignored if optimization is turned on!)
+what the heck is reversed()?
+----------------------------
+
+I had a question in a PR:
+
+"what is ``reversed(x)``'s resultant object? what good is it?""
+
+.. nextslide::
+
+try it:
+
+.. code-block:: ipython
+
+ In [14]: type(reversed(l))
+ Out[14]: list_reverseiterator
+
+so it's a ``list_reverseiterator`` object -- not helpful, is it :-)
+
+But what it means is that it's an "iterable" that you can then do things like loop through with a for loop, etc. but it hasn't made a copy of the list -- it returns the items one by one as they are asked for. this has performance benefits, as it doesn't have to make a copy of the whole thing.
+
+So you use it if you want to loop through something in reversed order, but dont actually need an actual list with the order reversed.
+
+we'll get more into the details of iterators and iterables later in the class.
=================
A little warm up
@@ -194,6 +242,15 @@ Fun with strings
- for an arbitrary number of numbers...
+===============
+Lightning Talks
+===============
+
+|
+| Isaac Cowhey
+|
+| Paul G Anderson
+|
=====================
Dictionaries and Sets
@@ -324,7 +381,7 @@ Dictionaries have no defined order
In [353]: d
Out[353]: {'one': 1, 'three': 3, 'two': 2}
In [354]: d.keys()
- Out[354]: ['three', 'two', 'one']
+ Out[354]: dict_keys(['three', 'two', 'one'])
Dictionary Iterating
--------------------
@@ -336,7 +393,7 @@ Dictionary Iterating
In [15]: d = {'name': 'Brian', 'score': 42}
In [16]: for x in d:
- print x
+ print(x)
....:
score
name
@@ -352,13 +409,13 @@ dict keys and values
In [20]: d = {'name': 'Brian', 'score': 42}
In [21]: d.keys()
- Out[21]: ['score', 'name']
+ Out[21]: dict_keys(['score', 'name'])
In [22]: d.values()
- Out[22]: [42, 'Brian']
+ Out[22]: dict_values([42, 'Brian'])
In [23]: d.items()
- Out[23]: [('score', 42), ('name', 'Brian')]
+ Out[23]: dict_items([('score', 42), ('name', 'Brian')])
dict keys and values
@@ -371,7 +428,7 @@ Iterating on everything
In [26]: d = {'name': 'Brian', 'score': 42}
In [27]: for k, v in d.items():
- print "%s: %s" % (k,v)
+ print("%s: %s" % (k,v))
....:
score: 42
name: Brian
@@ -399,7 +456,7 @@ Other dict operations:
See them all here:
-https://docs.python.org/2/library/stdtypes.html#mapping-types-dict
+https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
Is it in there?
@@ -440,23 +497,34 @@ iterating
.. code-block:: ipython
- In [13]: for item in d.iteritems():
- ....: print item
+ In [13]: for item in d:
+ ....: print(item)
....:
- ('this', 5)
- ('that', 7)
- In [15]: for key in d.iterkeys():
- print key
+ this
+ that
+
+which is equivalent to, but faster than:
+
+.. code-block:: ipython
+
+ In [15]: for key in d.keys():
+ print(key)
....:
this
that
- In [16]: for val in d.itervalues():
- print val
+
+.. nextslide::
+
+but to get values, must specify you want values:
+
+.. code-block:: ipython
+
+ In [16]: for val in d.values():
+ print(val)
....:
5
7
-the ``iter*`` methods don't actually create the lists.
.. nextslide::
@@ -498,30 +566,42 @@ gets the value if it's there, sets it if it's not
In [28]: d
Out[28]: {'something': 'a value'}
- In [29]: d.setdefault('something', 'a value')
- Out[29]: 'a value'
-
- In [30]: d
- Out[30]: {'something': 'a value'}
.. nextslide::
-dict View objects:
-
-Like ``keys()``, ``values()``, ``items()``, but maintain a link to the original dict
+Assignment maintains link to the original dict
.. code-block:: ipython
In [47]: d
Out[47]: {'something': 'a value'}
- In [48]: item_view = d.viewitems()
+ In [48]: item_view = d
In [49]: d['something else'] = 'another value'
In [50]: item_view
- Out[50]: dict_items([('something else', 'another value'), ('something', 'a value')])
+ Out[50]: {'something': 'a value', 'something else': 'another value'}
+
+.. nextslide::
+
+Use explicit copy method to get a copy
+
+.. code-block:: ipython
+
+ In [51] item_copy = d.copy()
+
+ In [52]: d['another thing'] = 'different value'
+
+ In [53]: d
+ Out[53]:
+ {'another thing': 'different value',
+ 'something': 'a value',
+ 'something else': 'another value'}
+
+ In [54]: item_copy
+ Out[54]: {'something': 'a value', 'something else': 'another value'}
Sets
@@ -536,19 +616,19 @@ Set Constructors
.. code-block:: ipython
>>> set()
- set([])
+ set()
>>> set([1, 2, 3])
- set([1, 2, 3])
+ {1, 2, 3}
>>> {1, 2, 3}
- set([1, 2, 3])
+ {1, 2, 3}
>>> s = set()
>>> s.update([1, 2, 3])
>>> s
- set([1, 2, 3])
+ {1, 2, 3}
Set Properties
@@ -621,308 +701,23 @@ immutable -- for use as a key in a dict
File "", line 1, in
AttributeError: 'frozenset' object has no attribute 'add'
-LAB
-====
-
-Dict / Set Lab
-
-Dictionaries and Sets lab
---------------------------
-
-1.
-
-* Create a dictionary containing "name", "city", and "cake" for "Chris" from "Seattle" who likes "Chocolate".
-
-* Display the dictionary.
-
-* Delete the entry for "cake".
-
-* Display the dictionary.
-
-* Add an entry for "fruit" with "Mango" and display the dictionary.
-
- - Display the dictionary keys.
- - Display the dictionary values.
- - Display whether or not "cake" is a key in the dictionary (i.e. False) (now).
- - Display whether or not "Mango" is a value in the dictionary (i.e. True).
-
-.. nextslide::
-
-2.
-
-* Using the dict constructor and zip, build a dictionary of numbers from zero
- to fifteen and the hexadecimal equivalent (string is fine).
-
-3.
-
-* Using the dictionary from item 1: Make a dictionary using the same keys but
- with the number of 't's in each value.
-
-.. nextslide:: sets
-
-4.
-* Create sets s2, s3 and s4 that contain numbers from zero through twenty,
- divisible 2, 3 and 4.
-
-* Display the sets.
-
-* Display if s3 is a subset of s2 (False)
-
-* and if s4 is a subset of s2 (True).
-
-5.
+LAB: Dictionaries and Sets lab
+==============================
-* Create a set with the letters in 'Python' and add 'i' to the set.
+Have some fun with dictionaries and sets!
-* Create a frozenset with the letters in 'marathon'
+:ref:`exercise_dict_lab`
-* display the union and intersection of the two sets.
Lightning Talks
-----------------
-
-|
-| Benjamin C Mier
-|
-|
-| Robert W Perkins
-|
-
-==========
-Exceptions
-==========
-
-Exceptions
-----------
-
-Another Branching structure:
-
-.. code-block:: python
-
- try:
- do_something()
- f = open('missing.txt')
- process(f) # never called if file missing
- except IOError:
- print "couldn't open missing.txt"
-
-Exceptions
-----------
-Never Do this:
-
-.. code-block:: python
-
- try:
- do_something()
- f = open('missing.txt')
- process(f) # never called if file missing
- except:
- print "couldn't open missing.txt"
-
-
-Exceptions
-----------
-
-Use Exceptions, rather than your own tests:
-
-Don't do this:
-
-.. code-block:: python
-
- do_something()
- if os.path.exists('missing.txt'):
- f = open('missing.txt')
- process(f) # never called if file missing
-
-It will almost always work -- but the almost will drive you crazy
-
-.. nextslide::
-
-Example from homework
-
-.. code-block:: python
-
- if num_in.isdigit():
- num_in = int(num_in)
-
-but -- ``int(num_in)`` will only work if the string can be converted to an integer.
-
-So you can do
-
-.. code-block:: python
-
- try:
- num_in = int(num_in)
- except ValueError:
- print "Input must be an integer, try again."
-
-Or let the Exception be raised....
-
-
-.. nextslide:: EAFP
-
-
-"it's Easier to Ask Forgiveness than Permission"
-
- -- Grace Hopper
-
-
-http://www.youtube.com/watch?v=AZDWveIdqjY
-
-(Pycon talk by Alex Martelli)
-
-.. nextslide:: Do you catch all Exceptions?
-
-For simple scripts, let exceptions happen.
-
-Only handle the exception if the code can and will do something about it.
-
-(much better debugging info when an error does occur)
-
-
-Exceptions -- finally
----------------------
-
-.. code-block:: python
-
- try:
- do_something()
- f = open('missing.txt')
- process(f) # never called if file missing
- except IOError:
- print "couldn't open missing.txt"
- finally:
- do_some_clean-up
-
-The ``finally:`` clause will always run
-
-
-Exceptions -- else
--------------------
-
-.. code-block:: python
-
- try:
- do_something()
- f = open('missing.txt')
- except IOError:
- print "couldn't open missing.txt"
- else:
- process(f) # only called if there was no exception
-
-Advantage:
-
-you know where the Exception came from
-
-Exceptions -- using them
-------------------------
-
-.. code-block:: python
-
- try:
- do_something()
- f = open('missing.txt')
- except IOError as the_error:
- print the_error
- the_error.extra_info = "some more information"
- raise
-
-
-Particularly useful if you catch more than one exception:
-
-.. code-block:: python
-
- except (IOError, BufferError, OSError) as the_error:
- do_something_with (the_error)
-
-
-Raising Exceptions
--------------------
-
-.. code-block:: python
-
- def divide(a,b):
- if b == 0:
- raise ZeroDivisionError("b can not be zero")
- else:
- return a / b
-
-
-when you call it:
-
-.. code-block:: ipython
-
- In [515]: divide (12,0)
- ZeroDivisionError: b can not be zero
-
-
-Built in Exceptions
--------------------
-
-You can create your own custom exceptions
-
-But...
-
-.. code-block:: python
-
- exp = \
- [name for name in dir(__builtin__) if "Error" in name]
- len(exp)
- 32
-
-
-For the most part, you can/should use a built in one
-
-.. nextslide::
-
-Choose the best match you can for the built in Exception you raise.
-
-Example (from last week's exercises)::
-
- if (not isinstance(m, int)) or (not isinstance(n, int)):
- raise ValueError
-
-Is it the *value* or the input the problem here?
-
-Nope: the *type* is the problem::
-
- if (not isinstance(m, int)) or (not isinstance(n, int)):
- raise TypeError
-
-but should you be checking type anyway? (EAFP)
-
-===
-LAB
-===
-
-Exceptions Lab
-
-
-Exceptions Lab
---------------
-Improving ``raw_input``
-
-* The ``raw_input()`` function can generate two exceptions: ``EOFError``
- or ``KeyboardInterrupt`` on end-of-file(EOF) or canceled input.
-
-* Create a wrapper function, perhaps ``safe_input()`` that returns ``None``
- rather rather than raising these exceptions, when the user enters ``^C`` for Keyboard Interrupt, or ``^D`` (``^Z`` on Windows) for End Of File.
-
-* Update your mailroom program to use exceptions (and IBAFP) to handle
- malformed numeric input
-
-Lightning Talks
-----------------
-
|
-| Lesley D Reece
+| Abdishu Hagi
|
+| Enrique R Silva
|
-| Wayne R Fukuhara
-|
-
========================
File Reading and Writing
@@ -972,8 +767,7 @@ File Opening Modes
'rb', 'wb', 'ab'
r+, w+, a+
r+b, w+b, a+b
- U
- U+
+
These follow the Unix conventions, and aren't all that well documented
in the Python docs. But these BSD docs make it pretty clear:
@@ -989,7 +783,6 @@ Text is default
* Newlines are translated: ``\r\n -> \n``
* -- reading and writing!
* Use \*nix-style in your code: ``\n``
- * In text mode, you can use 'U' for "Universal" newline mode.
Gotcha:
@@ -1019,7 +812,7 @@ Common Idioms
.. code-block:: python
for line in open('secrets.txt'):
- print line
+ print(line)
(the file object is an iterator!)
@@ -1032,6 +825,18 @@ Common Idioms
break
do_something_with_line()
+.. nextslide::
+
+We will learn more about the keyword with later, but for now, just understand
+the syntax and the advantage over the try-finally block:
+
+.. code-block:: python
+
+ with open('workfile', 'r') as f:
+ read_data = f.read()
+ f.closed
+ True
+
File Writing
------------
@@ -1041,6 +846,11 @@ File Writing
outfile = open('output.txt', 'w')
for i in range(10):
outfile.write("this is line: %i\n"%i)
+ outfile.close()
+
+ with open('output.txt', 'w'):
+ for i in range(10):
+ f.write("this is line: %i\n"%i)
File Methods
@@ -1056,37 +866,21 @@ Commonly Used Methods
f.seek(offset) f.tell() # for binary files, mostly
- f.flush()
-
f.close()
-
-File Like Objects
------------------
-
-
-Many classes implement the file interface:
-
- * loggers
- * ``sys.stdout``
- * ``urllib.open()``
- * pipes, subprocesses
- * StringIO
- * variois objects in the ``io`` module
-
-https://docs.python.org/2/library/stdtypes.html#file-objects
-
StringIO
--------
.. code-block:: python
- In [417]: import StringIO
- In [420]: f = StringIO.StringIO()
+ In [417]: import io
+ In [420]: f = io.StringIO()
In [421]: f.write("somestuff")
In [422]: f.seek(0)
In [423]: f.read()
Out[423]: 'somestuff'
+ Out[424]: stuff = f.getvalue()
+ Out[425]: f.close()
(handy for testing file handling code...)
@@ -1128,10 +922,10 @@ os module
.. code-block:: python
- os.getcwd() -- os.getcwdu() (u for Unicode)
- chdir(path)
+ os.getcwd()
+ os.chdir(path)
os.path.abspath()
- os.path.relpath()
+ os.path.relpath()
.. nextslide:: os.path module
@@ -1160,16 +954,10 @@ os module
pathlib
-------
-``pathlib`` is a new package for handling paths in an OO way:
+``pathlib`` is a package for handling paths in an OO way:
http://pathlib.readthedocs.org/en/pep428/
-It is now part of the Python3 standard library, and has been back-ported for use with Python2:
-
-.. code-block:: bash
-
- $ pip install pathlib
-
All the stuff in os.path and more:
.. code-block:: ipython
@@ -1179,9 +967,9 @@ All the stuff in os.path and more:
In [66]: pth.is_dir()
Out[66]: True
In [67]: pth.absolute()
- Out[67]: PosixPath('/Users/Chris/PythonStuff/CodeFellowsClass/sea-f2-python-sept14/Examples/Session04')
+ Out[67]: PosixPath('/Users/Chris/PythonStuff/UWPCE/IntroPython2015')
In [68]: for f in pth.iterdir():
- print f
+ print(f)
junk2.txt
junkfile.txt
...
@@ -1192,25 +980,8 @@ LAB
Files Lab: If there is time.
-Files Lab
----------
-
-In the class repo, in:
-
-``Examples\Session01\students.txt``
-
-You will find the list I generated of all the students in the class, and
-what programming languages they have used in the past.
-
-Write a little script that reads that file, and generates a list of all
-the languages that have been used.
-
-Extra credit: keep track of how many students specified each language.
-
-If you've got git set up right, ``git pull upstream master`` should update
-your repo. Otherwise, you can get it from gitHub:
+:ref:`exercise_file_lab`
-``https://github.com/UWPCE-PythonCert/IntroToPython/blob/master/Examples/Session01/students.txt``
=========
@@ -1219,66 +990,21 @@ Homework
Recommended Reading:
---------------------
- * Dive Into Python: Chapt. 13,14
- * Unicode: http://www.joelonsoftware.com/articles/Unicode.html
-
-Assignments:
--------------
-
- * Finish the dict/sets lab
- * Finish the Exceptions lab
- * Coding kata: trigrams
- * Paths and files
- * Update mailroom with dicts and exceptions
+ * Dive Into Python 3: Chapt. 2.7 (and 4 if you haven't already)
-Text and files and dicts, and...
----------------------------------
+http://www.diveintopython3.net/native-datatypes.html#dictionaries
-* Coding Kata 14 - Dave Thomas
+ * Dive Into Python 3: Chapt. 11
- http://codekata.com/kata/kata14-tom-swift-under-the-milkwood/
+http://www.diveintopython3.net/files.html
- and in this doc:
- :doc:`./exercises/kata_fourteen`
-
- and on github here
-
- http://uwpce-pythoncert.github.io/IntroToPython/exercises/kata_fourteen.html
-
-.. nextslide::
-
-* Use The Adventures of Sherlock Holmes as input:
-
- :download:`./exercises/sherlock.txt`
-
- and on github here:
-
- http://uwpce-pythoncert.github.io/IntroToPython/_downloads/sherlock.txt
-
-* This is intentionally open-ended and underspecified. There are many interesting decisions to make.
-
-* Experiment with different lengths for the lookup key. (3 words, 4 words, 3 letters, etc)
-
-
-Paths and File Processing
---------------------------
-
-* write a program which prints the full path to all files in the current
- directory, one per line
-
-* write a program which copies a file from a source, to a destination
- (without using shutil, or the OS copy command)
-
- - advanced: make it work for any size file: i.e. don't read the entire
- contents of the file into memory at once.
-
-* update mailroom from last week to:
+Assignments:
+-------------
- - use dicts where appropriate
- - write a full set of letters to everyone to individual files on disk
- - see if you can use a dict to switch between the users selections
- - Try to use a dict and the .format() method to do the letter as one
- big template -- rather than building up a big string in parts.
+ * Finish the dict/sets lab: :ref:`exercise_dict_lab`
+ * Finish the files lab: :ref:`exercise_file_lab`
+ * Coding kata: trigrams: :ref:`exercise_trigrams`
+ * update mailroom with dicts :ref:`exercise_mailroom_plus`
diff --git a/slides_sources/source/session05.rst b/slides_sources/source/session05.rst
index 057183c8..04b21d7a 100644
--- a/slides_sources/source/session05.rst
+++ b/slides_sources/source/session05.rst
@@ -1,11 +1,8 @@
+.. include:: include.rst
-.. Foundations 2: Python slides file, created by
- hieroglyph-quickstart on Wed Apr 2 18:42:06 2014.
-
-
-*********************************************************************
-Session Five: Advanced Argument passing, List and Dict Comprehensions
-*********************************************************************
+****************************************
+Session Five: Exceptions, Comprehensions
+****************************************
======================
Lightning Talks Today:
@@ -13,14 +10,13 @@ Lightning Talks Today:
.. rst-class:: medium
- Darcy Balcarce
-
- Eric Buer
+ Alexander C Truong
- Henry B Fischer
+ Darryl Wong
- Kyle R Hart
+ Madhumita Acharya
+ Matthew T Weidner
================
Review/Questions
@@ -30,8 +26,8 @@ Review of Previous Class
------------------------
* Dictionaries
- * Exceptions
- * Files, etc.
+ * Sets
+ * File processing, etc.
.. nextslide::
@@ -57,403 +53,502 @@ Review of Previous Class
* But it's all good stuff.
- * I want time to go over it in class.
+ * I'll take time to go over it in class.
- * So I'm ditching Unicode -- we'll hit it in the last class
+ * And this week is a light load, so you can catch up.
Homework review
---------------
-Homework Questions?
-
-My Solutions to ALL the excercises in the class repo in:
+My Solutions to all the exercises in the class repo in:
``Solutions/Session04``
-A few tidbits ....
+A few tidbits, then I'll take specific questions.
+
+
+The count() method
+------------------
+
+All Python sequences (including strings) have a ``count()`` method:
+
+.. code-block:: ipython
+
+ In [1]: s = "This is an arbitrary string"
+
+ In [2]: s.count('t')
+ Out[2]: 2
+
+What if you want a case-insensitive count?
+
+.. code-block:: ipython
+
+ In [3]: s.lower().count('t')
+ Out[3]: 3
+
+set.update()
+------------
+
+If you want to add a bunch of stuff to a set, you can use update:
+
+.. code-block:: ipython
+
+ In [1]: s = set()
+
+In [2]: s.update
+Out[2]:
+
+In [3]: s.update(['this', 'that'])
-.. nextslide:: Sorting stuff in dictionaries:
+In [4]: s
+Out[4]: {'that', 'this'}
+
+In [5]: s.update(['this', 'thatthing'])
+
+In [6]: s
+Out[6]: {'that', 'thatthing', 'this'}
+
+**NOTE:** It's VERY often the case that when you find yourself writing a trivial loop -- there is a way to do it with a built in method!
+
+
+
+Sorting stuff in dictionaries:
+-------------------------------
dicts aren't sorted, so what if you want to do something in a sorted way?
-The "old" way:
+The "standard" way:
.. code-block:: python
- keys = d.keys()
- keys.sort()
- for key in keys:
+ for key in sorted(d.keys()):
...
-Other options:
+Another option:
.. code-block:: python
collections.OrderedDict
- sorted()
+Also other nifty stuff in the ``collections`` module:
-(demo)
+https://docs.python.org/3.5/library/collections.html
-Code Review
-------------
+Using files and "with"
+-----------------------
-.. rst-class:: center medium
+Sorry for the confusion, but I'll be more clear now.
-Anyone stuck or confused that's willing to volunteer for a live code review?
+When working with files, unless you have a good reason not to, use ``with``:
-My Solutions
--------------
+.. code-block:: python
-Anyone look at my solutions?
+ with open(the_filename, 'w') as outfile:
+ outfile.write(something)
+ do_some_more...
+ # now done with out file -- it will be closed, regardless of errors, etc.
+ do_other_stuff
-(yeah, not much time for that...)
+``with`` invokes a context manager -- which can be confusing, but for now,
+just follow this pattern -- it really is more robust.
-Anything in particular you'd like me to go over?
+And you can even do two at once:
-=========================
-Advanced Argument Passing
-=========================
+.. code-block:: python
-Keyword arguments
------------------
+ with open(source, 'rb') as infile, open(dest, 'wb') as outfile:
+ outfile.write(infile.read())
+
+
+Binary files
+------------
+
+Python can open files in one of two modes:
-When defining a function, you can specify only what you need -- in any order
+ * Text
+ * Binary
+
+This is just what you'd think -- if the file contains text, you want text mode. If the file contains arbitrary binary data, you want binary mode.
+
+All data in all files is binary -- that's how computers work. So in Python3, "text" actually means Unicode -- which is a particular system for matching characters to binary data.
+
+But this too is complicated -- there are multiple ways that binary data can be mapped to Unicode text, known as "encodings". In Python, text files are by default opened with the "utf-8" encoding. These days, that mostly "just works".
+
+.. nextslide::
+
+But if you read a binary file as text, then Python will try to interpret the bytes as utf-8 encoded text -- and this will likely fail:
.. code-block:: ipython
- In [151]: def fun(x,y=0,z=0):
- print x,y,z
- .....:
- In [152]: fun(1,2,3)
- 1 2 3
- In [153]: fun(1, z=3)
- 1 0 3
- In [154]: fun(1, z=3, y=2)
- 1 2 3
+ In [13]: open("a_photo.jpg").read()
+ ---------------------------------------------------------------------------
+ UnicodeDecodeError Traceback (most recent call last)
+ in ()
+ ----> 1 open("PassportPhoto.JPG").read()
+ /Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/codecs.py in decode(self, input, final)
+ 319 # decode input (taking the buffer into account)
+ 320 data = self.buffer + input
+ --> 321 (result, consumed) = self._buffer_decode(data, self.errors, final)
+ 322 # keep undecoded input until the next call
+ 323 self.buffer = data[consumed:]
+
+ UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
.. nextslide::
+In Python2, it's less likely that you'll get an error like this -- it doesn't try to decode the file as it's read -- even for text files -- so it's a bit tricky and more error prone.
-A Common Idiom:
+**NOTE:** If you want to actually DO anything with a binary file, other than passing it around, then you'll need to know a lot about how the details of what the bytes in the file mean -- and most likely, you'll use a library for that -- like an image processing library for the jpeg example above.
-.. code-block:: python
- def fun(x, y=None):
- if y is None:
- do_something_different
- go_on_here
+PEP 8 reminder
+--------------
+PEP 8 (Python Enhancement Proposal 8): https://www.python.org/dev/peps/pep-0008/
+Is the "official" style guide for Python code.
-.. nextslide::
+Strictly speaking, you only need to follow it for code in the standard library.
-Can set defaults to variables
+But style matters -- consistent style makes your code easier to read and understand.
-.. code-block:: ipython
+So **follow PEP 8**
- In [156]: y = 4
- In [157]: def fun(x=y):
- print "x is:", x
- .....:
- In [158]: fun()
- x is: 4
+**Exception:** if you have a company style guide -- follow that instead.
+Try the "pycodestyle" module on your code::
-.. nextslide::
+ $ python3 -m pip install pycodestyle
+ $ pycodestyle my_python_file
+
+(demo)
-Defaults are evaluated when the function is defined
+Naming things...
+----------------
-.. code-block:: ipython
+It matters what names you give your variables.
- In [156]: y = 4
- In [157]: def fun(x=y):
- print "x is:", x
- .....:
- In [158]: fun()
- x is: 4
- In [159]: y = 6
- In [160]: fun()
- x is: 4
+Python has rules about what it *allows*
+PEP8 has rules for style: capitalization, and underscores and all that.
+But you still get to decide within those rules.
-Function arguments in variables
--------------------------------
+So use names that make sense to the reader.
-function arguments are really just
+Naming Guidelines
+-----------------
-* a tuple (positional arguments)
-* a dict (keyword arguments)
+Only use single-letter names for things with limited scope: indexes and the like:
.. code-block:: python
- def f(x, y, w=0, h=0):
- print "position: %s, %s -- shape: %s, %s"%(x, y, w, h)
+ for i, item in enumerate(a_sequence):
+ do_something(i, item)
- position = (3,4)
- size = {'h': 10, 'w': 20}
+**Don't** use a name like "item", when there is a meaning to what the item is:
- >>> f( *position, **size)
- position: 3, 4 -- shape: 20, 10
+.. code-block:: python
+ for name in all_the_names:
+ do_something_with(name)
+Use plurals for collections of things:
-Function parameters in variables
---------------------------------
+.. code-block:: python
-You can also pull the parameters out in the function as a tuple and a dict:
+ names = ['Fred', 'George', ...]
-.. code-block:: ipython
+.. nextslide::
- def f(*args, **kwargs):
- print "the positional arguments are:", args
- print "the keyword arguments are:", kwargs
+**Do** re-use names when the use is essentially the same, and you don't need the old one:
- In [389]: f(2, 3, this=5, that=7)
- the positional arguments are: (2, 3)
- the keyword arguments are: {'this': 5, 'that': 7}
+.. code-block:: python
-This can be very powerful...
+ line = line.strip()
+ line = line.replace(",", " ")
+ ....
-Passing a dict to str.format()
--------------------------------
+Here's a nice talk about naming:
-Now that you know that keyword args are really a dict,
-you can do this nifty trick:
+http://pyvideo.org/video/3792/name-things-once-0
-The ``format`` method takes keyword arguments:
-.. code-block:: ipython
+Code Review
+------------
- In [24]: u"My name is {first} {last}".format(last=u"Barker", first=u"Chris")
- Out[24]: u'My name is Chris Barker'
+.. rst-class:: center medium
-Build a dict of the keys and values:
+Anyone stuck or confused that's willing to volunteer for a live code review?
-.. code-block:: ipython
+My Solutions
+-------------
+
+Anyone look at my solutions?
- In [25]: d = {u"last":u"Barker", u"first":u"Chris"}
+(yeah, not much time for that...)
-And pass to ``format()``with ``**``
+Anything in particular you'd like me to go over?
-.. code-block:: ipython
- In [26]: u"My name is {first} {last}".format(**d)
- Out[26]: u'My name is Chris Barker'
+trigrams
+--------
-=====================================
-A bit more on mutability (and copies)
-=====================================
+Let's take a close look at trigrams!
-mutable objects
+Some of you have already done a nice solution to this.
+
+But some of you are not sure how to start.
+
+So let's start from the beginning...
+
+NOTE: think about set vs list.
+
+(demo)
+
+Lightning Talks
----------------
-We've talked about this: mutable objects can have their contents changed in place.
+.. rst-class:: medium
-Immutable objects can not.
+|
+| Alexander C Truong
+|
+|
+| Darryl Wong
+|
-This has implications when you have a container with mutable objects in it:
+==========
+Exceptions
+==========
-.. code-block:: ipython
+A really nifty python feature -- really handy!
- In [28]: list1 = [ [1,2,3], ['a','b'] ]
+Exceptions
+----------
-one way to make a copy of a list:
+Another Branching structure:
-.. code-block:: ipython
+.. code-block:: python
- In [29]: list2 = list1[:]
+ try:
+ do_something()
+ f = open('missing.txt')
+ process(f) # never called if file missing
+ except IOError:
+ print("couldn't open missing.txt")
- In [30]: list2 is list1
- Out[30]: False
+Exceptions
+----------
-they are different lists.
+Never Do this:
-.. nextslide::
+.. code-block:: python
-What if we set an element to a new value?
+ try:
+ do_something()
+ f = open('missing.txt')
+ process(f) # never called if file missing
+ except:
+ print "couldn't open missing.txt"
-.. code-block:: ipython
+**always** capture the *particular* Exception you know how to handle.
+
+
+Exceptions
+----------
- In [31]: list1[0] = [5,6,7]
+Use Exceptions, rather than your own tests:
- In [32]: list1
- Out[32]: [[5, 6, 7], ['a', 'b']]
+Don't do this:
- In [33]: list2
- Out[33]: [[1, 2, 3], ['a', 'b']]
+.. code-block:: python
+
+ do_something()
+ if os.path.exists('missing.txt'):
+ f = open('missing.txt')
+ process(f)
-So they are independent.
+It will almost always work -- but the almost will drive you crazy
.. nextslide::
-But what if we mutate an element?
+Example from homework
-.. code-block:: ipython
+.. code-block:: python
- In [34]: list1[1].append('c')
+ if num_in.isdigit():
+ num_in = int(num_in)
- In [35]: list1
- Out[35]: [[5, 6, 7], ['a', 'b', 'c']]
+but -- ``int(num_in)`` will only work if the string can be converted to an integer.
- In [36]: list2
- Out[36]: [[1, 2, 3], ['a', 'b', 'c']]
+So you can do
-uuh oh! mutating an element in one list mutated the one in the other list.
+.. code-block:: python
-.. nextslide::
+ try:
+ num_in = int(num_in)
+ except ValueError:
+ print("Input must be an integer, try again.")
-Why is that?
+Or let the Exception be raised....
-.. code-block:: ipython
- In [38]: list1[1] is list2[1]
- Out[38]: True
+EAFP
+----
-The elements are the same object!
-This is known as a "shallow" copy -- Python doesn't want to copy more than it needs to, so in this case, it makes a new list, but does not make copies of the contents.
+"it's Easier to Ask Forgiveness than Permission"
-Same for dicts (and any container type)
+ -- Grace Hopper
-If the elements are immutable, it doesn't really make a differnce -- but be very careful with mutable elements.
+http://www.youtube.com/watch?v=AZDWveIdqjY
-The copy module
-----------------
+(PyCon talk by Alex Martelli)
-most objects have a way to make copies (``dict.copy()`` for instance).
-but if not, you can use the ``copy`` module to make a copy:
+.. nextslide:: Do you catch all Exceptions?
-.. code-block:: ipython
+For simple scripts, let exceptions happen.
- In [39]: import copy
+Only handle the exception if the code can and will do something about it.
- In [40]: list3 = copy.copy(list2)
+(much better debugging info when an error does occur)
- In [41]: list3
- Out[41]: [[1, 2, 3], ['a', 'b', 'c']]
-This is also a shallow copy.
+Exceptions -- finally
+---------------------
-.. nextslide::
+.. code-block:: python
-But there is another option:
+ try:
+ do_something()
+ f = open('missing.txt')
+ process(f) # never called if file missing
+ except IOError:
+ print("couldn't open missing.txt")
+ finally:
+ do_some_clean-up
-.. code-block:: ipython
+The ``finally:`` clause will always run
- In [3]: list1
- Out[3]: [[1, 2, 3], ['a', 'b', 'c']]
- In [4]: list2 = copy.deepcopy(list1)
+Exceptions -- else
+-------------------
- In [5]: list1[0].append(4)
+.. code-block:: python
- In [6]: list1
- Out[6]: [[1, 2, 3, 4], ['a', 'b', 'c']]
+ try:
+ do_something()
+ f = open('missing.txt')
+ except IOError:
+ print("couldn't open missing.txt")
+ else:
+ process(f) # only called if there was no exception
- In [7]: list2
- Out[7]: [[1, 2, 3], ['a', 'b', 'c']]
+Advantage:
-``deepcopy`` recurses through the object, making copies of everything as it goes.
+you know where the Exception came from
-.. nextslide::
+Exceptions -- using them
+------------------------
+.. code-block:: python
-I happened on this thread on stack overflow:
+ try:
+ do_something()
+ f = open('missing.txt')
+ except IOError as the_error:
+ print(the_error)
+ the_error.extra_info = "some more information"
+ raise
-http://stackoverflow.com/questions/3975376/understanding-dict-copy-shallow-or-deep
+Particularly useful if you catch more than one exception:
-The OP is pretty confused -- can you sort it out?
+.. code-block:: python
-Make sure you understand the difference between a reference, a shallow copy, and a deep copy.
+ except (IOError, BufferError, OSError) as the_error:
+ do_something_with (the_error)
-Mutables as default arguments:
-------------------------------
-Another "gotcha" is using mutables as default arguments:
+Raising Exceptions
+-------------------
-.. code-block:: ipython
+.. code-block:: python
- In [11]: def fun(x, a=[]):
- ....: a.append(x)
- ....: print a
- ....:
+ def divide(a,b):
+ if b == 0:
+ raise ZeroDivisionError("b can not be zero")
+ else:
+ return a / b
-This makes sense: maybe you'd pass in a list, but the default is an empty list.
-But:
+when you call it:
.. code-block:: ipython
- In [12]: fun(3)
- [3]
+ In [515]: divide (12,0)
+ ZeroDivisionError: b can not be zero
- In [13]: fun(4)
- [3, 4]
-Huh?!
+Built in Exceptions
+-------------------
-.. nextslide::
+You can create your own custom exceptions
-Remember that that default argument is defined when the function is created: there will be only one list, and every time the function is called, that same list is used.
+But...
+.. code-block:: python
-The solution:
+ exp = \
+ [name for name in dir(__builtin__) if "Error" in name]
+ len(exp)
+ 32
-The standard practice for such a mutable default argument:
-.. code-block:: ipython
+For the most part, you can/should use a built in one
- In [15]: def fun(x, a=None):
- ....: if a is None:
- ....: a = []
- ....: a.append(x)
- ....: print a
- In [16]: fun(3)
- [3]
- In [17]: fun(4)
- [4]
+.. nextslide::
-You get a new list every time the function is called
+Choose the best match you can for the built in Exception you raise.
+Example (from last week's exercises)::
+ if (not isinstance(m, int)) or (not isinstance(n, int)):
+ raise ValueError
-LAB
-----
+Is it the *value* or the input the problem here?
-.. rst-class:: medium
+Nope: the *type* is the problem::
- keyword arguments:
+ if (not isinstance(m, int)) or (not isinstance(n, int)):
+ raise TypeError
-* Write a function that has four optional parameters (with defaults):
+but should you be checking type anyway? (EAFP)
- - fore_color
- - back_color
- - link_color
- - visited_color
+===
+LAB
+===
+
+Exceptions Lab:
+
+
+:ref:`exercise_exceptions_lab`
-* Have it print the colors (use strings for the colors)
-* Call it with a couple different parameters set
-* Have it pull the parameters out with ``*args, **kwargs``
Lightning Talks
-----------------
+---------------
.. rst-class:: medium
|
-| Darcy Balcarce
+| Madhumita Acharya
|
-|
-| Eric Buer
-|
-
+| Matthew T Weidner
@@ -463,8 +558,8 @@ List and Dict Comprehensions
List comprehensions
-------------------
-A bit of functional programming
+A bit of functional programming
consider this common ``for`` loop structure:
@@ -483,7 +578,6 @@ This can be expressed with a single line using a "list comprehension"
.. nextslide::
-
What about nested for loops?
.. code-block:: python
@@ -521,7 +615,6 @@ You can add a conditional to the comprehension:
new_list = [expr for var in a_list if something_is_true]
-
(demo)
.. nextslide::
@@ -543,7 +636,7 @@ Examples:
.. nextslide::
-Remember this from last week?
+Remember this from earlier today?
.. code-block:: python
@@ -556,7 +649,6 @@ Remember this from last week?
....
-
Set Comprehensions
------------------
@@ -586,7 +678,7 @@ Example: finding all the vowels in a string...
In [20]: vowels = set('aeiou')
- In [21]: { let for let in s if let in vowels }
+ In [21]: { l for l in s if l in vowels }
Out[21]: {'a', 'e', 'i', 'o'}
Side note: why did I do ``set('aeiou')`` rather than just `aeiou` ?
@@ -629,258 +721,14 @@ Example
LAB
===
-See the list comps exercises...
-
-Lightning Talks
-----------------
-
-.. rst-class:: medium
-
-|
-| Some person
-|
-|
-| Some other person
-|
+Here is a nice tutorial on list comprehensions:
+http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/
-=======
-Testing
-=======
+List comps exercises:
-.. rst-class:: build left
-.. container::
+:ref:`exercise_comprehensions`
- You've already seen some a very basic testing strategy.
-
- You've written some tests using that strategy.
-
- These tests were pretty basic, and a bit awkward in places (testing error
- conditions in particular).
-
- .. rst-class:: centered
-
- **It gets better**
-
-Test Runners
-------------
-
-So far our tests have been limited to code in an ``if __name__ == "__main__":``
-block.
-
-.. rst-class:: build
-
-* They are run only when the file is executed
-* They are always run when the file is executed
-* You can't do anything else when the file is executed without running tests.
-
-.. rst-class:: build
-.. container::
-
- This is not optimal.
-
- Python provides testing systems to help.
-
-
-Standard Library: ``unittest``
--------------------------------
-
-
-The original testing system in Python.
-
-``import unittest``
-
-More or less a port of Junit from Java
-
-A bit verbose: you have to write classes & methods
-
-(And we haven't covered that yet!)
-
-
-Using ``unittest``
--------------------
-
-You write subclasses of the ``unittest.TestCase`` class:
-
-.. code-block:: python
-
- # in test.py
- import unittest
-
- class MyTests(unittest.TestCase):
- def test_tautology(self):
- self.assertEquals(1, 1)
-
-Then you run the tests by using the ``main`` function from the ``unittest``
-module:
-
-.. code-block:: python
-
- # in test.py
- if __name__ == '__main__':
- unittest.main()
-
-.. nextslide:: Testing Your Code
-
-This way, you can write your code in one file and test it from another:
-
-.. code-block:: python
-
- # in my_mod.py
- def my_func(val1, val2):
- return val1 * val2
-
- # in test_my_mod.py
- import unittest
- from my_mod import my_func
-
- class MyFuncTestCase(unittest.TestCase):
- def test_my_func(self):
- test_vals = (2, 3)
- expected = reduce(lambda x, y: x * y, test_vals)
- actual = my_func(*test_vals)
- self.assertEquals(expected, actual)
-
- if __name__ == '__main__':
- unittest.main()
-
-.. nextslide:: Advantages of ``unittest``
-
-.. rst-class:: build
-.. container::
-
- The ``unittest`` module is pretty full featured
-
- It comes with the standard Python distribution, no installation required.
-
- It provides a wide variety of assertions for testing all sorts of situations.
-
- It allows for a setup and tear down workflow both before and after all tests
- and before and after each test.
-
- It's well known and well understood.
-
-.. nextslide:: Disadvantages:
-
-.. rst-class:: build
-.. container::
-
-
- It's Object Oriented, and quite heavy.
-
- - modeled after Java's ``junit`` and it shows...
-
- It uses the framework design pattern, so knowing how to use the features
- means learning what to override.
-
- Needing to override means you have to be cautious.
-
- Test discovery is both inflexible and brittle.
-
-.. nextslide:: Other Options
-
-There are several other options for running tests in Python.
-
-
-* `Nose`_
-* `pytest`_
-* ... (many frameworks supply their own test runners)
-
-We are going to play today with pytest
-
-.. _Nose: https://nose.readthedocs.org/
-.. _pytest: http://pytest.org/latest/
-
-
-.. nextslide:: Installing ``pytest``
-
-The first step is to install the package:
-
-.. code-block:: bash
-
- (cff2py)$ pip install pytest
-
-Once this is complete, you should have a ``py.test`` command you can run
-at the command line:
-
-.. code-block:: bash
-
- $ py.test
-
-If you have any tests in your repository, that will find and run them.
-
-.. rst-class:: build
-.. container::
-
- **Do you?**
-
-.. nextslide:: Pre-existing Tests
-
-Let's take a look at some examples.
-
-``\Examples\Session05``
-
-`` $ py.test``
-
-You can also run py.test on a particular test file:
-
-``py.test test_this.py``
-
-The results you should have seen when you ran ``py.test`` above come
-partly from these files.
-
-Let's take a few minutes to look these files over.
-
-[demo]
-
-.. nextslide:: What's Happening Here.
-
-When you run the ``py.test`` command, ``pytest`` starts in your current
-working directory and searches the filesystem for things that might be tests.
-
-It follows some simple rules:
-
-.. rst-class:: build
-
-* Any python file that starts with ``test_`` or ``_test`` is imported.
-* Any functions in them that start with ``test_`` are run as tests.
-* Any classes that start with ``Test`` are treated similarly, with methods that
- begin with ``test_`` treated as tests.
-
-
-.. nextslide:: pytest
-
-This test running framework is simple, flexible and configurable.
-
-`Read the documentation`_ for more information.
-
-.. _Read the documentation: http://pytest.org/latest/getting-started.html#getstarted
-
-.. nextslide:: Test Driven Development
-
-What we've just done here is the first step in what is called **Test Driven
-Development**.
-
-A bunch of tests exist, but the code to make them pass does not yet exist.
-
-The red you see in the terminal when we run our tests is a goad to us to write
-the code that fixes these tests.
-
-Let's do that next!
-
-===
-LAB
-===
-
-Pick an example from codingbat:
-
-``http://codingbat.com``
-
-Do a bit of test-driven development on it:
-
- * run somethign on the web site.
- * write a few tests using the examples from the site.
- * then write the function, and fix it 'till it passes the tests.
=========
@@ -890,241 +738,39 @@ Homework
Catch up!
---------
+* Finish the LABs from today
+ - Exceptions lab
-* First task -- catch up from last week.
-
- - and add some tests
- - and list (and dict, and set) comprehensions...
-
-* Then on to some exercises....
-
-
-List comprehensions
---------------------
-
-Note: this is a bit of a "backwards" exercise --
-we show you code, you figure out what it does.
-
-As a result, not much to submit -- but so we can give you credit, submit
-a file with a solution to the final problem.
-
-.. code-block:: python
-
- >>> feast = ['lambs', 'sloths', 'orangutans', 'breakfast cereals', 'fruit bats']
-
- >>> comprehension = [delicacy.capitalize() for delicacy in feast]
-
-What is the output of:
-
-.. code-block:: python
-
- >>> comprehension[0]
- ???
-
- >>> comprehension[2]
- ???
-
-(figure it out before you try it)
-
-.. nextslide:: 2. Filtering lists with list comprehensions
-
-
-.. code-block:: python
-
- >>> feast = ['spam', 'sloths', 'orangutans', 'breakfast cereals',
- 'fruit bats']
-
- >>> comprehension = [delicacy for delicacy in feast if len(delicacy) > 6]
-
-What is the output of:
-
-.. code-block:: python
-
- >>> len(feast)
- ???
-
- >>> len(comprehension)
- ???
-
-(figure it out first!)
-
-.. nextslide:: 3. Unpacking tuples in list comprehensions
-
-
-.. code-block:: python
-
- >>> list_of_tuples = [(1, 'lumberjack'), (2, 'inquisition'), (4, 'spam')]
-
- >>> comprehension = [ skit * number for number, skit in list_of_tuples ]
-
-What is the output of:
-
-.. code-block:: python
-
- >>> comprehension[0]
- ???
-
- >>> len(comprehension[2])
- ???
-
-.. nextslide:: 4. Double list comprehensions
-
-.. code-block:: python
-
- >>> list_of_eggs = ['poached egg', 'fried egg']
-
- >>> list_of_meats = ['lite spam', 'ham spam', 'fried spam']
-
- >>> comprehension = [ '{0} and {1}'.format(egg, meat) for egg in list_of_eggs for meat in list_of_meats]
-
-What is the output of:
-
-.. code-block:: python
-
- >>> len(comprehension)
- ???
-
- >>> comprehension[0]
- ???
-
-.. nextslide:: 5. Set comprehensions
-
-
-.. code-block:: python
-
- >>> comprehension = { x for x in 'aabbbcccc'}
-
-What is the output of:
-
-.. code-block:: python
-
- >>> comprehension
- ???
-
-.. nextslide:: 6. Dictionary comprehensions
-
-
-.. code-block:: python
-
- >>> dict_of_weapons = {'first': 'fear',
- 'second': 'surprise',
- 'third':'ruthless efficiency',
- 'forth':'fanatical devotion',
- 'fifth': None}
- >>> dict_comprehension = \
- { k.upper(): weapon for k, weapon in dict_of_weapons.iteritems() if weapon}
-
-What is the output of:
-
-.. code-block:: python
-
- >>> 'first' in dict_comprehension
- ???
- >>> 'FIRST' in dict_comprehension
- ???
- >>> len(dict_of_weapons)
- ???
- >>> len(dict_comprehension)
- ???
-
-.. nextslide:: Other resources
-
-
-See also:
-
-https://github.com/gregmalcolm/python_koans
-
-https://github.com/gregmalcolm/python_koans/blob/master/python2/koans/about_comprehension.py
-
-
-.. nextslide:: 7. Count even numbers
-
-
-Use test-driven development!
-
-This is from CodingBat "count_evens" (http://codingbat.com/prob/p189616)
-
-*Using a list comprehension*, return the number of even ints in the given array.
-
-Note: the % "mod" operator computes the remainder, e.g. ``5 % 2`` is 1.
-
-.. code-block:: python
-
- count_evens([2, 1, 2, 3, 4]) == 3
-
- count_evens([2, 2, 0]) == 3
-
- count_evens([1, 3, 5]) == 0
-
-
-.. code-block:: python
-
- def count_evens(nums):
- one_line_comprehension_here
-
-
-``dict`` and ``set`` comprehensions
-------------------------------------
-
-Let's revisiting the dict/set lab -- see how much you can do with
-comprehensions instead.
-
-Specifically, look at these:
-
-First a slightly bigger, more interesting (or at least bigger..) dict:
-
-.. code-block:: python
-
- food_prefs = {"name": u"Chris",
- u"city": u"Seattle",
- u"cake": u"chocolate",
- u"fruit": u"mango",
- u"salad": u"greek",
- u"pasta": u"lasagna"}
-
-.. nextslide:: Working with this dict:
-
-1. Print the dict by passing it to a string format method, so that you
-get something like:
-
- "Chris is from Seattle, and he likes chocolate cake, mango fruit,
- greek salad, and lasagna pasta"
-
-2. Using a list comprehension, build a dictionary of numbers from zero
-to fifteen and the hexadecimal equivalent (string is fine).
-
-3. Do the previous entirely with a dict comprehension -- should be a one-liner
-
-4. Using the dictionary from item 1: Make a dictionary using the same
-keys but with the number of 'a's in each value. You can do this either
-by editing the dict in place, or making a new one. If you edit in place,
-make a copy first!
-
-.. nextslide::
+* Catch up from last week.
-5. Create sets s2, s3 and s4 that contain numbers from zero through twenty,
-divisible 2, 3 and 4.
+ - Add Exception handling to mailroom
+ - And list (and dict, and set) comprehensions...
- a. Do this with one set comprehension for each set.
+* If you've done all that -- check out the collections module:
- b. What if you had a lot more than 3? -- Don't Repeat Yourself (DRY)
+ - https://docs.python.org/3.5/library/collections.html
+ - here's a good overview: https://pymotw.com/3/collections/
- - create a sequence that holds all three sets
+====================================
+Material to review before next week:
+====================================
- - loop through that sequence to build the sets up -- so no repeated code.
+**Unit Testing:**
- c. Extra credit: do it all as a one-liner by nesting a set comprehension inside a list comprehension. (OK, that may be getting carried away!)
+* Dive into Python: chapter 9:
+ http://www.diveintopython3.net/unit-testing.html
-================================
-Material to review for Homework:
-================================
+NOTE: you will find that most introductions to unit testing with Python use the builtin ``unitest`` module. However, it is a bit heavyweight, and requires some knowledge of OOP -- classes, etc. So we'll be using pytest in this class: http://doc.pytest.org/en/latest/. But the principles of testing are the same.
-Raymond Hettinger:
+* Ned Batchelder's intro to testing presentation:
-https://youtu.be/HTLu2DFOdTg
+ - http://nedbatchelder.com/text/test0.html
-https://speakerdeck.com/pyconslides/pythons-class-development-toolkit-by-raymond-hettinger
+** Advanced Argument Passing
+* arguments and parameters:
+ - http://stupidpythonideas.blogspot.com/2013/08/arguments-and-parameters.html
+ - https://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/
diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst
index 5d4bd0ad..d5e624b9 100644
--- a/slides_sources/source/session06.rst
+++ b/slides_sources/source/session06.rst
@@ -1,25 +1,8 @@
+.. include:: include.rst
-********************************************************
-Session Six: Functional and Object Oriented Programming
-********************************************************
-
-.. rst-class:: left medium
-
- Lambda and Functional programming.
-
- Object oriented programming:
-
- classes, instances, attributes, and subclassing
-
-=====
-NOTE:
-=====
-
-.. rst-class:: center large
-
- Veteran's Day:
-
- No class next week
+***********************************************
+Session Six: Testing, Advanced Argument Passing
+***********************************************
======================
Lightning Talks Today:
@@ -27,14 +10,11 @@ Lightning Talks Today:
.. rst-class:: medium
- Aleksey Kramer
+ Adam Hollis
- Alexander R Galvin
-
- Gideon I Sylvan
-
- Hui Zhang
+ Nachiket Galande
+ Paul A Casey
================
Review/Questions
@@ -43,11 +23,10 @@ Review/Questions
Review of Previous Class
------------------------
-* Argument Passing: ``*args``, ``**kwargs``
+* Exceptions
-* comprehensions
+* Comprehensions
-* testing (a bit more on that soon)
===============
Homework review
@@ -76,16 +55,6 @@ rich comparisons: numpy
.. nextslide::
-Binary mode for files:
-
-.. code-block:: python
-
- infile = open(infilename, 'rb')
- outfile = open(outfilename, 'wb')
-
-|
-|
-
You don't actually need to use the result of a list comp:
.. code-block:: python
@@ -93,1016 +62,773 @@ You don't actually need to use the result of a list comp:
for i, st in zip( divisors, sets):
[ st.add(j) for j in range(21) if not j%i ]
+.. nextslide::
-The collections module
------------------------
-
-The collections module has a numbe rof handy special purpose
-collections:
-
- * defautltdict
- * namedtuple
- * deque
- * Counter
-
-https://docs.python.org/2/library/collections.html
-
-defaultdict
------------
-
-An alternative to ``dict.setdefault()``
-
-Makes sense when you are buildng a dict where every value will be the same thing
-
-Carolyn found this in the ``collections`` package. Useful for the trigrams
-assignment:
-
-.. code-block:: python
-
- from collections import defaultdict
-
- trigrams = defaultdict(list)
- ...
- trigrams[pair].append(follower)
+Python functions are objects, so if you don't call them, you don't get an error, you just get the function object, usually not what you want::
-Counter
--------
+ elif donor_name.lower == "exit":
-``Counter``:
+this is comparing the string ``lower`` method to the string "exit" and they are never going to be equal!
-Hui Zhang found this for counting how many students used which previous
-languages.
+That should be::
-See my example in ``/Solutions/Session05``
+ elif donor_name.lower() == "exit":
+This is actually a pretty common typo -- keep an eye out for it when you get strange errors, or something just doesn't seem to be getting triggered.
-============================
-Test Driven development demo
-============================
+long strings
+------------
-In ``Examples/Session06/``
+if you need to do along string literal, sometimes a triple quoted string is perfect::
+ """this is a long string.
+ I want it to hvae multiple lines.
+ so having the line endings automatic is great.
+ """
-===================
-Anonymous functions
-===================
+But you don't always want the line endings quite like that. And you may not want all that whitespace when fitting it into indented code.
-lambda
-------
+It turns out that when you put a multiple strings together with no commas or anythign in between -- python will join them:
.. code-block:: ipython
- In [171]: f = lambda x, y: x+y
- In [172]: f(2,3)
- Out[172]: 5
-
-Content can only be an expression -- not a statement
-
-Anyone remember what the difference is?
-
-Called "Anonymous": it doesn't need a name.
+ In [81]: "this is " "a string " "built up of parts"
+ Out[81]: 'this is a string built up of parts'
.. nextslide::
-It's a python object, it can be stored in a list or other container
-
-.. code-block:: ipython
-
- In [7]: l = [lambda x, y: x+y]
- In [8]: type(l[0])
- Out[8]: function
-
-
-And you can call it:
-
-.. code-block:: ipython
-
- In [9]: l[0](3,4)
- Out[9]: 7
-
-
-Functions as first class objects
----------------------------------
-
-You can do that with "regular" functions too:
-
-.. code-block:: ipython
-
- In [12]: def fun(x,y):
- ....: return x+y
- ....:
- In [13]: l = [fun]
- In [14]: type(l[0])
- Out[14]: function
- In [15]: l[0](3,4)
- Out[15]: 7
-
-
+If it's in parentheses, you can put the next chunk on the next line:
-======================
-Functional Programming
-======================
+.. code-block:: python
-map
----
+ print("{} is from {}, and he likes "
+ "{} cake, {} fruit, {} salad, "
+ "and {} pasta.".format(food_prefs["name"],
+ food_prefs["city"],
+ food_prefs["cake"],
+ food_prefs["fruit"],
+ food_prefs["salad"],
+ food_prefs["pasta"]))
-``map`` "maps" a function onto a sequence of objects -- It applies the function to each item in the list, returning another list
+pretty print
+------------
+If you need to print our nested (or large) data structure in a more readable fashion, the "pretty print" module is handy:
.. code-block:: ipython
- In [23]: l = [2, 5, 7, 12, 6, 4]
- In [24]: def fun(x):
- return x*2 + 10
- In [25]: map(fun, l)
- Out[25]: [14, 20, 24, 34, 22, 18]
-
+ from pprint import pprint
-But if it's a small function, and you only need it once:
+ In [28]: print(food_prefs)
+ {'pasta': 'lasagna', 'cake': 'chocolate', 'salad': 'greek', 'fruit': 'mango', 'name': 'Chris', 'city': 'Seattle'}
-.. code-block:: ipython
-
- In [26]: map(lambda x: x*2 + 10, l)
- Out[26]: [14, 20, 24, 34, 22, 18]
+ In [29]: pprint(food_prefs)
+ {'cake': 'chocolate',
+ 'city': 'Seattle',
+ 'fruit': 'mango',
+ 'name': 'Chris',
+ 'pasta': 'lasagna',
+ 'salad': 'greek'}
+Exceptions
+----------
-filter
-------
-
-``filter`` "filters" a sequence of objects with a boolean function --
-It keeps only those for which the function is True
+Adding stuff to an Exception:
-To get only the even numbers:
+Example from slack
-.. code-block:: ipython
- In [27]: l = [2, 5, 7, 12, 6, 4]
- In [28]: filter(lambda x: not x%2, l)
- Out[28]: [2, 12, 6, 4]
+Anything else?
+--------------
-If you pass ``None`` to ``filter()``, you get only items that evaluate to true:
+.. rst-class:: center medium
-.. code-block:: ipython
+ Anything else you want me to go over?
- In [1]: l = [1, 0, 2.3, 0.0, 'text', '', [1,2], [], False, True, None ]
- In [2]: filter(None, l)
- Out[2]: [1, 2.3, 'text', [1, 2], True]
+Lightning Talks
+----------------
+.. rst-class:: medium
+|
+| Adam Hollis
+|
+| Nachiket Galande
+|
-reduce
-------
+=======
+Testing
+=======
-``reduce`` "reduces" a sequence of objects to a single object with a function that combines two arguments
+.. rst-class:: build left
+.. container::
-To get the sum:
+ You've already seen a very basic testing strategy.
-.. code-block:: ipython
+ You've written some tests using that strategy.
- In [30]: l = [2, 5, 7, 12, 6, 4]
- In [31]: reduce(lambda x,y: x+y, l)
- Out[31]: 36
+ These tests were pretty basic, and a bit awkward in places (testing error
+ conditions in particular).
+ .. rst-class:: centered large
-To get the product:
+ **It gets better**
-.. code-block:: ipython
+Test Runners
+------------
- In [32]: reduce(lambda x,y: x*y, l)
- Out[32]: 20160
+So far our tests have been limited to code in an ``if __name__ == "__main__":``
+block.
-or
+.. rst-class:: build
-.. code-block:: ipython
+* They are run only when the file is executed
+* They are always run when the file is executed
+* You can't do anything else when the file is executed without running tests.
- In [13]: import operator
+.. rst-class:: build
+.. container::
- In [14]: reduce(operator.mul, l)
- Out[14]: 20160
+ This is not optimal.
-Comprehensions
---------------
+ Python provides testing systems to help.
-Couldn't you do all this with comprehensions?
-Yes:
+Standard Library: ``unittest``
+-------------------------------
-.. code-block:: ipython
+The original testing system in Python.
- In [33]: [x+2 + 10 for x in l]
- Out[33]: [14, 17, 19, 24, 18, 16]
+``import unittest``
- In [34]: [x for x in l if not x%2]
- Out[34]: [2, 12, 6, 4]
+More or less a port of ``Junit`` from Java
- In [6]: l
- Out[6]: [1, 0, 2.3, 0.0, 'text', '', [1, 2], [], False, True, None]
- In [7]: [i for i in l if i]
- Out[7]: [1, 2.3, 'text', [1, 2], True]
+A bit verbose: you have to write classes & methods
-(Except Reduce)
+(And we haven't covered that yet!)
-But Guido thinks almost all uses of reduce are really ``sum()``
-Functional Programming
-----------------------
+Using ``unittest``
+-------------------
-Comprehensions and map, filter, reduce are all "functional programming" approaches}
+You write subclasses of the ``unittest.TestCase`` class:
-``map, filter`` and ``reduce`` pre-date comprehensions in Python's history
+.. code-block:: python
-Some people like that syntax better
+ # in test.py
+ import unittest
-And "map-reduce" is a big concept these days for parallel processing of "Big Data" in NoSQL databases.
+ class MyTests(unittest.TestCase):
+ def test_tautology(self):
+ self.assertEquals(1, 1)
-(Hadoop, MongoDB, etc.)
+Then you run the tests by using the ``main`` function from the ``unittest``
+module:
+.. code-block:: python
-A bit more about lambda
-------------------------
+ # in test.py
+ if __name__ == '__main__':
+ unittest.main()
-Can also use keyword arguments
+.. nextslide:: Testing Your Code
-.. code-block:: ipython
+This way, you can write your code in one file and test it from another:
- In [186]: l = []
- In [187]: for i in range(3):
- l.append(lambda x, e=i: x**e)
- .....:
- In [189]: for f in l:
- print f(3)
- 1
- 3
- 9
+.. code-block:: python
-Note when the keyword argument is evaluated: this turns out to be very handy!
+ # in my_mod.py
+ def my_func(val1, val2):
+ return val1 * val2
-===
-LAB
-===
+ # in test_my_mod.py
+ import unittest
+ from my_mod import my_func
-lambda and keyword argument magic
------------------------------------
+ class MyFuncTestCase(unittest.TestCase):
+ def test_my_func(self):
+ test_vals = (2, 3)
+ expected = reduce(lambda x, y: x * y, test_vals)
+ actual = my_func(*test_vals)
+ self.assertEquals(expected, actual)
-Write a function that returns a list of n functions,
-such that each one, when called, will return the input value,
-incremented by an increasing number.
+ if __name__ == '__main__':
+ unittest.main()
-Use a for loop, ``lambda``, and a keyword argument
+.. nextslide:: Advantages of ``unittest``
-( Extra credit ):
+.. rst-class:: build
+.. container::
-Do it with a list comprehension, instead of a for loop
+ The ``unittest`` module is pretty full featured
-Not clear? here's what you should get
+ It comes with the standard Python distribution, no installation required.
-.. nextslide:: Example calling code
+ It provides a wide variety of assertions for testing all sorts of situations.
-.. code-block:: ipython
+ It allows for a setup and tear down workflow both before and after all tests and before and after each test.
- In [96]: the_list = function_builder(4)
- ### so the_list should contain n functions (callables)
- In [97]: the_list[0](2)
- Out[97]: 2
- ## the zeroth element of the list is a function that add 0
- ## to the input, hence called with 2, returns 2
- In [98]: the_list[1](2)
- Out[98]: 3
- ## the 1st element of the list is a function that adds 1
- ## to the input value, thus called with 2, returns 3
- In [100]: for f in the_list:
- print f(5)
- .....:
- 5
- 6
- 7
- 8
- ### If you loop through them all, and call them, each one adds one more
- to the input, 5... i.e. the nth function in the list adds n to the input.
+ It's well known and well understood.
+.. nextslide:: Disadvantages:
+.. rst-class:: build
+.. container::
-Lightning Talks
-----------------
-.. rst-class:: medium
+ It's Object Oriented, and quite "heavyweight".
-|
-| Aleksey Kramer
-|
-| Alexander R Galvin
-|
+ - modeled after Java's ``junit`` and it shows...
+ It uses the framework design pattern, so knowing how to use the features
+ means learning what to override.
-===========================
-Object Oriented Programming
-===========================
+ Needing to override means you have to be cautious.
-Object Oriented Programming
----------------------------
+ Test discovery is both inflexible and brittle.
-More about Python implementation than OO design/strengths/weaknesses
+ And there is no built-in parameterized testing.
-One reason for this:
+Other Options
+-------------
-Folks can't even agree on what OO "really" means
+There are several other options for running tests in Python.
-See: The Quarks of Object-Oriented Development
+* `Nose`: https://nose.readthedocs.org/
- - Deborah J. Armstrong
+* `pytest`: http://pytest.org/latest/
-http://agp.hx0.ru/oop/quarks.pdf
+* ... (many frameworks supply their own test runners: e.g. django)
+Both are very capable and widely used. I have a personal preference for pytest
-.. nextslide::
+-- so we'll use it for this class
-Is Python a "True" Object-Oriented Language?
+Installing ``pytest``
+---------------------
-(Doesn't support full encapsulation, doesn't *require*
-classes, etc...)
+The first step is to install the package:
-.. nextslide::
+.. code-block:: bash
-.. rst-class:: center large
+ $ python3 -m pip install pytest
- I don't Care!
+Once this is complete, you should have a ``py.test`` command you can run
+at the command line:
+.. code-block:: bash
-Good software design is about code re-use, clean separation of concerns,
-refactorability, testability, etc...
+ $ py.test
-OO can help with all that, but:
- * It doesn't guarantee it
- * It can get in the way
+If you have any tests in your repository, that will find and run them.
-.. nextslide::
+.. rst-class:: build
+.. container::
-Python is a Dynamic Language
+ **Do you?**
-That clashes with "pure" OO
+Pre-existing Tests
+------------------
-Think in terms of what makes sense for your project
- -- not any one paradigm of software design.
+Let's take a look at some examples.
+in ``IntroPython2016\Examples\Session06``
-.. nextslide::
+.. code-block:: bash
-So what is "object oriented programming"?
+ $ py.test
-|
- "Objects can be thought of as wrapping their data
- within a set of functions designed to ensure that
- the data are used appropriately, and to assist in
- that use"
+You can also run py.test on a particular test file:
-|
+.. code-block:: bash
-http://en.wikipedia.org/wiki/Object-oriented_programming
+ $ py.test test_random_unitest.py
-.. nextslide::
+The results you should have seen when you ran ``py.test`` above come
+partly from these files.
-Even simpler:
+Let's take a few minutes to look these files over.
+[demo]
-"Objects are data and the functions that act on them in one place."
+What's Happening Here.
+----------------------
-This is the core of "encapsulation"
+When you run the ``py.test`` command, ``pytest`` starts in your current
+working directory and searches the filesystem for things that might be tests.
-In Python: just another namespace.
+It follows some simple rules:
-.. nextslide::
+* Any python file that starts with ``test_`` or ``_test`` is imported.
-The OO buzzwords:
+* Any functions in them that start with ``test_`` are run as tests.
- * data abstraction
- * encapsulation
- * modularity
- * polymorphism
- * inheritance
+* Any classes that start with ``Test`` are treated similarly, with methods that begin with ``test_`` treated as tests.
-Python does all of this, though it doesn't enforce it.
+( don't worry about "classes" part just yet ;-) )
-.. nextslide::
+pytest
+------
-You can do OO in C
+This test running framework is simple, flexible and configurable.
-(see the GTK+ project)
+Read the documentation for more information:
+http://pytest.org/latest/getting-started.html#getstarted
-"OO languages" give you some handy tools to make it easier (and safer):
+It will run ``unittest`` tests for you.
- * polymorphism (duck typing gives you this anyway)
- * inheritance
+But in addition to finding and running tests, it makes writting tests simple, and provides a bunch of nifty utilities to support more complex testing.
-.. nextslide::
+Test Driven Development
+-----------------------
+in the Examples dir, try::
-OO is the dominant model for the past couple decades
+ $ py.test test_cigar_party
-You will need to use it:
+What we've just done here is the first step in what is called:
-- It's a good idea for a lot of problems
+.. rst-class:: centered
-- You'll need to work with OO packages
+ **Test Driven Development**.
-(Even a fair bit of the standard library is Object Oriented)
+A bunch of tests exist, but the code to make them pass does not yet exist.
+The red you see in the terminal when we run our tests is a goad to us to write the code that fixes these tests.
-.. nextslide:: Some definitions
+Let's do that next!
-class
- A category of objects: particular data and behavior: A "circle" (same as a type in python)
+Test Driven development demo
+-----------------------------
-instance
- A particular object of a class: a specific circle
+Open up:
-object
- The general case of a instance -- really any value (in Python anyway)
+``Examples/Session06/test_cigar_party.py``
-attribute
- Something that belongs to an object (or class): generally thought of
- as a variable, or single object, as opposed to a ...
+and:
-method
- A function that belongs to a class
+``Examples/Session06/cigar_party.py``
-.. nextslide::
+and run::
-.. rst-class:: center
+ $ py.teset test_cigar_party.py
- Note that in python, functions are first class objects, so a method *is* an attribute
+Now go in to ``cigar_party.py`` and let's fix the tests.
+Let's play with codingbat.py also...
-==============
-Python Classes
-==============
+===
+LAB
+===
.. rst-class:: left
- The ``class`` statement
-
- ``class`` creates a new type object:
-
- .. code-block:: ipython
+ Pick an example from codingbat:
- In [4]: class C(object):
- pass
- ...:
- In [5]: type(C)
- Out[5]: type
+ ``http://codingbat.com``
- A class is a type -- interesting!
+ Do a bit of test-driven development on it:
- It is created when the statement is run -- much like ``def``
+ * run something on the web site.
+ * write a few tests using the examples from the site.
+ * then write the function, and fix it 'till it passes the tests.
- You don't *have* to subclass from ``object``, but you *should*
+ Do at least two of these...
- (note on "new style" classes)
-
-Python Classes
+Lightning Talk
--------------
-About the simplest class you can write
-
-.. code-block:: python
-
- >>> class Point(object):
- ... x = 1
- ... y = 2
- >>> Point
-
- >>> Point.x
- 1
- >>> p = Point()
- >>> p
- <__main__.Point instance at 0x2de918>
- >>> p.x
- 1
+.. rst-class:: medium
-.. nextslide::
+ |
+ | Paul A Casey
+ |
-Basic Structure of a real class:
+=========================
+Advanced Argument Passing
+=========================
-.. code-block:: python
+This is a very, very nifty Python feature -- it really lets you write dynamic programs.
- class Point(object):
- # everything defined in here is in the class namespace
-
- def __init__(self, x, y):
- self.x = x
- self.y = y
+Keyword arguments
+-----------------
- ## create an instance of the class
- p = Point(3,4)
+When defining a function, you can specify only what you need -- in any order
- ## access the attributes
- print "p.x is:", p.x
- print "p.y is:", p.y
+.. code-block:: ipython
+ In [151]: def fun(x=0, y=0, z=0):
+ print(x,y,z)
+ .....:
+ In [152]: fun(1,2,3)
+ 1 2 3
+ In [153]: fun(1, z=3)
+ 1 0 3
+ In [154]: fun(z=3, y=2)
+ 0 2 3
-see: ``Examples/Session06/simple_classes.py``
.. nextslide::
-The Initializer
-The ``__init__`` special method is called when a new instance of a class is created.
-
-You can use it to do any set-up you need
+A Common Idiom:
.. code-block:: python
- class Point(object):
- def __init__(self, x, y):
- self.x = x
- self.y = y
-
-
-It gets the arguments passed when you call the class object:
-
-.. code-block:: python
+ def fun(x, y=None):
+ if y is None:
+ do_something_different
+ go_on_here
- Point(x, y)
.. nextslide::
+Can set defaults to variables
-What is this ``self`` thing?
+.. code-block:: ipython
-The instance of the class is passed as the first parameter for every method.
+ In [156]: y = 4
+ In [157]: def fun(x=y):
+ print("x is:", x)
+ .....:
+ In [158]: fun()
+ x is: 4
-"``self``" is only a convention -- but you DO want to use it.
-.. code-block:: python
+.. nextslide::
- class Point(object):
- def a_function(self, x, y):
- ...
+Defaults are evaluated when the function is defined
+.. code-block:: ipython
-Does this look familiar from C-style procedural programming?
+ In [156]: y = 4
+ In [157]: def fun(x=y):
+ print("x is:", x)
+ .....:
+ In [158]: fun()
+ x is: 4
+ In [159]: y = 6
+ In [160]: fun()
+ x is: 4
+This is a **very** important point -- I will repeat it!
-.. nextslide::
-Anything assigned to a ``self.`` attribute is kept in the instance
-name space -- ``self`` *is* the instance.
+Function arguments in variables
+-------------------------------
-That's where all the instance-specific data is.
+When a function is called, its arguments are really just:
+
+* a tuple (positional arguments)
+* a dict (keyword arguments)
.. code-block:: python
- class Point(object):
- size = 4
- color= "red"
- def __init__(self, x, y):
- self.x = x
- self.y = y
+ def f(x, y, w=0, h=0):
+ print("position: {}, {} -- shape: {}, {}".format(x, y, w, h))
-.. nextslide::
+ position = (3,4)
+ size = {'h': 10, 'w': 20}
-Anything assigned in the class scope is a class attribute -- every
-instance of the class shares the same one.
+ >>> f(*position, **size)
+ position: 3, 4 -- shape: 20, 10
-Note: the methods defined by ``def`` are class attributes as well.
-The class is one namespace, the instance is another.
+Function parameters in variables
+--------------------------------
+You can also pull the parameters out in the function as a tuple and a dict:
-.. code-block:: python
+.. code-block:: ipython
- class Point(object):
- size = 4
- color= "red"
- ...
- def get_color():
- return self.color
- >>> p3.get_color()
- 'red'
+ def f(*args, **kwargs):
+ print("the positional arguments are:", args)
+ print("the keyword arguments are:", kwargs)
+ In [389]: f(2, 3, this=5, that=7)
+ the positional arguments are: (2, 3)
+ the keyword arguments are: {'this': 5, 'that': 7}
-class attributes are accessed with ``self`` also.
+This can be very powerful...
+Passing a dict to str.format()
+-------------------------------
-.. nextslide::
+Now that you know that keyword args are really a dict,
+you know how this nifty trick works:
-Typical methods:
+The string ``format()`` method takes keyword arguments:
-.. code-block:: python
+.. code-block:: ipython
- class Circle(object):
- color = "red"
+ In [24]: "My name is {first} {last}".format(last="Barker", first="Chris")
+ Out[24]: 'My name is Chris Barker'
- def __init__(self, diameter):
- self.diameter = diameter
+Build a dict of the keys and values:
- def grow(self, factor=2):
- self.diameter = self.diameter * factor
+.. code-block:: ipython
+ In [25]: d = {"last":"Barker", "first":"Chris"}
-Methods take some parameters, manipulate the attributes in ``self``.
+And pass to ``format()``with ``**``
-They may or may not return something useful.
+.. code-block:: ipython
-.. nextslide::
+ In [26]: "My name is {first} {last}".format(**d)
+ Out[26]: 'My name is Chris Barker'
-Gotcha!
+Kinda handy for the dict lab, eh?
-.. code-block:: python
+This:
- ...
- def grow(self, factor=2):
- self.diameter = self.diameter * factor
- ...
- In [205]: C = Circle(5)
- In [206]: C.grow(2,3)
+.. code-block:: ipython
- TypeError: grow() takes at most 2 arguments (3 given)
+ print("{} is from {}, and he likes "
+ "{} cake, {} fruit, {} salad, "
+ "and {} pasta.".format(food_prefs["name"],
+ food_prefs["city"],
+ food_prefs["cake"],
+ food_prefs["fruit"],
+ food_prefs["salad"],
+ food_prefs["pasta"]))
-Huh???? I only gave 2
+Becomes:
-``self`` is implicitly passed in for you by python.
+.. code-block:: ipython
-(demo of bound vs. unbound methods)
+ print("{name} is from {city}, and he likes "
+ "{cake} cake, {fruit} fruit, {salad} salad, "
+ "and {pasta} pasta.".format(**food_prefs))
LAB
----
-Let's say you need to render some html...
-
-The goal is to build a set of classes that render an html
-page like this:
-
-``Examples/Session06/sample_html.html``
+Time to play with all this to get a feel for it.
-We'll start with a single class, then add some sub-classes
-to specialize the behavior
+:ref:`exercise_args_kwargs_lab`
-Details in:
+This is not all that clearly specified -- the goal is for you to
+experiment with various ways to define and call functions, so you
+can understand what's possible, and what happens with each call.
-:ref:`exercise_html_renderer`
+=====================================
+A bit more on mutability (and copies)
+=====================================
-Let's see if you can do step 1. in class...
-
-
-Lightning Talks
+mutable objects
----------------
-.. rst-class:: medium
-
-|
-| Gideon Sylvan
-|
-| Hui Zhang
-|
-
-=======================
-Subclassing/Inheritance
-=======================
-
-Inheritance
------------
-
-In object-oriented programming (OOP), inheritance is a way to reuse code
-of existing objects, or to establish a subtype from an existing object.
-
-Objects are defined by classes, classes can inherit attributes and behavior
-from pre-existing classes called base classes or super classes.
-
-The resulting classes are known as derived classes or subclasses.
-
-(http://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29)
-
-Subclassing
------------
-
-A subclass "inherits" all the attributes (methods, etc) of the parent class.
+We've talked about this: mutable objects can have their contents changed in place.
-You can then change ("override") some or all of the attributes to change the behavior.
+Immutable objects can not.
-You can also add new attributes to extend the behavior.
+This has implications when you have a container with mutable objects in it:
-The simplest subclass in Python:
-
-.. code-block:: python
-
- class A_subclass(The_superclass):
- pass
-
-``A_subclass`` now has exactly the same behavior as ``The_superclass``
-
-NOTE: when we put ``object`` in there, it means we are deriving from object -- getting core functionality of all objects.
-
-Overriding attributes
----------------------
-
-Overriding is as simple as creating a new attribute with the same name:
+.. code-block:: ipython
-.. code-block:: python
+ In [28]: list1 = [ [1,2,3], ['a','b'] ]
- class Circle(object):
- color = "red"
+one way to make a copy of a list:
- ...
+.. code-block:: ipython
- class NewCircle(Circle):
- color = "blue"
- >>> nc = NewCircle
- >>> print nc.color
- blue
+ In [29]: list2 = list1[:]
+ In [30]: list2 is list1
+ Out[30]: False
-all the ``self`` instances will have the new attribute.
+they are different lists.
-Overriding methods
-------------------
+.. nextslide::
-Same thing, but with methods (remember, a method *is* an attribute in python)
+What if we set an element to a new value?
-.. code-block:: python
+.. code-block:: ipython
- class Circle(object):
- ...
- def grow(self, factor=2):
- """grows the circle's diameter by factor"""
- self.diameter = self.diameter * factor
- ...
+ In [31]: list1[0] = [5,6,7]
- class NewCircle(Circle):
- ...
- def grow(self, factor=2):
- """grows the area by factor..."""
- self.diameter = self.diameter * math.sqrt(2)
+ In [32]: list1
+ Out[32]: [[5, 6, 7], ['a', 'b']]
+ In [33]: list2
+ Out[33]: [[1, 2, 3], ['a', 'b']]
-all the instances will have the new method
+So they are independent.
.. nextslide::
-Here's a program design suggestion:
+But what if we mutate an element?
-"""
-
-Whenever you override a method, the
-interface of the new method should be the same as the old. It should take
-the same parameters, return the same type, and obey the same preconditions
-and postconditions.
+.. code-block:: ipython
-If you obey this rule, you will find that any function
-designed to work with an instance of a superclass, like a Deck, will also work
-with instances of subclasses like a Hand or PokerHand. If you violate this
-rule, your code will collapse like (sorry) a house of cards.
+ In [34]: list1[1].append('c')
-"""
+ In [35]: list1
+ Out[35]: [[5, 6, 7], ['a', 'b', 'c']]
-|
-| [ThinkPython 18.10]
-|
-| ( Demo of class vs. instance attributes )
+ In [36]: list2
+ Out[36]: [[1, 2, 3], ['a', 'b', 'c']]
+uuh oh! mutating an element in one list mutated the one in the other list.
-===================
-More on Subclassing
-===================
+.. nextslide::
-Overriding \_\_init\_\_
------------------------
+Why is that?
-``__init__`` common method to override}
+.. code-block:: ipython
-You often need to call the super class ``__init__`` as well
+ In [38]: list1[1] is list2[1]
+ Out[38]: True
-.. code-block:: python
+The elements are the same object!
- class Circle(object):
- color = "red"
- def __init__(self, diameter):
- self.diameter = diameter
- ...
- class CircleR(Circle):
- def __init__(self, radius):
- diameter = radius*2
- Circle.__init__(self, diameter)
+This is known as a "shallow" copy -- Python doesn't want to copy more than it needs to, so in this case, it makes a new list, but does not make copies of the contents.
+Same for dicts (and any container type -- even tuples!)
+If the elements are immutable, it doesn't really make a differnce -- but be very careful with mutable elements.
-exception to: "don't change the method signature" rule.
-More subclassing
+The copy module
----------------
-You can also call the superclass' other methods:
-.. code-block:: python
-
- class Circle(object):
- ...
- def get_area(self, diameter):
- return math.pi * (diameter/2.0)**2
+most objects have a way to make copies (``dict.copy()`` for instance).
+but if not, you can use the ``copy`` module to make a copy:
- class CircleR2(Circle):
- ...
- def get_area(self):
- return Circle.get_area(self, self.radius*2)
-
-There is nothing special about ``__init__`` except that it gets called
-automatically when you instantiate an instance.
+.. code-block:: ipython
+ In [39]: import copy
-When to Subclass
-----------------
+ In [40]: list3 = copy.copy(list2)
-"Is a" relationship: Subclass/inheritance
+ In [41]: list3
+ Out[41]: [[1, 2, 3], ['a', 'b', 'c']]
-"Has a" relationship: Composition
+This is also a shallow copy.
.. nextslide::
-"Is a" vs "Has a"
-
-You may have a class that needs to accumulate an arbitrary number of objects.
-
-A list can do that -- so should you subclass list?
-
-Ask yourself:
-
--- **Is** your class a list (with some extra functionality)?
-
-or
-
--- Does you class **have** a list?
-
-You only want to subclass list if your class could be used anywhere a list can be used.
-
-
-Attribute resolution order
---------------------------
-
-When you access an attribute:
-
-``an_instance.something``
-
-Python looks for it in this order:
-
- * Is it an instance attribute ?
- * Is it a class attribute ?
- * Is it a superclass attribute ?
- * Is it a super-superclass attribute ?
- * ...
-
-
-It can get more complicated...
-
-http://www.python.org/getit/releases/2.3/mro/
-
-http://python-history.blogspot.com/2010/06/method-resolution-order.html
-
-
-What are Python classes, really?
---------------------------------
-
-Putting aside the OO theory...
-
-Python classes are:
-
- * Namespaces
-
- * One for the class object
- * One for each instance
-
- * Attribute resolution order
- * Auto tacking-on of ``self`` when methods are called
+But there is another option:
+.. code-block:: ipython
-That's about it -- really!
+ In [3]: list1
+ Out[3]: [[1, 2, 3], ['a', 'b', 'c']]
+ In [4]: list2 = copy.deepcopy(list1)
-Type-Based dispatch
--------------------
+ In [5]: list1[0].append(4)
-You'll see code that looks like this:
+ In [6]: list1
+ Out[6]: [[1, 2, 3, 4], ['a', 'b', 'c']]
-.. code-block:: python
+ In [7]: list2
+ Out[7]: [[1, 2, 3], ['a', 'b', 'c']]
- if isinstance(other, A_Class):
- Do_something_with_other
- else:
- Do_something_else
+``deepcopy`` recurses through the object, making copies of everything as it goes.
+.. nextslide::
-Usually better to use "duck typing" (polymorphism)
-But when it's called for:
+I happened on this thread on stack overflow:
- * ``isinstance()``
- * ``issubclass()``
+http://stackoverflow.com/questions/3975376/understanding-dict-copy-shallow-or-deep
-.. nextslide::
+The OP is pretty confused -- can you sort it out?
-GvR: "Five Minute Multi- methods in Python":
+Make sure you understand the difference between a reference, a shallow copy, and a deep copy.
-http://www.artima.com/weblogs/viewpost.jsp?thread=101605
+Mutables as default arguments:
+------------------------------
-http://www.python.org/getit/releases/2.3/mro/
+Another "gotcha" is using mutables as default arguments:
-http://python-history.blogspot.com/2010/06/method-resolution-order.html
+.. code-block:: ipython
+ In [11]: def fun(x, a=[]):
+ ....: a.append(x)
+ ....: print(a)
+ ....:
-Wrap Up
--------
+This makes sense: maybe you'd pass in a specific list, but if not, the default is an empty list.
-Thinking OO in Python:
+But:
-Think about what makes sense for your code:
+.. code-block:: ipython
-* Code re-use
-* Clean APIs
-* ...
+ In [12]: fun(3)
+ [3]
-Don't be a slave to what OO is *supposed* to look like.
+ In [13]: fun(4)
+ [3, 4]
-Let OO work for you, not *create* work for you
+Huh?!
.. nextslide::
-OO in Python:
+Remember that that default argument is defined when the function is created: there will be only one list, and every time the function is called, that same list is used.
-The Art of Subclassing: *Raymond Hettinger*
-http://pyvideo.org/video/879/the-art-of-subclassing
+The solution:
-"classes are for code re-use -- not creating taxonomies"
+The standard practice for such a mutable default argument:
-Stop Writing Classes: *Jack Diederich*
+.. code-block:: ipython
-http://pyvideo.org/video/880/stop-writing-classes
+ In [15]: def fun(x, a=None):
+ ....: if a is None:
+ ....: a = []
+ ....: a.append(x)
+ ....: print(a)
+ In [16]: fun(3)
+ [3]
+ In [17]: fun(4)
+ [4]
-"If your class has only two methods -- and one of them is ``__init__``
--- you don't need a class"
+You get a new list every time the function is called
========
Homework
========
-.. rst-class:: left medium
-
- * finish the lambda:keyword magic lab
-
- * functional files
-
- * html renderer
-
-
-Functional files
------------------
-
-Write a program that takes a filename and "cleans" the file be removing
-all the leading and trailing whitespace from each line.
-
-Read in the original file and write out a new one, either creating a new
-file or overwriting the existing one.
-
-Give your user the option of which to perform.
-
-Use ``map()`` to do the work.
+.. rst-class:: left
-Write a second version using a comprehension.
+ Finish up the Labs
-.. nextslide:: Hint
+ Write a complete set of unit tests for your mailroom program.
-``sys.argv`` hold the command line arguments the user typed in. If the
-user types:
+ * You will likely find that it is really hard to test without refactoring.
-.. code-block:: bash
+ * This is Good!
- $ python the_script a_file_name
+ * If code is hard to test -- it probably should be refactored.
-Then:
-.. code-block:: python
- import sys
- filename = sys.argv[1]
+Material to review for next week
+--------------------------------
-will get ``filename == "a_file_name"``
+Next week, we'll get started on Object Oriented Methods. It's a good idea to read up on it first -- so we can dive right in:
+ * Dive into Python3: 7.2 -- 7.3
+ http://www.diveintopython3.net/iterators.html#defining-classes
-Html rendering system:
------------------------
+ * Think Python: 15 -- 18
+ http://www.greenteapress.com/thinkpython/html/thinkpython016.html
-:ref:`exercise_html_renderer`
+ * LPTHW: 40 -- 44
+ http://learnpythonthehardway.org/book/ex40.html
-|
+[note that in py3 you don't need to inherit from object]
-You will build an html generator, using:
+Talk by Raymond Hettinger:
-* A Base Class with a couple methods
-* Subclasses overriding class attributes
-* Subclasses overriding a method
-* Subclasses overriding the ``__init__``
+Video of talk: https://youtu.be/HTLu2DFOdTg
-These are the core OO approaches
+Slides: https://speakerdeck.com/pyconslides/pythons-class-development-toolkit-by-raymond-hettinger
diff --git a/slides_sources/source/session07.rst b/slides_sources/source/session07.rst
index d47c51d3..46bbd8e4 100644
--- a/slides_sources/source/session07.rst
+++ b/slides_sources/source/session07.rst
@@ -1,22 +1,19 @@
+.. include:: include.rst
-.. Foundations 2: Python slides file, created by
- hieroglyph-quickstart on Wed Apr 2 18:42:06 2014.
+***************************
+Object Oriented Programming
+***************************
-***********************
-Session Seven: More OO
-***********************
+Lightning Talks today
+---------------------
-.. rst-class:: medium centered
-
-.. container::
-
- Multiple Inheritance
-
- Properties
-
- Class methods and static methods
+.. rst-class:: medium
- Special (Magic) Methods
+ |
+ | Charles E Robison
+ |
+ | Paul Vosper
+ |
================
Review/Questions
@@ -25,727 +22,898 @@ Review/Questions
Review of Previous Class
------------------------
-* Object Oriented Programming:
+.. rst-class:: medium
- - classes
+ Advanced Argument passing
- - instances
+ Testing
- - attributes and methods
+Any questions?
- - subclassing
+Should I go over my solution(s)?
- - overriding methods
-Homework review
----------------
+Notes from homework:
+--------------------
-Homework Questions?
+**chaining or...**
-Have you all got an HTML Renderer working?
+Consider this:
-Do you have a feel for classes, subclassing, overriding methods, ...?
+``elif selection == '3' or 'exit':``
-Personal Project
------------------
+Careful here: you want to check if selection is '3' or 'exit', but that is no quite what this means:
-The bulk of the homework for the rest of the class will be a personal project:
+You want:
-* It can be for fun, or something you need for your job.
-* It should be large enough to take a few weeks homework time to do.
-* It should demostrate that you can do something useful with python.
-* It should follow PEP8 (https://www.python.org/dev/peps/pep-0008)
-* It should have unit tests!
-* Ideally, it will be in version control (gitHub)
-* I'm not going to require an specific python features (i.e. classes): use
- what is appropriate for your project
+``(selection == '3') or (selection == 'exit')``
-* Due the Friday after the last class (December 12)
+== has higher precedence than or, so you don't need the parentheses.
-|
-| By next week, send me a project proposal: can be short and sweet.
-|
+``selection == '3' or selection == 'exit'``
+That feels like more typing, but that's what you have to do.
-Lightning Talks Today:
------------------------
+.. nextslide::
-.. rst-class:: medium
+So what does the first version mean?
- Andrew P Klock
+It would return true for '3', but would never fail. Due to operator precedence, it is:
- Vinay Gupta
+``(selection == '3') or 'exit'``
- Ousmane Conde
+so first it's checking if selection == '3', which will return True or False.
- Salim Hassan Hamed
+Then it does the or: ``True or 'exit'`` or ``False or 'exit'``
+``or`` returns the first "truthy" value it finds, to it will return either True or 'exit', regardless of the value of selection. 'exit' is truthy, so this if clause will always run.
-===================
-More on Subclassing
-===================
+(let's try this out in iPython)
-.. rst-class:: left
- I pointed you to this Video last class:
+===========================
+Object Oriented Programming
+===========================
- The Art of Subclassing: *Raymond Hettinger*
+.. rst-class:: medium centered
- http://pyvideo.org/video/879/the-art-of-subclassing
+.. container::
- If you haven't watched it, It's well worth your time
+ Classes
+ Instances
-What's a Subclass For?
-----------------------
+ Class and instance attributes
-The most salient points from that video are as follows:
+ Subclassing
-* **Subclassing is not for Specialization**
+ Overriding methods
-* **Subclassing is for Reusing Code**
-* **Bear in mind that the subclass is in charge**
-Multiple Inheritance
---------------------
+===========================
+Object Oriented Programming
+===========================
-Multiple inheritance: Inheriting from more than one class
+A Core approach to organizing code.
-Simply provide more than one parent.
+I'm going to go through this fast.
-.. code-block:: python
+So we can get to the actual coding.
- class Combined(Super1, Super2, Super3):
- def __init__(self, something, something else):
- # some custom initialization here.
- Super1.__init__(self, ......)
- Super2.__init__(self, ......)
- Super3.__init__(self, ......)
- # possibly more custom initialization
-(calls to the super class ``__init__`` are optional -- case dependent)
+Object Oriented Programming
+---------------------------
-.. nextslide:: Method Resolution Order
+More about Python implementation than OO design/strengths/weaknesses
-.. code-block:: python
+One reason for this:
- class Combined(Super1, Super2, Super3)
+Folks can't even agree on what OO "really" means
-Attributes are located bottom-to-top, left-to-right
+See: The Quarks of Object-Oriented Development
-* Is it an instance attribute ?
-* Is it a class attribute ?
-* Is it a superclass attribute ?
+ - Deborah J. Armstrong
- - Is it an attribute of the left-most superclass?
- - Is it an attribute of the next superclass?
- - and so on up the hierarchy...
+http://agp.hx0.ru/oop/quarks.pdf
-* Is it a super-superclass attribute ?
-* ... also left to right ...
-http://python-history.blogspot.com/2010/06/method-resolution-order.html
+.. nextslide::
-.. nextslide:: Mix-ins
+Is Python a "True" Object-Oriented Language?
-So why would you want to do this? One reason: *mixins*
+(Doesn't support full encapsulation, doesn't *require*
+classes, etc...)
-Provides an subset of expected functionality in a re-usable package.
+.. nextslide::
-Huh? this is why --
+.. rst-class:: center large
-Hierarchies are not always simple:
+ I don't Care!
-* Animal
- * Mammal
+Good software design is about code re-use, clean separation of concerns,
+refactorability, testability, etc...
- * GiveBirth()
+OO can help with all that, but:
+ * It doesn't guarantee it
+ * It can get in the way
- * Bird
+.. nextslide::
- * LayEggs()
+Python is a Dynamic Language
-Where do you put a Platypus?
+That clashes with "pure" OO
-Real World Example: `FloatCanvas`_
+Think in terms of what makes sense for your project
-.. _FloatCanvas: https://github.com/svn2github/wxPython/blob/master/3rdParty/FloatCanvas/floatcanvas/FloatCanvas.py#L485
+-- not any one paradigm of software design.
-.. nextslide:: New-Style Classes
+.. nextslide::
-All the class definitions we've been showing inherit from ``object``.
+So what is "object oriented programming"?
-This is referred to as a "new style" class.
+|
+ "Objects can be thought of as wrapping their data
+ within a set of functions designed to ensure that
+ the data are used appropriately, and to assist in
+ that use"
-They were introduced in python2.2 to better merge types and classes, and clean
-up a few things.
+|
-There are differences in method resolution order and properties.
+http://en.wikipedia.org/wiki/Object-oriented_programming
-**Always Make New-Style Classes**
+.. nextslide::
-(that is, always subclass from object...)
+Even simpler:
-The differences are subtle, and may not appear until they jump up to bite you.
+"Objects are data and the functions that act on them in one place."
-.. nextslide:: ``super()``
+This is the core of "encapsulation"
-``super()``: use it to call a superclass method, rather than explicitly calling
-the unbound method on the superclass.
+In Python: just another namespace.
-instead of:
+.. nextslide::
-.. code-block:: python
+The OO buzzwords:
- class A(B):
- def __init__(self, *args, **kwargs)
- B.__init__(self, *argw, **kwargs)
- ...
+ * data abstraction
+ * encapsulation
+ * modularity
+ * polymorphism
+ * inheritance
-You can do:
+Python does all of this, though it doesn't enforce it.
-.. code-block:: python
+.. nextslide::
- class A(B):
- def __init__(self, *args, **kwargs)
- super(A, self).__init__(*argw, **kwargs)
- ...
+You can do OO in C
-.. nextslide:: Caveats
+(see the GTK+ project)
-Caution: There are some subtle differences with multiple inheritance.
-You can use explicit calling to ensure that the 'right' method is called.
+"OO languages" give you some handy tools to make it easier (and safer):
-.. rst-class:: medium
+ * polymorphism (duck typing gives you this anyway)
+ * inheritance
- **Background**
-Two seminal articles about ``super()``:
+.. nextslide::
-"Super Considered Harmful" -- James Knight
+OO is the dominant model for the past couple decades
-https://fuhm.net/super-harmful/
+You will need to use it:
-"super() considered super!" -- Raymond Hettinger
+- It's a good idea for a lot of problems
-http://rhettinger.wordpress.com/2011/05/26/super-considered-super/}
+- You'll need to work with OO packages
-(Both worth reading....)
+(Even a fair bit of the standard library is Object Oriented)
+
+
+.. nextslide:: Some definitions
+
+class
+ A category of objects: particular data and behavior: A "circle" (same as a type in python)
-==========
-Properties
-==========
+instance
+ A particular object of a class: a specific circle
+
+object
+ The general case of a instance -- really any value (in Python anyway)
+
+attribute
+ Something that belongs to an object (or class): generally thought of
+ as a variable, or single object, as opposed to a ...
+
+method
+ A function that belongs to a class
+
+.. nextslide::
+
+.. rst-class:: center
+
+ Note that in python, functions are first class objects, so a method *is* an attribute
+
+
+==============
+Python Classes
+==============
.. rst-class:: left
-.. container::
- One of the strengths of Python is lack of clutter.
+ The ``class`` statement
- Attributes are simple and concise:
+ ``class`` creates a new type object:
.. code-block:: ipython
- In [5]: class C(object):
- def __init__(self):
- self.x = 5
- In [6]: c = C()
- In [7]: c.x
- Out[7]: 5
- In [8]: c.x = 8
- In [9]: c.x
- Out[9]: 8
+ In [4]: class C:
+ pass
+ ...:
+ In [5]: type(C)
+ Out[5]: type
+ A class is a type -- interesting!
-Getter and Setters?
--------------------
+ It is created when the statement is run -- much like ``def``
-But what if you need to add behavior later?
+Python Classes
+--------------
-.. rst-class:: build
+About the simplest class you can write
-* do some calculation
-* check data validity
-* keep things in sync
+.. code-block:: python
+ >>> class Point:
+ ... x = 1
+ ... y = 2
+ >>> Point
+
+ >>> Point.x
+ 1
+ >>> p = Point()
+ >>> p
+ <__main__.Point instance at 0x2de918>
+ >>> p.x
+ 1
.. nextslide::
-.. code-block:: ipython
+Basic Structure of a real class:
- In [5]: class C(object):
- ...: def __init__(self):
- ...: self.x = 5
- ...: def get_x(self):
- ...: return self.x
- ...: def set_x(self, x):
- ...: self.x = x
- ...:
- In [6]: c = C()
- In [7]: c.get_x()
- Out[7]: 5
- In [8]: c.set_x(8)
- In [9]: c.get_x()
- Out[9]: 8
+.. code-block:: python
+ class Point:
+ # everything defined in here is in the class namespace
- This is ugly and verbose -- `Java`_?
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
-.. _Java: http://dirtsimple.org/2004/12/python-is-not-java.html
+ ## create an instance of the class
+ p = Point(3,4)
+
+ ## access the attributes
+ print("p.x is:", p.x)
+ print("p.y is:", p.y)
+
+
+see: ``Examples/Session07/simple_classes.py``
+
+.. nextslide::
+
+The Initializer
+
+The ``__init__`` special method is called when a new instance of a class is created.
+
+You can use it to do any set-up you need
+
+.. code-block:: python
+
+ class Point(object):
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
-properties
------------
-.. code-block:: ipython
+It gets the arguments passed when you call the class object:
- class C(object):
- _x = None
- @property
- def x(self):
- return self._x
- @x.setter
- def x(self, value):
- self._x = value
+.. code-block:: python
+
+ Point(x, y)
+
+.. nextslide::
+
+
+What is this ``self`` thing?
+
+The instance of the class is passed as the first parameter for every method.
+
+"``self``" is only a convention -- but you DO want to use it.
+
+.. code-block:: python
+
+ class Point:
+ def a_function(self, x, y):
+ ...
- In [28]: c = C()
- In [30]: c.x = 5
- In [31]: print c.x
- 5
-Now the interface is like simple attribute access!
+Does this look familiar from C-style procedural programming?
+
.. nextslide::
-What's up with the "@" symbols?
+Anything assigned to a ``self`` attribute is kept in the instance
+name space
-Those are "decorations" it's a syntax for wrapping functions up with something special.
+-- ``self`` *is* the instance.
-We'll cover that in detail in a couple weeks, but for now -- just copy the syntax.
+That's where all the instance-specific data is.
.. code-block:: python
- @property
- def x(self):
+ class Point(object):
+ size = 4
+ color= "red"
+ def __init__(self, x, y):
+ self.x = x
+ self.y = y
+
+.. nextslide::
+
+Anything assigned in the class scope is a class attribute -- every
+instance of the class shares the same one.
+
+Note: the methods defined by ``def`` are class attributes as well.
-means: make a property called x with this as the "getter".
+The class is one namespace, the instance is another.
.. code-block:: python
- @x.setter
- def x(self, value):
+ class Point:
+ size = 4
+ color= "red"
+ ...
+ def get_color():
+ return self.color
+ >>> p3.get_color()
+ 'red'
-means: make the "setter" of the 'x' property this new function
-.. nextslide:: "Read Only" Attributes
+class attributes are accessed with ``self`` also.
-You do not need to define a setter. If you don't, you get a "read only" attribute:
-.. code-block:: ipython
+.. nextslide::
- In [11]: class D(object):
- ....: def __init__(self, x=5):
- ....: self._x = 5
- ....: @property
- ....: def getx(self):
- ....: """I am read only"""
- ....: return self._x
- ....:
- In [12]: d = D()
- In [13]: d.x
- Out[13]: 5
- In [14]: d.x = 6
- ---------------------------------------------------------------------------
- AttributeError Traceback (most recent call last)
- in ()
- ----> 1 d.x = 6
- AttributeError: can't set attribute
+Typical methods:
-deleters
----------
+.. code-block:: python
+
+ class Circle:
+ color = "red"
-If you want to do something special when a property is deleted, you can define
-a deleter is well:
+ def __init__(self, diameter):
+ self.diameter = diameter
-.. code-block:: ipython
+ def grow(self, factor=2):
+ self.diameter = self.diameter * factor
- In [11]: class D(object):
- ....: def __init__(self, x=5):
- ....: self._x = 5
- ....: @property
- ....: def x(self):
- ....: return self._x
- ....: @x.deleter
- ....: def x(self):
- ....: del self._x
-If you leave this out, the property can't be deleted, which is usually
-what you want.
+Methods take some parameters, manipulate the attributes in ``self``.
-.. rst-class:: centered
+They may or may not return something useful.
-[demo: :download:`properties_example.py <../../Examples/Session07/properties_example.py>`]
+.. nextslide::
+Gotcha!
+
+.. code-block:: python
+
+ ...
+ def grow(self, factor=2):
+ self.diameter = self.diameter * factor
+ ...
+ In [205]: C = Circle(5)
+ In [206]: C.grow(2,3)
+
+ TypeError: grow() takes at most 2 arguments (3 given)
+
+Huh???? I only gave 2
+
+``self`` is implicitly passed in for you by python.
+
+(demo of bound vs. unbound methods)
LAB
----
+.. rst-class:: medium
+
+ We now know enough to do something useful.
+
+Let's say you need to render some html...
+
+The goal is to build a set of classes that render an html
+page.
+
+We'll start with a single class, then add some sub-classes
+to specialize the behavior
+
+Details in:
-Let's use some of this to build a nice class to represent a Circle.
+:ref:`exercise_html_renderer`
-For now, Let's do steps 1-4 of:
+Let's get a start with step 1. in class.
+
+I'll give you a few minutes to think about it -- then we'll get started as a group.
-:ref:`exercise_circle_class`
Lightning Talks
----------------
.. rst-class:: medium
+ |
+ | Charles E Robisons
+ |
+ | Paul Vosper
+ |
+
+=======================
+Subclassing/Inheritance
+=======================
+
+Inheritance
+-----------
+
+In object-oriented programming (OOP), inheritance is a way to reuse code
+of existing objects, or to establish a subtype from an existing object.
+
+Objects are defined by classes, classes can inherit attributes and behavior
+from pre-existing classes called base classes or super classes.
+
+The resulting classes are known as derived classes or subclasses.
+
+(http://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29)
+
+Subclassing
+-----------
+
+A subclass "inherits" all the attributes (methods, etc) of the parent class.
+
+You can then change ("override") some or all of the attributes to change the behavior.
+
+You can also add new attributes to extend the behavior.
+
+The simplest subclass in Python:
+
+.. code-block:: python
+
+ class A_subclass(The_superclass):
+ pass
+
+``A_subclass`` now has exactly the same behavior as ``The_superclass``
+
+Overriding attributes
+---------------------
+
+Overriding is as simple as creating a new attribute with the same name:
+
+.. code-block:: python
+
+ class Circle:
+ color = "red"
+
+ ...
+
+ class NewCircle(Circle):
+ color = "blue"
+ >>> nc = NewCircle
+ >>> print(nc.color)
+ blue
+
+
+all the ``self`` instances will have the new attribute.
+
+Overriding methods
+------------------
+
+Same thing, but with methods (remember, a method *is* an attribute in python)
+
+.. code-block:: python
+
+ class Circle:
+ ...
+ def grow(self, factor=2):
+ """grows the circle's diameter by factor"""
+ self.diameter = self.diameter * factor
+ ...
+
+ class NewCircle(Circle):
+ ...
+ def grow(self, factor=2):
+ """grows the area by factor..."""
+ self.diameter = self.diameter * math.sqrt(2)
+
+
+all the instances will have the new method
+
+.. nextslide::
+
+Here's a program design suggestion:
+
+"""
+
+Whenever you override a method, the interface of the new method should be the same as the old. It should take the same parameters, return the same type, and obey the same preconditions and postconditions.
+
+If you obey this rule, you will find that any function designed to work with an instance of a superclass, like a Deck, will also work with instances of subclasses like a Hand or PokerHand. If you violate this rule, your code will collapse like (sorry) a house of cards.
+
+"""
+
|
-| Andrew P Klock
-|
-| Vinay Gupta
+| [ThinkPython 18.10]
|
+| ( Demo of class vs. instance attributes )
-========================
-Static and Class Methods
-========================
-
-.. rst-class:: left build
-.. container::
+===================
+More on Subclassing
+===================
- You've seen how methods of a class are *bound* to an instance when it is
- created.
+Overriding \_\_init\_\_
+-----------------------
- And you've seen how the argument ``self`` is then automatically passed to
- the method when it is called.
+``__init__`` common method to override
- And you've seen how you can call *unbound* methods on a class object so
- long as you pass an instance of that class as the first argument.
+You often need to call the super class ``__init__`` as well
- .. rst-class:: centered
+.. code-block:: python
- **But what if you don't want or need an instance?**
+ class Circle:
+ color = "red"
+ def __init__(self, diameter):
+ self.diameter = diameter
+ ...
+ class CircleR(Circle):
+ def __init__(self, radius):
+ diameter = radius*2
+ Circle.__init__(self, diameter)
-Static Methods
---------------
+exception to: "don't change the method signature" rule.
-A *static method* is a method that doesn't get self:
+More subclassing
+----------------
+You can also call the superclass' other methods:
-.. code-block:: ipython
+.. code-block:: python
- In [36]: class StaticAdder(object):
+ class Circle:
+ ...
+ def get_area(self, diameter):
+ return math.pi * (diameter/2.0)**2
- ....: @staticmethod
- ....: def add(a, b):
- ....: return a + b
- ....:
- In [37]: StaticAdder.add(3, 6)
- Out[37]: 9
+ class CircleR2(Circle):
+ ...
+ def get_area(self):
+ return Circle.get_area(self, self.radius*2)
-.. rst-class:: centered
+There is nothing special about ``__init__`` except that it gets called
+automatically when you instantiate an instance.
-[demo: :download:`static_method.py <../../Examples/Session07/static_method.py>`]
+When to Subclass
+----------------
-.. nextslide:: Why?
+"Is a" relationship: Subclass/inheritance
-.. rst-class:: build
-.. container::
+"Has a" relationship: Composition
- Where are static methods useful?
+.. nextslide::
- Usually they aren't
+"**Is** a" vs "**Has** a"**
- 99% of the time, it's better just to write a module-level function
+You may have a class that needs to accumulate an arbitrary number of objects.
- An example from the Standard Library (tarfile.py):
+A list can do that -- so should you subclass list?
- .. code-block:: python
+Ask yourself:
- class TarInfo(object):
- # ...
- @staticmethod
- def _create_payload(payload):
- """Return the string payload filled with zero bytes
- up to the next 512 byte border.
- """
- blocks, remainder = divmod(len(payload), BLOCKSIZE)
- if remainder > 0:
- payload += (BLOCKSIZE - remainder) * NUL
- return payload
+-- **Is** your class a list (with some extra functionality)?
+or
-Class Methods
--------------
+-- Does you class **have** a list?
-A class method gets the class object, rather than an instance, as the first
-argument
+You only want to subclass list if your class could be used anywhere a list can be used.
-.. code-block:: ipython
- In [41]: class Classy(object):
- ....: x = 2
- ....: @classmethod
- ....: def a_class_method(cls, y):
- ....: print "in a class method: ", cls
- ....: return y ** cls.x
- ....:
- In [42]: Classy.a_class_method(4)
- in a class method:
- Out[42]: 16
+Attribute resolution order
+--------------------------
-.. rst-class:: centered
+When you access an attribute:
-[demo: :download:`class_method.py <../../Examples/Session07/class_method.py>`]
+``an_instance.something``
+Python looks for it in this order:
-.. nextslide:: Why?
+ * Is it an instance attribute ?
+ * Is it a class attribute ?
+ * Is it a superclass attribute ?
+ * Is it a super-superclass attribute ?
+ * ...
-.. rst-class:: build
-.. container::
- Unlike static methods, class methods are quite common.
+It can get more complicated...
- They have the advantage of being friendly to subclassing.
+https://www.python.org/download/releases/2.3/mro/
- Consider this:
+http://python-history.blogspot.com/2010/06/method-resolution-order.html
- .. code-block:: ipython
- In [44]: class SubClassy(Classy):
- ....: x = 3
- ....:
+What are Python classes, really?
+--------------------------------
- In [45]: SubClassy.a_class_method(4)
- in a class method:
- Out[45]: 64
+Putting aside the OO theory...
-.. nextslide:: Alternate Constructors
+Python classes are:
-Because of this friendliness to subclassing, class methods are often used to
-build alternate constructors.
+ * Namespaces
-Consider the case of wanting to build a dictionary with a given iterable of
-keys:
+ * One for the class object
+ * One for each instance
-.. code-block:: ipython
+ * Attribute resolution order
+ * Auto tacking-on of ``self`` when methods are called
- In [57]: d = dict([1,2,3])
- ---------------------------------------------------------------------------
- TypeError Traceback (most recent call last)
- in ()
- ----> 1 d = dict([1,2,3])
- TypeError: cannot convert dictionary update sequence element #0 to a sequence
+That's about it -- really!
-.. nextslide:: ``dict.fromkeys()``
+Type-Based dispatch
+-------------------
-The stock constructor for a dictionary won't work this way. So the dict object
-implements an alternate constructor that *can*.
+You'll see code that looks like this:
.. code-block:: python
- @classmethod
- def fromkeys(cls, iterable, value=None):
- '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
- If not specified, the value defaults to None.
- '''
- self = cls()
- for key in iterable:
- self[key] = value
- return self
+ if isinstance(other, A_Class):
+ Do_something_with_other
+ else:
+ Do_something_else
-(this is actually from the OrderedDict implementation in ``collections.py``)
-See also datetime.datetime.now(), etc....
+Usually better to use "duck typing" (polymorphism)
-.. nextslide:: Curious?
+But when it's called for:
-Properties, Static Methods and Class Methods are powerful features of Pythons
-OO model.
+ * ``isinstance()``
+ * ``issubclass()``
-They are implemented using an underlying structure called *descriptors*
+.. nextslide::
-`Here is a low level look`_ at how the descriptor protocol works.
+GvR: "Five Minute Multi- methods in Python":
-The cool part is that this mechanism is available to you, the programmer, as
-well.
+http://www.artima.com/weblogs/viewpost.jsp?thread=101605
-.. _Here is a low level look: https://docs.python.org/2/howto/descriptor.html
+https://www.python.org/download/releases/2.3/mro/
+http://python-history.blogspot.com/2010/06/method-resolution-order.html
-Extra Credit: use a class method to make an alternate constructor that takes
-the diameter instead.
-===============
-Special Methods
-===============
+Wrap Up
+-------
-.. rst-class:: left
-.. container::
+Thinking OO in Python:
- Special methods (also called *magic* methods) are the secret sauce to Python's
- Duck typing.
+Think about what makes sense for your code:
- Defining the appropriate special methods in your classes is how you make your
- class act like standard classes.
+* Code re-use
+* Clean APIs
+* ...
-What's in a Name?
------------------
+Don't be a slave to what OO is *supposed* to look like.
-We've seen at least one special method so far::
+Let OO work for you, not *create* work for you
- __init__
+.. nextslide::
-It's all in the double underscores...
+OO in Python:
-Pronounced "dunder" (or "under-under")
+The Art of Subclassing: *Raymond Hettinger*
-try: ``dir(2)`` or ``dir(list)``
+http://pyvideo.org/video/879/the-art-of-subclassing
-.. nextslide:: Generally Useful Special Methods
+"classes are for code re-use -- not creating taxonomies"
-Most classes should at lest have these special methods:
+Stop Writing Classes: *Jack Diederich*
-``object.__str__``:
- Called by the str() built-in function and by the print statement to compute
- the *informal* string representation of an object.
+http://pyvideo.org/video/880/stop-writing-classes
-``object.__unicode__``:
- Called by the unicode() built-in function. This converts an object to an
- *informal* unicode representation.
+"If your class has only two methods -- and one of them is ``__init__``
+-- you don't need a class"
- (more on Unicode later....)
-``object.__repr__``:
- Called by the repr() built-in function and by string conversions (reverse
- quotes) to compute the *official* string representation of an object.
+===
+LAB
+===
- (ideally: ``eval( repr(something) ) == something``)
+.. rst-class:: left medium
+ * html renderer: let's see how much more we can do!
-Protocols
-----------
+.. rst-class:: left
-.. rst-class:: build
-.. container::
+ :ref:`exercise_html_renderer`
- The set of special methods needed to emulate a particular type of Python object
- is called a *protocol*.
+ Now we have a base class, and we can:
- Your classes can "become" like Python built-in classes by implementing the
- methods in a given protocol.
+ * Subclass overriding class attributes
+ * Subclass overriding a method
+ * Subclass overriding the ``__init__``
- Remember, these are more *guidelines* than laws. Implement what you need.
+ These are the core OO approaches
-.. nextslide:: The Numerics Protocol
+===================
+More on Subclassing
+===================
-Do you want your class to behave like a number? Implement these methods:
+.. rst-class:: left
+
+ This is a great talk (yes, I'm repeating):
+
+ The Art of Subclassing: *Raymond Hettinger*
+
+ http://pyvideo.org/video/879/the-art-of-subclassing
+
+ If you haven't watched it, It's well worth your time
+
+What's a Subclass For?
+----------------------
+
+The most salient points from that video are as follows:
+
+* **Subclassing is not for Specialization**
+
+* **Subclassing is for Reusing Code**
+
+* **Bear in mind that the subclass is in charge**
+
+
+Multiple Inheritance
+--------------------
+
+Multiple inheritance: Inheriting from more than one class
+
+Simply provide more than one parent.
.. code-block:: python
- object.__add__(self, other)
- object.__sub__(self, other)
- object.__mul__(self, other)
- object.__floordiv__(self, other)
- object.__mod__(self, other)
- object.__divmod__(self, other)
- object.__pow__(self, other[, modulo])
- object.__lshift__(self, other)
- object.__rshift__(self, other)
- object.__and__(self, other)
- object.__xor__(self, other)
- object.__or__(self, other)
+ class Combined(Super1, Super2, Super3):
+ def __init__(self, something, something else):
+ # some custom initialization here.
+ Super1.__init__(self, ......)
+ Super2.__init__(self, ......)
+ Super3.__init__(self, ......)
+ # possibly more custom initialization
-.. nextslide:: The Container Protocol
+(calls to the super class ``__init__`` are optional -- case dependent)
-Want to make a container type? Here's what you need:
+.. nextslide:: Method Resolution Order
.. code-block:: python
- object.__len__(self)
- object.__getitem__(self, key)
- object.__setitem__(self, key, value)
- object.__delitem__(self, key)
- object.__iter__(self)
- object.__reversed__(self)
- object.__contains__(self, item)
- object.__getslice__(self, i, j)
- object.__setslice__(self, i, j, sequence)
- object.__delslice__(self, i, j)
+ class Combined(Super1, Super2, Super3)
+Attributes are located bottom-to-top, left-to-right
-.. nextslide:: An Example
+* Is it an instance attribute ?
+* Is it a class attribute ?
+* Is it a superclass attribute ?
-Each of these methods supports a common Python operation.
+ - Is it an attribute of the left-most superclass?
+ - Is it an attribute of the next superclass?
+ - and so on up the hierarchy...
-For example, to make '+' work with a sequence type in a vector-like fashion,
-implement ``__add__``:
+* Is it a super-superclass attribute ?
+* ... also left to right ...
-.. code-block:: python
+http://python-history.blogspot.com/2010/06/method-resolution-order.html
- def __add__(self, v):
- """return the element-wise vector sum of self and v
- """
- assert len(self) == len(v)
- return vector([x1 + x2 for x1, x2 in zip(self, v)])
+.. nextslide:: Mix-ins
-.. rst-class:: centered
+So why would you want to do this? One reason: *mixins*
-[a more complete example may be seen :download:`here <./supplements/vector.py>`]
+Provides an subset of expected functionality in a re-usable package.
+Huh? this is why --
+Hierarchies are not always simple:
-.. nextslide:: Summary
+* Animal
-Use special methods when you want your class to act like a "standard" class in
-some way.
+ * Mammal
-Look up the special methods you need and define them.
+ * GiveBirth()
-There's more to read about the details of implementing these methods:
+ * Bird
-* https://docs.python.org/2/reference/datamodel.html#special-method-names
-* http://www.rafekettler.com/magicmethods.html
+ * LayEggs()
+Where do you put a Platypus?
-Lightning Talks
-----------------
+Real World Example: `FloatCanvas`_
+
+.. _FloatCanvas: https://github.com/wxWidgets/wxPython/blob/master/wx/lib/floatcanvas/FloatCanvas.py#L485
+
+
+``super()``
+-----------
+
+``super()``: use it to call a superclass method, rather than explicitly calling
+the unbound method on the superclass.
+
+instead of:
+
+.. code-block:: python
+
+ class A(B):
+ def __init__(self, *args, **kwargs)
+ B.__init__(self, *argw, **kwargs)
+ ...
+
+You can do:
+
+.. code-block:: python
+
+ class A(B):
+ def __init__(self, *args, **kwargs)
+ super().__init__(*argw, **kwargs)
+ ...
+
+.. nextslide:: Caveats
+
+Caution: There are some subtle differences with multiple inheritance.
+
+You can use explicit calling to ensure that the 'right' method is called.
.. rst-class:: medium
-|
-| Ousmane Conde
-|
-| Salim Hassan Hamed
-|
+ **Background**
-LAB
-----
+Two seminal articles about ``super()``:
-Let's complete our nifty Circle class:
+"Super Considered Harmful" -- James Knight
-Steps 5-8 of:
+https://fuhm.net/super-harmful/
-:ref:`exercise_circle_class`
+"super() considered super!" -- Raymond Hettinger
+http://rhettinger.wordpress.com/2011/05/26/super-considered-super/
+
+(Both worth reading....)
========
Homework
========
-Complete the Circle class
+Complete your html renderer.
+
+Watch these videos:
+
+Python class toolkit: *Raymond Hettinger* -- https://youtu.be/HTLu2DFOdTg
+
+https://speakerdeck.com/pyconslides/pythons-class-development-toolkit-by-raymond-hettinger
+
+The Art of Subclassing: *Raymond Hettinger* -- http://pyvideo.org/video/879/the-art-of-subclassing
+
+Stop Writing Classes: *Jack Diederich* -- http://pyvideo.org/video/880/stop-writing-classes
-Decide what you are going to do for your proejct, and send me a simple proposal.
+Read up on super()
diff --git a/slides_sources/source/session08.rst b/slides_sources/source/session08.rst
index f1a9624c..609a26e1 100644
--- a/slides_sources/source/session08.rst
+++ b/slides_sources/source/session08.rst
@@ -1,10 +1,8 @@
-******************************************************
-Session Eight: Callable classes, Iterators, Generators
-******************************************************
+.. include:: include.rst
-.. rst-class:: large centered
-
-The tools of Pythonicity
+****************************************************
+Session Eight: More OO: Properties, Special methods.
+****************************************************
================
@@ -14,613 +12,923 @@ Review/Questions
Review of Previous Class
------------------------
-* Advanced OO Concepts
-
- * Properties
- * Special Methods
+* Basic OO Concepts
-Homework review
----------------
+ * Classes
+ * class vs. instance attributes
+ * subclassing
+ * overriding methods / attributes
-* Circle Class
-* Writing Tests using the ``pytest`` module
Lightning Talks Today:
-----------------------
.. rst-class:: medium
-Alireza Hashemloo
+ Paul Briant
-Arielle R Simmons
+ Jay Raina
-Eric W Westman
+ Josh Hicks
-Ryan J Albright
-=========================
-Emulating Standard types
-=========================
+Personal Project
+-----------------
-.. rst-class:: medium
+The bulk of the homework for the rest of the class will be a personal project:
- Making your classes behave like the built-ins
+* It can be for fun, or something you need for your job.
+* It should be large enough to take a few weeks homework time to do.
+* **It should demostrate that you can do something useful with python.**
+* It should follow PEP8 (https://www.python.org/dev/peps/pep-0008)
+* It should have unit tests!
+* Ideally, it will be in version control (gitHub)
+* I don't require any specific python features (i.e. classes): use
+ what is appropriate for your project
+* Due the Sunday after the last class (December 11)
-Callable classes
------------------
+|
+| By next week, send me a project proposal: short and sweet.
+|
-We've been using functions a lot:
+Homework review
+---------------
+
+* html renderer
+* Test-driven development
+
+Homework Notes:
+---------------
+
+``**kwargs`` will always define a ``kwargs`` dict: it just may be empty.
+
+And there is no need to check if it's empty before trying to loop through it.
.. code-block:: python
- def my_fun(something):
- do_something
- ...
- return something
+ if self.attributes != {}:
+ for key, value in self.attributes.items():
+ self.atts += ' {}="{}"'.format(key, value)
-And then we can call it:
+no need for ``!= {}`` -- an empty dict is "Falsey"
+
+**but** no need for that check at all. If the dict (or list, or tuple) is
+empty, then the loop is a do-nothing operation:
.. code-block:: python
- result = my_fun(some_arguments)
+ for key, value in self.attributes.items():
+ self.atts += ' {}="{}"'.format(key, value)
-.. nextslide::
+will not run if self.attributes is an empty dict.
-But what if we need to store some data to know how to evaluate that function?
-Example: a function that computes a quadratic function:
+Dynamic typing and class attributes
+-----------------------------------
-.. math::
+* what happens if we change a class attribute after creating instances??
- y = a x^2 + bx + c
+ - let's try ``Element.indent`` ...
-You could pass in a, b and c each time:
+* setting an instance attribute overwrites class attributes:
-.. code-block:: python
+``self.tag =`` overrights the class attribute (sort of!)
- def quadratic(x, a, b, c):
- return a * x**2 + b * x + c
+Let's experiment with that.
-But what if you are using the same a, b, and c numerous times?
-Or what if you need to pass this in to something
-(like map) that requires a function that takes a single argument?
+dict as switch
+--------------
-"Callables"
------------
+.. rst-class:: medium
-Various places in python expect a "callable" -- something that you can
-call like a function:
+ What to use instead of "switch-case"?
+
+A number of languages have a "switch-case" construct::
+
+ switch(argument) {
+ case 0:
+ return "zero";
+ case 1:
+ return "one";
+ case 2:
+ return "two";
+ default:
+ return "nothing";
+ };
+
+How do you spell this in python?
+
+``if-elif`` chains
+-------------------
+
+The obvious way to spell it is a chain of ``elif`` statements:
.. code-block:: python
- a_result = something(some_arguments)
+ if argument == 0:
+ return "zero"
+ elif argument == 1:
+ return "one"
+ elif argument == 2:
+ return "two"
+ else:
+ return "nothing"
-"something" in this case is often a function, but can be anything else
-that is "callable".
+And there is nothing wrong with that, but....
-What have we been introduced to recently that is "callable", but not a
-function object?
+.. nextslide::
-Custom callable objects
-------------------------
+The ``elif`` chain is neither elegant nor efficient.
-The trick is one of Python's "magic methods"
+There are a number of ways to spell it in python -- one elegant one is to use a dict:
.. code-block:: python
- __call__(*args, **kwargs)
+ arg_dict = {0:"zero", 1:"one", 2: "two"}
+ dict.get(argument, "nothing")
-If you define a ``__call__`` method in your class, it will be used when
-code "calls" an instance of your class:
+Simple, elegant, and fast.
-.. code-block:: python
+You can do a dispatch table by putting functions as the value.
- class Callable(object):
- def __init__(self, .....)
- some_initilization
- def __call__(self, some_parameters)
+Example: Chris' mailroom2 solution.
-Then you can do:
+Polymorphism as switch:
+-----------------------
-.. code-block:: python
+It turns out that a lot of uses of switch-case in non-OO languages is to
+change behaviour depending on teh type of object being worked on::
- callable_instance = Callable(some_arguments)
+ switch(object.tag) {
+ case 'html':
+ render_html_element;
+ case 'p':
+ render_p_element;
+ ...
- result = callable_instance(some_arguments)
+I saw some of this in the html renderer:
+.. nextslide::
-Writing your own sequence type
--------------------------------
+.. code-block:: python
-Python has a handful of nifty sequence types built in:
+ def render(out_file, ind=""):
+ ....
+ if self.tag == 'html':
+ tag = ""
+ end_tag = ""
+ elif self.tag == 'p':
+ tag = "
"
+ end_tag = "
"
- * lists
- * tuples
- * strings
- * ...
+This will work, of course, but:
-But what if you need a sequence that isn't built in?
+* it means you need to know every tag that you might render when you write this render method.
-A Sparse array
---------------
+* In a more complex system, you will need to go update all sorts of things all over teh place when you add a tag.
-Example: Sparse Array
+* It means anyone extending the system with more tags needs to edit the core base class.
-Sometimes we have data sets that are "sparse" -- i.e. most of the values are zero.
+Polymorphism
+------------
-So you may not want to store a huge bunch of zeros.
+The alternative is to use polymorphism:
-But you do want the array to look like a regular old sequence.
+Your ``render()`` method doesn't need to know what all the objects are
+that it may need to render.
-So how do you do that?
+All it needs to know is that they all will have a method
+that does the right thing.
-The Sequence protocol
-----------------------
+So the above becomes, simply:
-You can make your class look like a regular python sequence by defining
-the set of special methods you need:
+.. code-block:: python
-https://docs.python.org/2/reference/datamodel.html#emulating-container-types
+ def render(out_file, ind=""):
+ ....
+ tag, end_tag = self.make_tags()
-and
+This is known as polymorphism, because many different objects are behave
+the same way.
-http://www.rafekettler.com/magicmethods.html#sequence
+.. nextslide::
-The key ones are:
+This is usally handled by subclassing, so they all get all teh same
+methods by default, and you only need to specialize the ones that need it.
-+-------------------+-----------------------+
-| ``__len__`` | for ``len(sequence)`` |
-+-------------------+-----------------------+
-| ``__getitem__`` | for ``x = seq[i]`` |
-+-------------------+-----------------------+
-| ``__setitem__`` | for ``seq[i] = x`` |
-+-------------------+-----------------------+
-| ``__delitem__`` | for ``del seq[i]`` |
-+-------------------+-----------------------+
-| ``__contains__`` | for ``x in seq`` |
-+-------------------+-----------------------+
+But in Python -- it can be done with duck-typing instead, as the TextWrapper example.
-====
-LAB
-====
+Duck typing and EAFP
+--------------------
-.. rst-class:: medium
+* notes on Duck Typing: :ref:`exercise_html_renderer`
- Let's do the previous motivating examples.
+* put the ``except`` as close as you can to where you expect an exception to be raised!
-Callables:
+* Let's look at a couple ways to do that.
+
+Code Review
-----------
-Write a class for a quadratic equation.
+* anyone stuck that wants to work through your code?
-* The initializer for that class should take the parameters: ``a, b, c``
+* And/or go through mine...
-* It should store those parameters as attributes.
+Lightning Talks:
+----------------
-* The resulting instance should evaluate the function when called, and return the result:
+.. rst-class:: medium
+ |
+ | Paul Briant
+ |
+ | Jay Raina
+ |
+ | Josh Hicks
-.. code-block:: python
- my_quad = Quadratic(a=2, b=3, c=1)
- my_quad(0)
+==========
+Properties
+==========
-Sparse Array:
--------------
+.. rst-class:: left
+.. container::
+
+ One of the strengths of Python is lack of clutter.
-Write a class for a sparse array
+ Attributes are simple and concise:
-* Internally, it can store the values in a dict, with the index as the keys)
+ .. code-block:: ipython
-* It should take a sequence of values as an initializer
+ In [5]: class C:
+ def __init__(self):
+ self.x = 5
+ In [6]: c = C()
+ In [7]: c.x
+ Out[7]: 5
+ In [8]: c.x = 8
+ In [9]: c.x
+ Out[9]: 8
-* you should be able to tell how long it is: ``len(my_array)``
-* It should support getting and setting particular elements via indexing.
+Getter and Setters?
+-------------------
+
+But what if you need to add behavior later?
-* It should support deleting an element by index.
+.. rst-class:: build
-* It should raise an ``IndexError`` if you try to access an index beyond the end.
+* do some calculation
+* check data validity
+* keep things in sync
-* Can you make it support slicing?
-* How else can you make it like a list?
+.. nextslide::
.. code-block:: ipython
- In [10]: my_array = SparseArray( (1,0,0,0,2,0,0,0,5) )
- In [11]: my_array[4]
- Out[11]: 2
- In [12]: my_array[2]
- Out[12]: 0
+ In [5]: class C:
+ ...: def __init__(self):
+ ...: self.x = 5
+ ...: def get_x(self):
+ ...: return self.x
+ ...: def set_x(self, x):
+ ...: self.x = x
+ ...:
+ In [6]: c = C()
+ In [7]: c.get_x()
+ Out[7]: 5
+ In [8]: c.set_x(8)
+ In [9]: c.get_x()
+ Out[9]: 8
-Lightning Talks
-----------------
-.. rst-class:: medium
+ This is ugly and verbose -- `Java`_?
-|
-| Alireza Hashemloo
-|
-| Arielle R Simmons
-|
+.. _Java: http://dirtsimple.org/2004/12/python-is-not-java.html
-=========================
-Iterators and Generators
-=========================
+properties
+-----------
-.. rst-class:: medium
+.. code-block:: ipython
- What goes on in those for loops?
+ class C:
+ _x = None
+ @property
+ def x(self):
+ return self._x
+ @x.setter
+ def x(self, value):
+ self._x = value
-Iterators
----------
+ In [28]: c = C()
+ In [30]: c.x = 5
+ In [31]: print(c.x)
+ 5
-Iterators are one of the main reasons Python code is so readable:
+Now the interface is like simple attribute access!
+
+.. nextslide::
+
+What's up with the "@" symbols?
+
+Those are "decorations" it's a syntax for wrapping functions up with something special.
+
+We'll cover that in detail in a couple weeks, but for now -- just copy the syntax.
.. code-block:: python
- for x in just_about_anything:
- do_stuff(x)
+ @property
+ def x(self):
-It does not have to be a "sequence": list, tuple, etc.
+means: make a property called x with this as the "getter".
-Rather: you can loop through anything that satisfies the "iterator protocol"
+.. code-block:: python
-http://docs.python.org/library/stdtypes.html#iterator-types
+ @x.setter
+ def x(self, value):
-The Iterator Protocol
+means: make the "setter" of the 'x' property this new function
+
+"Read Only" Attributes
----------------------
-An iterator must have the following methods:
+You do not need to define a setter. If you don't, you get a "read only" attribute:
-.. code-block:: python
+.. code-block:: ipython
- an_iterator.__iter__()
+ In [11]: class D():
+ ....: def __init__(self, x=5):
+ ....: self._x = 5
+ ....: @property
+ ....: def getx(self):
+ ....: """I am read only"""
+ ....: return self._x
+ ....:
+ In [12]: d = D()
+ In [13]: d.x
+ Out[13]: 5
+ In [14]: d.x = 6
+ ---------------------------------------------------------------------------
+ AttributeError Traceback (most recent call last)
+ in ()
+ ----> 1 d.x = 6
+ AttributeError: can't set attribute
+
+Deleters
+---------
-Returns the iterator object itself. This is required to allow both containers
-and iterators to be used with the ``for`` and ``in`` statements.
+If you want to do something special when a property is deleted, you can define
+a deleter is well:
-.. code-block:: python
+.. code-block:: ipython
- an_iterator.next()
+ In [11]: class D():
+ ....: def __init__(self, x=5):
+ ....: self._x = 5
+ ....: @property
+ ....: def x(self):
+ ....: return self._x
+ ....: @x.deleter
+ ....: def x(self):
+ ....: del self._x
-Returns the next item from the container. If there are no further items,
-raises the ``StopIteration`` exception.
+If you leave this out, the property can't be deleted, which is usually
+what you want.
-List as an Iterator:
---------------------
+.. rst-class:: centered
+
+[demo: :download:`properties_example.py <../../Examples/Session08/properties_example.py>`]
+
+
+===
+LAB
+===
+
+Let's use some of this to build a nice class to represent a Circle.
+
+For now, Let's do steps 1-4 of:
+
+:ref:`exercise_circle_class`
+
+
+========================
+Static and Class Methods
+========================
+
+.. rst-class:: left build
+.. container::
+
+ You've seen how methods of a class are *bound* to an instance when it is
+ created.
+
+ And you've seen how the argument ``self`` is then automatically passed to
+ the method when it is called.
+
+ And you've seen how you can call *unbound* methods on a class object so
+ long as you pass an instance of that class as the first argument.
+
+
+ .. rst-class:: centered
+
+ **But what if you don't want or need an instance?**
+
+
+Static Methods
+--------------
+
+A *static method* is a method that doesn't get self:
.. code-block:: ipython
- In [10]: a_list = [1,2,3]
+ In [36]: class StaticAdder:
- In [11]: list_iter = a_list.__iter__()
+ ....: @staticmethod
+ ....: def add(a, b):
+ ....: return a + b
+ ....:
- In [12]: list_iter.next()
- Out[12]: 1
+ In [37]: StaticAdder.add(3, 6)
+ Out[37]: 9
- In [13]: list_iter.next()
- Out[13]: 2
+.. rst-class:: centered
- In [14]: list_iter.next()
- Out[14]: 3
+[demo: :download:`static_method.py <../../Examples/Session08/static_method.py>`]
- In [15]: list_iter.next()
- --------------------------------------------------
- StopIteration Traceback (most recent call last)
- in ()
- ----> 1 list_iter.next()
- StopIteration:
-Making an Iterator
--------------------
+.. nextslide:: Why?
-A simple version of ``xrange()``
+.. rst-class:: build
+.. container::
-.. code-block:: python
+ Where are static methods useful?
- class IterateMe_1(object):
- def __init__(self, stop=5):
- self.current = 0
- self.stop = stop
- def __iter__(self):
- return self
- def next(self):
- if self.current < self.stop:
- self.current += 1
- return self.current
- else:
- raise StopIteration
-
-(demo: :download:`iterator_1.py <../../Examples/Session08/iterator_1.py>`)
-
-``iter()``
------------
+ Usually they aren't
+
+ 99% of the time, it's better just to write a module-level function
+
+ An example from the Standard Library (tarfile.py):
-How do you get the iterator object (the thing with the next() method) from an "iterable"?
+ .. code-block:: python
-The ``iter()`` function:
+ class TarInfo:
+ # ...
+ @staticmethod
+ def _create_payload(payload):
+ """Return the string payload filled with zero bytes
+ up to the next 512 byte border.
+ """
+ blocks, remainder = divmod(len(payload), BLOCKSIZE)
+ if remainder > 0:
+ payload += (BLOCKSIZE - remainder) * NUL
+ return payload
+
+
+Class Methods
+-------------
+
+A class method gets the class object, rather than an instance, as the first
+argument
.. code-block:: ipython
- In [20]: iter([2,3,4])
- Out[20]:
+ In [41]: class Classy:
+ ....: x = 2
+ ....: @classmethod
+ ....: def a_class_method(cls, y):
+ ....: print("in a class method: ", cls)
+ ....: return y ** cls.x
+ ....:
+ In [42]: Classy.a_class_method(4)
+ in a class method:
+ Out[42]: 16
+
+.. rst-class:: centered
- In [21]: iter("a string")
- Out[21]:
+[demo: :download:`class_method.py <../../Examples/Session08/class_method.py>`]
- In [22]: iter( ('a', 'tuple') )
- Out[22]:
-for an arbitrary object, ``iter()`` calls the ``__iter__`` method. But it knows about some objects (``str``, for instance) that don't have a ``__iter__`` method.
+Why?
+----
+.. rst-class:: build
+.. container::
-What does ``for`` do?
-----------------------
+ Unlike static methods, class methods are quite common.
+
+ They have the advantage of being friendly to subclassing.
-Now that we know the iterator protocol, we can write something like a for loop:
+ Consider this:
+ .. code-block:: ipython
-:download:`my_for.py <../../Examples/Session08/my_for.py>`
+ In [44]: class SubClassy(Classy):
+ ....: x = 3
+ ....:
+
+ In [45]: SubClassy.a_class_method(4)
+ in a class method:
+ Out[45]: 64
+
+Alternate Constructors
+-----------------------
+
+Because of this friendliness to subclassing, class methods are often used to
+build alternate constructors.
+
+Consider the case of wanting to build a dictionary with a given iterable of
+keys:
+
+.. code-block:: ipython
+
+ In [57]: d = dict([1,2,3])
+ ---------------------------------------------------------------------------
+ TypeError Traceback (most recent call last)
+ in ()
+ ----> 1 d = dict([1,2,3])
+
+ TypeError: cannot convert dictionary update sequence element #0 to a sequence
+
+
+.. nextslide:: ``dict.fromkeys()``
+
+The stock constructor for a dictionary won't work this way. So the dict object
+implements an alternate constructor that *can*.
.. code-block:: python
- def my_for(an_iterable, func):
- """
- Emulation of a for loop.
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S.
+ If not specified, the value defaults to None.
+ '''
+ self = cls()
+ for key in iterable:
+ self[key] = value
+ return self
- func() will be called with each item in an_iterable
- """
- # equiv of "for i in l:"
- iterator = iter(an_iterable)
- while True:
- try:
- i = iterator.next()
- except StopIteration:
- break
- func(i)
+(this is actually from the OrderedDict implementation in ``collections.py``)
+See also datetime.datetime.now(), etc....
-Itertools
----------
+.. nextslide:: Curious?
-``itertools`` is a collection of utilities that make it easy to
-build an iterator that iterates over sequences in various common ways
+Properties, Static Methods and Class Methods are powerful features of Python's
+OO model.
-http://docs.python.org/library/itertools.html
+They are implemented using an underlying structure called *descriptors*
-NOTE:
+`Here is a low level look`_ at how the descriptor protocol works.
-iterators are not *only* for ``for``
+The cool part is that this mechanism is available to you, the programmer, as
+well.
-They can be used with anything that expects an iterator:
+.. _Here is a low level look: https://docs.python.org/2/howto/descriptor.html
-``sum``, ``tuple``, ``sorted``, and ``list``
-For example.
+For the Circle Lab: use a class method to make an alternate constructor that takes
+the diameter instead.
-LAB
------
+===============
+Special Methods
+===============
+
+.. rst-class:: left
+.. container::
-In the ``Examples/session08`` dir, you will find:
-:download:`iterator_1.py <../../Examples/Session08/iterator_1.py>`
+ Special methods (also called *magic* methods) are the secret sauce to Python's Duck typing.
-* Extend (``iterator_1.py`` ) to be more like ``xrange()`` -- add three input parameters: ``iterator_2(start, stop, step=1)``
+ Defining the appropriate special methods in your classes is how you make your class act like standard classes.
-* See what happens if you break out in the middle of the loop:
+What's in a Name?
+-----------------
+
+We've seen at least one special method so far::
+
+ __init__
+
+It's all in the double underscores...
+
+Pronounced "dunder" (or "under-under")
+
+try: ``dir(2)`` or ``dir(list)``
+
+.. nextslide:: Generally Useful Special Methods
+
+Most classes should at least have these special methods:
+
+``object.__str__``:
+ Called by the str() built-in function and by the print function to compute
+ the *informal* string representation of an object.
+
+``object.__repr__``:
+ Called by the repr() built-in function to compute the *official* string representation of an object.
+
+ (ideally: ``eval( repr(something) ) == something``)
+
+(demo)
+
+Protocols
+----------
+
+.. rst-class:: build
+.. container::
+
+ The set of special methods needed to emulate a particular type of Python object is called a *protocol*.
+
+ Your classes can "become" like Python built-in classes by implementing the
+ methods in a given protocol.
+
+ Remember, these are more *guidelines* than laws. Implement what you need.
+
+
+.. nextslide:: The Numerics Protocol
+
+Do you want your class to behave like a number? Implement these methods:
.. code-block:: python
- it = IterateMe_2(2, 20, 2)
- for i in it:
- if i > 10: break
- print i
+ object.__add__(self, other)
+ object.__sub__(self, other)
+ object.__mul__(self, other)
+ object.__floordiv__(self, other)
+ object.__mod__(self, other)
+ object.__divmod__(self, other)
+ object.__pow__(self, other[, modulo])
+ object.__lshift__(self, other)
+ object.__rshift__(self, other)
+ object.__and__(self, other)
+ object.__xor__(self, other)
+ object.__or__(self, other)
-And then pick up again:
+.. nextslide:: The Container Protocol
+
+Want to make a container type? Here's what you need:
.. code-block:: python
- for i in it:
- print i
+ object.__len__(self)
+ object.__getitem__(self, key)
+ object.__setitem__(self, key, value)
+ object.__delitem__(self, key)
+ object.__iter__(self)
+ object.__reversed__(self)
+ object.__contains__(self, item)
+ object.__getslice__(self, i, j)
+ object.__setslice__(self, i, j, sequence)
+ object.__delslice__(self, i, j)
-* Does ``xrange()`` behave the same?
- - make yours match ``xrange()``
+.. nextslide:: An Example
-LAB2
------
+Each of these methods supports a common Python operation.
-Make the SparseArray class from the previous lab an iterator, so you can do:
+For example, to make '+' work with a sequence type in a vector-like fashion,
+implement ``__add__``:
.. code-block:: python
- for i in my_sparse_array:
- do_something_with(i)
+ def __add__(self, v):
+ """return the element-wise vector sum of self and v
+ """
+ assert len(self) == len(v)
+ return vector([x1 + x2 for x1, x2 in zip(self, v)])
+.. rst-class:: centered
-Lightning Talks
-----------------
+A more complete example may be seen in:
-.. rst-class:: medium
+Examples/Session08/vector.py
-|
-| Eric W Westman
-|
-| Ryan J Albright
-|
+or: :download:`here <../../Examples/Session08/vector.py>`
+.. nextslide:: Summary
+Use special methods when you want your class to act like a "standard" class in
+some way.
-Generators
-----------
+Look up the special methods you need and define them.
-Generators give you the iterator immediately:
+There's more to read about the details of implementing these methods:
-* no access to the underlying data ... if it even exists
+* https://docs.python.org/3.5/reference/datamodel.html#special-method-names
+* https://github.com/RafeKettler/magicmethods/blob/master/magicmethods.markdown
+===
+LAB
+===
-Conceptually:
- Iterators are about various ways to loop over data, generators generate the data on the fly.
+Let's complete our nifty Circle class:
-Practically:
- You can use either one either way (and a generator is one type of iterator)
+Steps 5-8 of:
- Generators do some of the book-keeping for you -- simpler syntax.
+:ref:`exercise_circle_class`
-yield
-------
-``yield`` is a way to make a quickie generator with a function:
+=========================
+Emulating Standard types
+=========================
-.. code-block:: python
+.. rst-class:: medium
- def a_generator_function(params):
- some_stuff
- yield something
+ Making your classes behave like the built-ins
-Generator functions "yield" a value, rather than returning a value.
-State is preserved in between yields.
+Callable classes
+-----------------
+We've been using functions a lot:
-.. nextslide:: generator functions
+.. code-block:: python
-A function with ``yield`` in it is a "factory" for a generator
+ def my_fun(something):
+ do_something
+ ...
+ return something
-Each time you call it, you get a new generator:
+And then we can call it:
.. code-block:: python
- gen_a = a_generator()
- gen_b = a_generator()
+ result = my_fun(some_arguments)
+
+.. nextslide::
-Each instance keeps its own state.
+But what if we need to store some data to know how to evaluate that function?
-Really just a shorthand for an iterator class that does the book keeping for you.
+Example: a function that computes a quadratic function:
-.. nextslide::
+.. math::
-An example: like ``xrange()``
+ y = a x^2 + bx + c
+
+You could pass in a, b and c each time:
.. code-block:: python
- def y_xrange(start, stop, step=1):
- i = start
- while i < stop:
- yield i
- i += step
+ def quadratic(x, a, b, c):
+ return a * x**2 + b * x + c
-Real World Example from FloatCanvas:
+But what if you are using the same a, b, and c numerous times?
-https://github.com/svn2github/wxPython/blob/master/3rdParty/FloatCanvas/floatcanvas/FloatCanvas.py#L100
+Or what if you need to pass this in to something
+(like map) that requires a function that takes a single argument?
+"Callables"
+-----------
-.. nextslide::
+Various places in python expect a "callable" -- something that you can
+call like a function:
-Note:
+.. code-block:: python
-.. code-block:: ipython
+ a_result = something(some_arguments)
- In [164]: gen = y_xrange(2,6)
- In [165]: type(gen)
- Out[165]: generator
- In [166]: dir(gen)
- Out[166]:
- ...
- '__iter__',
- ...
- 'next',
+"something" in this case is often a function, but can be anything else
+that is "callable".
+What have we been introduced to recently that is "callable", but not a
+function object?
-So the generator **is** an iterator
+Custom callable objects
+------------------------
-Note: A generator function can also be a method in a class
+The trick is one of Python's "magic methods"
+.. code-block:: python
-.. More about iterators and generators:
+ __call__(*args, **kwargs)
-.. http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/
+If you define a ``__call__`` method in your class, it will be used when
+code "calls" an instance of your class:
-:download:`yield_example.py <../../Examples/Session08/yield_example.py>`
+.. code-block:: python
-generator comprehension
------------------------
+ class Callable:
+ def __init__(self, .....)
+ some_initilization
+ def __call__(self, some_parameters)
-yet another way to make a generator:
+Then you can do:
.. code-block:: python
- >>> [x * 2 for x in [1, 2, 3]]
- [2, 4, 6]
- >>> (x * 2 for x in [1, 2, 3])
- at 0x10911bf50>
- >>> for n in (x * 2 for x in [1, 2, 3]):
- ... print n
- ... 2 4 6
+ callable_instance = Callable(some_arguments)
+ result = callable_instance(some_arguments)
-More interesting if [1, 2, 3] is also a generator
-LAB
-----
+Writing your own sequence type
+-------------------------------
-Write a few generators:
+Python has a handful of nifty sequence types built in:
-* Sum of integers
-* Doubler
-* Fibonacci sequence
-* Prime numbers
+ * lists
+ * tuples
+ * strings
+ * ...
-(test code in
-:download:`test_generator.py <../../Examples/Session08/test_generator.py>`)
+But what if you need a sequence that isn't built in?
-Descriptions:
+A Sparse array
+--------------
-Sum of the integers:
- keep adding the next integer
+Example: Sparse Array
- 0 + 1 + 2 + 3 + 4 + 5 + ...
+Sometimes we have data sets that are "sparse" -- i.e. most of the values are zero.
- so the sequence is:
+So you may not want to store a huge bunch of zeros.
- 0, 1, 3, 6, 10, 15 .....
+But you do want the array to look like a regular old sequence.
-.. nextslide::
+So how do you do that?
+
+The Sequence protocol
+----------------------
+
+You can make your class look like a regular python sequence by defining
+the set of special methods you need:
+
+https://docs.python.org/3/reference/datamodel.html#emulating-container-types
+
+and
+
+https://github.com/RafeKettler/magicmethods/blob/master/magicmethods.markdown#sequence
+
+The key ones are:
+
++-------------------+-----------------------+
+| ``__len__`` | for ``len(sequence)`` |
++-------------------+-----------------------+
+| ``__getitem__`` | for ``x = seq[i]`` |
++-------------------+-----------------------+
+| ``__setitem__`` | for ``seq[i] = x`` |
++-------------------+-----------------------+
+| ``__delitem__`` | for ``del seq[i]`` |
++-------------------+-----------------------+
+| ``__contains__`` | for ``x in seq`` |
++-------------------+-----------------------+
+
+====
+LAB
+====
+
+.. rst-class:: medium
+
+ Let's do the previous motivating examples.
-Doubler:
- Each value is double the previous value:
+Callables:
+-----------
- 1, 2, 4, 8, 16, 32,
+Write a class for a quadratic equation.
-Fibonacci sequence:
- The fibonacci sequence as a generator:
+* The initializer for that class should take the parameters: ``a, b, c``
- f(n) = f(n-1) + f(n-2)
+* It should store those parameters as attributes.
- 1, 1, 2, 3, 5, 8, 13, 21, 34...
+* The resulting instance should evaluate the function when called, and return the result:
-Prime numbers:
- Generate the prime numbers (numbers only divisible by them self and 1):
- 2, 3, 5, 7, 11, 13, 17, 19, 23...
+.. code-block:: python
-Others to try:
- Try x^2, x^3, counting by threes, x^e, counting by minus seven, ...
+ my_quad = Quadratic(a=2, b=3, c=1)
+
+ my_quad(0)
+
+Sparse Array:
+-------------
+
+Write a class for a sparse array:
+
+:ref:`exercise_sparse_array`
========
Homework
========
-.. rst-class:: left medium
+.. rst-class:: left
+
+ Reading:
+
+ Lambda:
+
+ http://www.blog.pythonlibrary.org/2015/10/28/python-101-lambda-basics/
+
+ https://pythonconquerstheuniverse.wordpress.com/2011/08/29/lambda_tutorial/
+
+ Complete the Circle class
+
+ Complete the Sparse Array class
+
+ Decide what you are going to do for your project, and send me a simple proposal. Get started if you can.
- Finish up the Labs from class
+ Good book:
- Get started on your project!
+ Python 3 Object Oriented Programming: *Dusty Phillips*
- (Send me a proposal if you haven't already)
+ (Dusty is a local boy and co-founder of PuPPy)
diff --git a/slides_sources/source/session09.rst b/slides_sources/source/session09.rst
index 6f2fc56d..e6d953d7 100644
--- a/slides_sources/source/session09.rst
+++ b/slides_sources/source/session09.rst
@@ -1,1274 +1,656 @@
+.. include:: include.rst
-.. Foundations 2: Python slides file, created by
- hieroglyph-quickstart on Wed Apr 2 18:42:06 2014.
+************************************************************
+Anonymous Functions and Iterators, Iterables, and Generators
+************************************************************
-
-******************************************************************
-Session Nine: Decorators, Context Managers, Packages and packaging
-******************************************************************
-
-======================
-Lightning Talks Today:
-======================
+====================
+Lightning Talks Now:
+====================
.. rst-class:: medium
- Lou Ascoli
-
- Ralph Brand
-
- Danielle Marcos
-
- Carolyn Evans
-
-================
-Review/Questions
-================
-
-Review of complete sparse array class
-
-==========
-Decorators
-==========
-
-**A Short Reminder**
-
-.. rst-class:: left
-.. container::
-
- Functions are things that generate values based on input (arguments).
-
- In Python, functions are first-class objects.
-
- This means that you can bind symbols to them, pass them around, just like
- other objects.
-
- Because of this fact, you can write functions that take functions as
- arguments and/or return functions as values:
-
- .. code-block:: python
+ Jack M Hefner
- def substitute(a_function):
- def new_function(*args, **kwargs):
- return "I'm not that other function"
- return new_function
+ Ninad Naik
-A Definition
-------------
-
-There are many things you can do with a simple pattern like this one.
-So many, that we give it a special name:
-
-.. rst-class:: centered medium
-
-**Decorator**
-
-.. rst-class:: build centered
-.. container::
-
- "A decorator is a function that takes a function as an argument and
- returns a function as a return value.""
-
- That's nice and all, but why is that useful?
-
-An Example
-----------
-
-Imagine you are trying to debug a module with a number of functions like this
-one:
-
-.. code-block:: python
-
- def add(a, b):
- return a + b
-
-.. rst-class:: build
-.. container::
-
- You want to see when each function is called, with what arguments and
- with what result. So you rewrite each function as follows:
-
- .. code-block:: python
-
- def add(a, b):
- print "Function 'add' called with args: %r, %r"%(a, b)
- result = a + b
- print "\tResult --> %r" % result
- return result
-
-.. nextslide::
+ Simbarashe P Change
-That's not particularly nice, especially if you have lots of functions
-in your module.
-Now imagine we defined the following, more generic *decorator*:
+===================
+Anonymous functions
+===================
-.. code-block:: python
-
- def logged_func(func):
- def logged(*args, **kwargs):
- print "Function %r called" % func.__name__
- if args:
- print "\twith args: %r" % args
- if kwargs:
- print "\twith kwargs: %r" % kwargs
- result = func(*args, **kwargs)
- print "\t Result --> %r" % result
- return result
- return logged
-
-.. nextslide::
-
-We could then make logging versions of our module functions:
-
-.. code-block:: python
-
- logging_add = logged_func(add)
-
-Then, where we want to see the results, we can use the logged version:
+lambda
+------
.. code-block:: ipython
- In [37]: logging_add(3, 4)
- Function 'add' called
- with args: (3, 4)
- Result --> 7
- Out[37]: 7
+ In [171]: f = lambda x, y: x+y
+ In [172]: f(2,3)
+ Out[172]: 5
-.. rst-class:: build
-.. container::
+Content of function can only be an expression -- not a statement
- This is nice, but we have to call the new function wherever we originally
- had the old one.
+Anyone remember what the difference is?
- It'd be nicer if we could just call the old function and have it log.
+Called "Anonymous": it doesn't get a name.
.. nextslide::
-Remembering that you can easily rebind symbols in Python using *assignment
-statements* leads you to this form:
-
-.. code-block:: python
-
- def logged_func(func):
- # implemented above
-
- def add(a, b):
- return a + b
- add = logged_func(add)
-
-.. rst-class:: build
-.. container::
-
- And now you can simply use the code you've already written and calls to
- ``add`` will be logged:
-
- .. code-block:: ipython
-
- In [41]: add(3, 4)
- Function 'add' called
- with args: (3, 4)
- Result --> 7
- Out[41]: 7
+It's a python object, it can be stored in a list or other container
-Syntax
-------
-
-Rebinding the name of a function to the result of calling a decorator on that
-function is called **decoration**.
-
-Because this is so common, Python provides a special operator to perform it
-more *declaratively*: the ``@`` operator:
-
-(I told you I'd eventually explain what was going on under the hood
-with that wierd `@` symbol)
-
-.. code-block:: python
-
- # this is the imperative version:
- def add(a, b):
- return a + b
- add = logged_func(add)
-
- # and this declarative form is exactly equal:
- @logged_func
- def add(a, b):
- return a + b
-
-.. rst-class:: build
-.. container::
-
- The declarative form (called a decorator expression) is far more common,
- but both have the identical result, and can be used interchangeably.
-
-Callables
----------
-
-Our original definition of a *decorator* was nice and simple, but a tiny bit
-incomplete.
-
-In reality, decorators can be used with anything that is *callable*.
-
-Remember from last week, a *callable* is a function, a method on a class,
-or a class that implements the ``__call__`` special method.
-
-So in fact the definition should be updated as follows:
+.. code-block:: ipython
-.. rst-class:: centered
+ In [7]: l = [lambda x, y: x+y]
+ In [8]: type(l[0])
+ Out[8]: function
-A decorator is a callable that takes a callable as an argument and
-returns a callable as a return value.
-An Example
-----------
+And you can call it:
-Consider a decorator that would save the results of calling an expensive
-function with given arguments:
+.. code-block:: ipython
-.. code-block:: python
+ In [9]: l[0](3,4)
+ Out[9]: 7
- class Memoize:
- """
- memoize decorator from avinash.vora
- http://avinashv.net/2008/04/python-decorators-syntactic-sugar/
- """
- def __init__(self, function): # runs when memoize class is called
- self.function = function
- self.memoized = {}
-
- def __call__(self, *args): # runs when memoize instance is called
- try:
- return self.memoized[args]
- except KeyError:
- self.memoized[args] = self.function(*args)
- return self.memoized[args]
-.. nextslide::
+Functions as first class objects
+---------------------------------
-Let's try that out with a potentially expensive function:
+You can do that with "regular" functions too:
.. code-block:: ipython
- In [56]: @Memoize
- ....: def sum2x(n):
- ....: return sum(2 * i for i in xrange(n))
+ In [12]: def fun(x,y):
+ ....: return x+y
....:
+ In [13]: l = [fun]
+ In [14]: type(l[0])
+ Out[14]: function
+ In [15]: l[0](3,4)
+ Out[15]: 7
- In [57]: sum2x(10000000)
- Out[57]: 99999990000000
-
- In [58]: sum2x(10000000)
- Out[58]: 99999990000000
-
-It's nice to see that in action, but what if we want to know *exactly* how much
-difference it made?
-Nested Decorators
------------------
-You can stack decorator expressions. The result is like calling each decorator
-in order, from bottom to top:
-
-.. code-block:: python
+======================
+Functional Programming
+======================
- @decorator_two
- @decorator_one
- def func(x):
- pass
+No real consensus about what that means.
- # is exactly equal to:
- def func(x):
- pass
- func = decorator_two(decorator_one(func))
+But there are some "classic" methods available in Python.
-.. nextslide::
+map
+---
-Let's define another decorator that will time how long a given call takes:
+``map`` "maps" a function onto a sequence of objects -- It applies the function to each item in the list, returning another list
-.. code-block:: python
- import time
- def timed_func(func):
- def timed(*args, **kwargs):
- start = time.time()
- result = func(*args, **kwargs)
- elapsed = time.time() - start
- print "time expired: %s" % elapsed
- return result
- return timed
+.. code-block:: ipython
-.. nextslide::
+ In [23]: l = [2, 5, 7, 12, 6, 4]
+ In [24]: def fun(x):
+ return x*2 + 10
+ In [25]: map(fun, l)
+ Out[25]: