From 8eb225e37bcb12dfa5c936afb9a04d780c460125 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Fri, 16 Oct 2015 20:30:00 -0700 Subject: [PATCH 001/129] updated session3 -- started --- slides_sources/source/session03.rst | 121 ++++++++++++++-------------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst index 9d0ed8fc..c876a65f 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -28,8 +28,6 @@ Homework Review * FizzBuzz -* Ackerman - * Series .. rst-class:: center large @@ -41,20 +39,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 + Eric Rosko - Lesley D Reece + Michael Waddle - Michel Claessens + Robert Alford Sequences @@ -86,16 +82,16 @@ 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 +* bytes * bytearrays * buffers * array.arrays -* xrange objects (almost) +* range objects (almost) For this class, you won't see much beyond the string types, lists, tuples -- the rest are pretty special purpose. @@ -215,7 +211,7 @@ 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? @@ -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 @@ -397,11 +392,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 @@ -445,7 +440,9 @@ Iteration .. rst-class:: center large -More on this in a while. + All sequences are "iterables" -- + + More on this in a while. LAB ==== @@ -468,10 +465,10 @@ Lightning Talks ---------------- | -| James Brent Nunn +| Eric Rosko | | -| Lauren Fries +| Michael Waddle | @@ -507,6 +504,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 +568,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 @@ -613,7 +610,7 @@ multiple names (or no name) .. rst-class:: center large -So Why Have Both? + So Why Have Both? Mutability @@ -642,6 +639,7 @@ Objects which are mutable may be *changed in place*. Objects which are immutable may not be changed. +Ever. .. nextslide:: The Types We Know @@ -760,7 +758,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 +788,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 +934,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 +942,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 +959,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 +979,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 +1007,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 +1033,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 +1041,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 +1061,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 +1074,8 @@ tuples are mixed types: -- Kind of like simple C structs. -.. nextslide:: Other Considerations +Other Considerations +-------------------- .. rst-class:: build @@ -1108,7 +1105,7 @@ 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....) From 1f5ca42d7ccd7c81c6e2bd7c4f5d0c280da5cedd Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sun, 18 Oct 2015 10:42:30 -0700 Subject: [PATCH 002/129] updated git instructions with new repo name --- slides_sources/source/session02.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/slides_sources/source/session02.rst b/slides_sources/source/session02.rst index 703f5a95..fa307627 100644 --- a/slides_sources/source/session02.rst +++ b/slides_sources/source/session02.rst @@ -132,8 +132,8 @@ 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/IntroPython2015.git (fetch) + origin https://github.com/UWPCE-PythonCert/IntroPython2015.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) @@ -267,13 +267,13 @@ When you add a *remote* (existing git repository), it creates a directory with t .. 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:: @@ -325,7 +325,7 @@ Then, fetch the upstream master branch and merge it into your master: .. code-block:: bash $ git fetch upstream master - From https://github.com/UWPCE-PythonCert/IntroToPython + From https://github.com/UWPCE-PythonCert/IntroPython2015 * branch master -> FETCH_HEAD $ git merge upstream/master From 05cf129c752451054281101ddec754740a8b7440 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sun, 18 Oct 2015 16:25:01 -0700 Subject: [PATCH 003/129] some more toward session 3 --- slides_sources/source/exercises/fizz_buzz.rst | 26 +++++- .../source/exercises/grid_printer.rst | 87 +++++++++++++++++-- slides_sources/source/exercises/index.rst | 11 ++- slides_sources/source/session03.rst | 72 ++------------- 4 files changed, 121 insertions(+), 75 deletions(-) diff --git a/slides_sources/source/exercises/fizz_buzz.rst b/slides_sources/source/exercises/fizz_buzz.rst index 63409276..33f0091c 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: ----- @@ -42,4 +42,28 @@ Hint: * Do try to write it without looking it up -- there are a million nifty solutions posted on the web. +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..22619198 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 three columns. + +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/index.rst b/slides_sources/source/exercises/index.rst index d047a2b2..6286bc51 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -1,5 +1,9 @@ -Homework Materials -====================== +========== +Excercises +========== + +Contents: +========= .. toctree:: :maxdepth: 1 @@ -7,6 +11,9 @@ Homework Materials grid_printer fizz_buzz fib_and_lucas + + list_lab + kata_fourteen html_renderer circle_class diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst index c876a65f..be7acfa5 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -1115,79 +1115,17 @@ LAB List Lab --------- -List Lab (after http://www.upriss.org.uk/python/session5.html) +Let's list a bit about Python lists... -In your student folder, create a new file called ``list_lab.py``. +:ref: `exercise_list_lab` -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) - -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. - -Lightning Talks ----------------- +Lightning Talk +--------------- | -| Lesley D Reece -| -| -| Michel Claessens +| Robert Alford | From 03ac9f344211bf66ff8be7de35ad73b81098e3de Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 20 Oct 2015 00:00:11 -0700 Subject: [PATCH 004/129] updates session 3 with new formatting methods --- slides_sources/source/exercises/index.rst | 2 + slides_sources/source/exercises/list_lab.rst | 105 ++++++ slides_sources/source/exercises/mailroom.rst | 91 +++++ slides_sources/source/exercises/rot13.rst | 48 +++ .../source/exercises/string_formatting.rst | 48 +++ slides_sources/source/session03.rst | 340 +++++++----------- 6 files changed, 419 insertions(+), 215 deletions(-) create mode 100644 slides_sources/source/exercises/list_lab.rst create mode 100644 slides_sources/source/exercises/mailroom.rst create mode 100644 slides_sources/source/exercises/rot13.rst create mode 100644 slides_sources/source/exercises/string_formatting.rst diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index 6286bc51..f2bbee02 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -13,6 +13,8 @@ Contents: fib_and_lucas list_lab + string_formatting + rot13 kata_fourteen html_renderer diff --git a/slides_sources/source/exercises/list_lab.rst b/slides_sources/source/exercises/list_lab.rst new file mode 100644 index 00000000..cb5aaae2 --- /dev/null +++ b/slides_sources/source/exercises/list_lab.rst @@ -0,0 +1,105 @@ +.. _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 ``session02`` 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) + +.. 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.rst b/slides_sources/source/exercises/mailroom.rst new file mode 100644 index 00000000..077349b7 --- /dev/null +++ b/slides_sources/source/exercises/mailroom.rst @@ -0,0 +1,91 @@ +.. _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 yo'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 2 actions: + 'Send a Thank You' or 'Create a Report'. + +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. + * 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.** + +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 + +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. 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/string_formatting.rst b/slides_sources/source/exercises/string_formatting.rst new file mode 100644 index 00000000..84261f1d --- /dev/null +++ b/slides_sources/source/exercises/string_formatting.rst @@ -0,0 +1,48 @@ +.. _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.github.io/2012/10/10/python-string-format/ + + +A couple Exercises +------------------ + +* Write a format string that will take: + + ``( 2, 123.4567, 10000)`` + + and produce: + + ``'file_002 : 123.46, 1e+04'`` + +* Rewrite: ``"the first 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 first 3 numbers are: {:d}, {:d}, {:d}".format(*t) + Out[53]: 'the first 3 numbers are: 1, 2, 3' diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst index be7acfa5..180570a2 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -602,7 +602,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 @@ -1115,7 +1115,7 @@ LAB List Lab --------- -Let's list a bit about Python lists... +Let's play a bit with Python lists... :ref: `exercise_list_lab` @@ -1145,7 +1145,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 @@ -1162,7 +1162,7 @@ Contrast this with other languages, where you must build and use an ``index``: .. code-block:: javascript - for(var i=0; i 50: .....: break .....: @@ -1240,11 +1256,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. @@ -1256,14 +1272,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! @@ -1295,7 +1311,7 @@ potential error -- infinite loops: i = 0; while i < 5: - print i + print(i) .. nextslide:: Terminating a while Loop @@ -1308,7 +1324,7 @@ Use ``break``: .....: i += 1 .....: if i > 10: .....: break - .....: print i + .....: print(i) .....: 1 2 3 4 5 6 7 8 9 10 @@ -1322,7 +1338,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 .....: @@ -1336,7 +1352,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 @@ -1354,7 +1370,7 @@ loop terminates normally (no ``break``) String Features -================= +================ .. rst-class:: center large @@ -1385,6 +1401,13 @@ You can also use ``str()`` (demo) +String Methods +=============== + +String objects have a lot of methods. + +Here are just a few: + String Manipulations --------------------- @@ -1400,7 +1423,8 @@ String Manipulations Out[170]: 'comma|separated|values' -.. nextslide:: Case Switching +Case Switching +-------------- .. code-block:: ipython @@ -1415,7 +1439,8 @@ String Manipulations Out[175]: 'A Long String Of Words' -.. nextslide:: Testing +Testing +-------- .. code-block:: ipython @@ -1430,6 +1455,7 @@ String Manipulations In [186]: fancy.isalnum() Out[186]: False + String Literals ----------------- @@ -1450,14 +1476,14 @@ for example -- for tab-separted values: 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: @@ -1465,10 +1491,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** @@ -1488,20 +1514,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 @@ -1513,66 +1539,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:: @@ -1580,41 +1628,24 @@ 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 - - +.. _formatting language: https://docs.python.org/3/library/string.html#format-specification-mini-language One Last Trick @@ -1629,10 +1660,11 @@ 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' + In [85]: fred = input('type something-->') + type something-->I've typed something + + In [86]: print(fred) + I've typed something This will display a prompt to the user, allowing them to input text and allowing you to bind that input to a symbol. @@ -1641,21 +1673,9 @@ allowing you to bind that input to a symbol. String Formatting LAB ===================== -.. rst-class:: left - - * Rewrite: ``the first 3 numbers are: %i, %i, %i"%(1,2,3)`` +Let's play with these a bit: - for an arbitrary number of numbers... - - * 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 ======== @@ -1674,39 +1694,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 ------ @@ -1715,79 +1703,14 @@ 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. - - * 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.** +:ref:`exercise_mailroom` -.. 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. - -.. nextslide:: Submission +Reading +------- -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. +Think Python, chapters 11, 13, 14 -When you are done, push your changes and make a pull request. +Learn Python the Hard way: Next Week: =========== @@ -1796,27 +1719,14 @@ Next Week: **Lightning talks next week:** -Benjamin C Mier - -Robert W Perkins +Andrey Gusev -Vinay Gupta +Cheryl Ohashi -Wayne R Fukuhara +Maxwell MacCamy -Homework -======== - -Review and/or finish reading these class notes. -Finish any labs from class.... - -**Reading:** - -Think Python, chapters 11, 13, 14 - -Learn Python the Hard way: From caf02e6f53cefbaa8844d3d568aae26a9877f979 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 20 Oct 2015 00:32:52 -0700 Subject: [PATCH 005/129] pretty much done with session 3 -- still some proofreading to do --- slides_sources/source/exercises/index.rst | 2 ++ slides_sources/source/exercises/slicing.rst | 24 +++++++++++++++++++++ slides_sources/source/session03.rst | 17 +++++---------- 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 slides_sources/source/exercises/slicing.rst diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index f2bbee02..da6e5c01 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -12,9 +12,11 @@ Contents: fizz_buzz fib_and_lucas + slicing list_lab string_formatting rot13 + mailroom kata_fourteen html_renderer diff --git a/slides_sources/source/exercises/slicing.rst b/slides_sources/source/exercises/slicing.rst new file mode 100644 index 00000000..4f1a891f --- /dev/null +++ b/slides_sources/source/exercises/slicing.rst @@ -0,0 +1,24 @@ +.. _exercise_slicing: + +*********** +Slicing Lab +*********** + +Goal +==== + +Get the basics of sequence slicing down + +Tasks +----- + +Write some functions that: + +* 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 + +NOTE: these should work with ANY sequence -- not just strings! + diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst index 180570a2..21066461 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -449,17 +449,8 @@ LAB Slicing Lab -Slicing Lab ------------- -Write some functions that: +:ref:`exercise_slicing` -* 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 - -NOTE: these should work with ANY sequence -- not just strings! Lightning Talks ---------------- @@ -1708,9 +1699,11 @@ Mail Room Reading ------- -Think Python, chapters 11, 13, 14 +Think Python: Chapters 11, 13, 14 + +Learn Python the Hard way: 15-17, 39 -Learn Python the Hard way: +Dive Into Python3: Sections 2.6, 2.7, 11 Next Week: =========== From 42bb3463a44f3307c716159ed6b1bf30b08777a4 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 20 Oct 2015 21:15:25 -0700 Subject: [PATCH 006/129] added slicing examples --- Examples/Session03/slicing_lab.py | 31 +++++++++++++++ slides_sources/source/exercises/index.rst | 6 +-- slides_sources/source/exercises/slicing.rst | 15 ++++---- slides_sources/source/session03.rst | 42 ++++++++++----------- 4 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 Examples/Session03/slicing_lab.py 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/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index da6e5c01..ca489367 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -1,6 +1,6 @@ -========== -Excercises -========== +========= +Exercises +========= Contents: ========= diff --git a/slides_sources/source/exercises/slicing.rst b/slides_sources/source/exercises/slicing.rst index 4f1a891f..61747eb8 100644 --- a/slides_sources/source/exercises/slicing.rst +++ b/slides_sources/source/exercises/slicing.rst @@ -14,11 +14,12 @@ Tasks Write some functions that: -* 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 - -NOTE: these should work with ANY sequence -- not just strings! +* 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/session03.rst b/slides_sources/source/session03.rst index 21066461..6fd8e757 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -8,12 +8,9 @@ Review/Questions Review of Previous Session -------------------------- -.. rst-class:: build - * Functions - recursion - - optional arguments * Booleans @@ -84,24 +81,24 @@ Sequence Types There are eight builtin types in Python that are *sequences*: -* strings -* lists -* tuples +* string +* list +* tuple * bytes -* bytearrays -* buffers -* array.arrays -* range objects (almost) +* 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. @@ -155,7 +152,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: @@ -357,8 +354,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 @@ -438,18 +434,20 @@ This does not raise an error if the item you seek is not present: Iteration --------- -.. rst-class:: center large +.. rst-class:: center mlarge All sequences are "iterables" -- More on this in a while. -LAB -==== +Slicing LAB +=========== + +.. rst-class:: center medium -Slicing Lab + Let's practice Slicing! -:ref:`exercise_slicing` + :ref:`exercise_slicing` Lightning Talks @@ -468,7 +466,7 @@ Lists, Tuples... .. rst-class:: center large -The *other* sequence types. +The *primary* sequence types. Lists ----- From 88c7a69542411876c231a688d958e3ec20d0ecc8 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 20 Oct 2015 21:19:28 -0700 Subject: [PATCH 007/129] fixed list lab link --- slides_sources/source/session03.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slides_sources/source/session03.rst b/slides_sources/source/session03.rst index 6fd8e757..baa8f531 100644 --- a/slides_sources/source/session03.rst +++ b/slides_sources/source/session03.rst @@ -1106,7 +1106,7 @@ List Lab Let's play a bit with Python lists... -:ref: `exercise_list_lab` +:ref:`exercise_list_lab` From 57ffd5be78cc23074a1414dc754035b9a85e6ccc Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Fri, 23 Oct 2015 23:36:51 -0700 Subject: [PATCH 008/129] updated lightning talks --- slides_sources/source/session04.rst | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/slides_sources/source/session04.rst b/slides_sources/source/session04.rst index 13e6be7d..e8acb52f 100644 --- a/slides_sources/source/session04.rst +++ b/slides_sources/source/session04.rst @@ -36,13 +36,10 @@ Lightning Talks Today: .. rst-class:: mlarge - Benjamin C Mier + Andrey Gusev + Cheryl Ohashi + Maxwell MacCamy - Robert W Perkins - - Lesley D Reece - - Wayne R Fukuhara ============================== @@ -683,10 +680,10 @@ Lightning Talks ---------------- | -| Benjamin C Mier +| Andrey Gusev | | -| Robert W Perkins +| Cheryl Ohashi | ========== @@ -913,14 +910,14 @@ Improving ``raw_input`` * Update your mailroom program to use exceptions (and IBAFP) to handle malformed numeric input -Lightning Talks +Lightning Talk ---------------- | | Lesley D Reece | | -| Wayne R Fukuhara +| Maxwell MacCamy | From edec513ebb6652e19338aa599fae12ed075505ba Mon Sep 17 00:00:00 2001 From: Maria Mckinley Date: Sat, 24 Oct 2015 22:49:33 -0700 Subject: [PATCH 009/129] updated to python3 up to "Dict / Set Lab" --- slides_sources/source/session04.rst | 82 ++++++++++++++++++----------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/slides_sources/source/session04.rst b/slides_sources/source/session04.rst index e8acb52f..f482b521 100644 --- a/slides_sources/source/session04.rst +++ b/slides_sources/source/session04.rst @@ -68,7 +68,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:%i, j:%i"%(i, j)) i:1, j:2 i:3, j:4 @@ -91,7 +91,7 @@ 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:%i, j:%i"%(i, j)) ....: i:1, j:3 i:2, j:4 @@ -116,7 +116,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 %ith item is: %s"%(i, item)) ...: the 0th item is: this the 1th item is: that @@ -321,7 +321,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 -------------------- @@ -333,7 +333,7 @@ Dictionary Iterating In [15]: d = {'name': 'Brian', 'score': 42} In [16]: for x in d: - print x + print(x) ....: score name @@ -349,13 +349,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 @@ -368,7 +368,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 @@ -437,23 +437,32 @@ 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 + +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:: @@ -495,30 +504,39 @@ 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'} + +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 @@ -533,19 +551,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 From f183f992796a22f518f794439b9a65ffe220faaf Mon Sep 17 00:00:00 2001 From: Maria Mckinley Date: Tue, 27 Oct 2015 00:12:52 -0700 Subject: [PATCH 010/129] did some more updating in session04 to python3 --- slides_sources/source/exercises/dict_lab.rst | 89 ++++++++++++ slides_sources/source/session04.rst | 145 ++++++------------- 2 files changed, 136 insertions(+), 98 deletions(-) create mode 100644 slides_sources/source/exercises/dict_lab.rst diff --git a/slides_sources/source/exercises/dict_lab.rst b/slides_sources/source/exercises/dict_lab.rst new file mode 100644 index 00000000..9abcd203 --- /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. + +.. 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/session04.rst b/slides_sources/source/session04.rst index f482b521..feec7c5d 100644 --- a/slides_sources/source/session04.rst +++ b/slides_sources/source/session04.rst @@ -36,9 +36,9 @@ Lightning Talks Today: .. rst-class:: mlarge - Andrey Gusev - Cheryl Ohashi - Maxwell MacCamy + Andrey Gusev + Cheryl Ohashi + Maxwell MacCamy @@ -453,6 +453,8 @@ which is equivalent to, but faster than: this that +.. nextslide:: + but to get values, must specify you want values: .. code-block:: ipython @@ -521,6 +523,9 @@ Assignment maintains link to the original dict In [50]: item_view Out[50]: {'something': 'a value', 'something else': 'another value'} + +.. nextslide:: + Use explicit copy method to get a copy .. code-block:: ipython @@ -636,63 +641,14 @@ 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 ---------------- @@ -720,7 +676,7 @@ Another Branching structure: f = open('missing.txt') process(f) # never called if file missing except IOError: - print "couldn't open missing.txt" + print("couldn't open missing.txt") Exceptions ---------- @@ -770,7 +726,7 @@ So you can do try: num_in = int(num_in) except ValueError: - print "Input must be an integer, try again." + print("Input must be an integer, try again.") Or let the Exception be raised.... @@ -806,7 +762,7 @@ Exceptions -- finally f = open('missing.txt') process(f) # never called if file missing except IOError: - print "couldn't open missing.txt" + print("couldn't open missing.txt") finally: do_some_clean-up @@ -822,7 +778,7 @@ Exceptions -- else do_something() f = open('missing.txt') except IOError: - print "couldn't open missing.txt" + print("couldn't open missing.txt") else: process(f) # only called if there was no exception @@ -839,7 +795,7 @@ Exceptions -- using them do_something() f = open('missing.txt') except IOError as the_error: - print the_error + print(the_error) the_error.extra_info = "some more information" raise @@ -987,8 +943,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: @@ -1004,7 +959,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: @@ -1034,7 +988,7 @@ Common Idioms .. code-block:: python for line in open('secrets.txt'): - print line + print(line) (the file object is an iterator!) @@ -1047,6 +1001,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 ------------ @@ -1056,6 +1022,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 @@ -1071,37 +1042,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...) @@ -1143,10 +1098,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 @@ -1175,16 +1130,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 @@ -1196,7 +1145,7 @@ All the stuff in os.path and more: In [67]: pth.absolute() Out[67]: PosixPath('/Users/Chris/PythonStuff/CodeFellowsClass/sea-f2-python-sept14/Examples/Session04') In [68]: for f in pth.iterdir(): - print f + print(f) junk2.txt junkfile.txt ... @@ -1212,7 +1161,7 @@ Files Lab In the class repo, in: -``Examples\Session01\students.txt`` +``Examples\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. @@ -1225,7 +1174,7 @@ 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/IntroToPython/blob/master/Examples/Session01/students.txt`` +``https://github.com/UWPCE-PythonCert/IntroPython2015/blob/master/Examples/students.txt`` ========= From a4bab820fe8108d307ce20120ad7d9766c333a4b Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sun, 1 Nov 2015 22:06:58 -0800 Subject: [PATCH 011/129] fixed rst in Syllablus --- Syllabus.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Syllabus.rst b/Syllabus.rst index 1d171f87..c1551b24 100644 --- a/Syllabus.rst +++ b/Syllabus.rst @@ -16,7 +16,7 @@ First Course: Introduction to Python ===================================== Tuesdays 6-9 pm: Oct 6 - Dec 8, 2015 (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. @@ -274,10 +274,10 @@ Week 4: Oct 27 Dictionaries, Sets and Mutability. -Exceptions. - Files and Text Processing +Exceptions. + Week 5: November 3 ........................ From ef5df8b58cc350512e024260e3a94daf0d9200db Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sun, 1 Nov 2015 22:45:40 -0800 Subject: [PATCH 012/129] updating session 5 notes --- .../source/exercises/comprehensions_lab.rst | 244 ++++++++++++++++ slides_sources/source/exercises/dict_lab.rst | 4 +- slides_sources/source/exercises/index.rst | 35 +++ slides_sources/source/session04.rst | 45 ++- slides_sources/source/session05.rst | 271 ++---------------- 5 files changed, 323 insertions(+), 276 deletions(-) create mode 100644 slides_sources/source/exercises/comprehensions_lab.rst diff --git a/slides_sources/source/exercises/comprehensions_lab.rst b/slides_sources/source/exercises/comprehensions_lab.rst new file mode 100644 index 00000000..a556e5f2 --- /dev/null +++ b/slides_sources/source/exercises/comprehensions_lab.rst @@ -0,0 +1,244 @@ +.. _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.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) + ??? + +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 +------------------ + +Use test-driven development! + +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 +------------------------------------ + +Let's 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": 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). +(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 three sets + + - loop through that sequence to build the sets up -- so no repeated code. + + 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 index 9abcd203..519e1f10 100644 --- a/slides_sources/source/exercises/dict_lab.rst +++ b/slides_sources/source/exercises/dict_lab.rst @@ -1,8 +1,8 @@ .. _exercise_dict_lab: -******** +********************** Dictionary and Set Lab -******** +********************** Learning about dictionaries and sets ==================================== diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index ca489367..eec1f93a 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -5,6 +5,11 @@ Exercises Contents: ========= +.. rst-class:: left + + +Session 2: +---------- .. toctree:: :maxdepth: 1 @@ -12,12 +17,42 @@ Contents: fizz_buzz fib_and_lucas +Session 3: +---------- +.. toctree:: + :maxdepth: 1 + slicing list_lab string_formatting rot13 mailroom +Session 4: +---------- +.. toctree:: + :maxdepth: 1 + + dict_lab kata_fourteen + +Session 5: +---------- +.. toctree:: + :maxdepth: 1 + + comprehensions_lab + +Session 6: +---------- +.. toctree:: + :maxdepth: 1 + html_renderer + +Session 7: +---------- +.. toctree:: + :maxdepth: 1 + circle_class diff --git a/slides_sources/source/session04.rst b/slides_sources/source/session04.rst index feec7c5d..a86a7a64 100644 --- a/slides_sources/source/session04.rst +++ b/slides_sources/source/session04.rst @@ -37,9 +37,10 @@ Lightning Talks Today: .. rst-class:: mlarge Andrey Gusev + Cheryl Ohashi - Maxwell MacCamy + Maxwell MacCamy ============================== @@ -396,7 +397,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? @@ -650,16 +651,14 @@ Have some fun with dictionaries and sets! :ref:`exercise_dict_lab` -Lightning Talks ----------------- +Lightning Talk +-------------- | -| Andrey Gusev -| -| -| Cheryl Ohashi +| Maxwell MacCamy | + ========== Exceptions ========== @@ -741,7 +740,7 @@ Or let the Exception be raised.... http://www.youtube.com/watch?v=AZDWveIdqjY -(Pycon talk by Alex Martelli) +(PyCon talk by Alex Martelli) .. nextslide:: Do you catch all Exceptions? @@ -873,9 +872,9 @@ Exceptions Lab Exceptions Lab --------------- -Improving ``raw_input`` +Improving ``input`` -* The ``raw_input()`` function can generate two exceptions: ``EOFError`` +* 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`` @@ -884,16 +883,6 @@ Improving ``raw_input`` * Update your mailroom program to use exceptions (and IBAFP) to handle malformed numeric input -Lightning Talk ----------------- - -| -| Lesley D Reece -| -| -| Maxwell MacCamy -| - ======================== File Reading and Writing @@ -1003,7 +992,7 @@ Common Idioms .. nextslide:: -We will learn more about the keyword with later, but for now, just understand +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 @@ -1143,7 +1132,7 @@ 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) junk2.txt @@ -1184,7 +1173,6 @@ Homework Recommended Reading: --------------------- * Dive Into Python: Chapt. 13,14 - * Unicode: http://www.joelonsoftware.com/articles/Unicode.html Assignments: ------------- @@ -1238,11 +1226,14 @@ Paths and File Processing - advanced: make it work for any size file: i.e. don't read the entire contents of the file into memory at once. + - Note that if you want it to do any kind of file, you need to open the files in binary mode: + ``open(filename, 'rb')`` (or ``'wb'`` for writing.) + * 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 + - 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. diff --git a/slides_sources/source/session05.rst b/slides_sources/source/session05.rst index 057183c8..cc45e973 100644 --- a/slides_sources/source/session05.rst +++ b/slides_sources/source/session05.rst @@ -1,11 +1,7 @@ -.. 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: Advanced Argument passing, Testing, Comprehensions +***************************************************************** ====================== Lightning Talks Today: @@ -13,14 +9,11 @@ Lightning Talks Today: .. rst-class:: medium - Darcy Balcarce - - Eric Buer - - Henry B Fischer +Michael Cimino - Kyle R Hart +Pei Lin +Tiffany Ku ================ Review/Questions @@ -59,15 +52,13 @@ Review of Previous Class * I want time to go over it in class. - * So I'm ditching Unicode -- we'll hit it in the last class - 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`` @@ -156,7 +147,7 @@ Can set defaults to variables In [156]: y = 4 In [157]: def fun(x=y): - print "x is:", x + print("x is:", x) .....: In [158]: fun() x is: 4 @@ -170,7 +161,7 @@ Defaults are evaluated when the function is defined In [156]: y = 4 In [157]: def fun(x=y): - print "x is:", x + print("x is:", x) .....: In [158]: fun() x is: 4 @@ -191,7 +182,7 @@ function arguments are really just .. code-block:: python def f(x, y, w=0, h=0): - print "position: %s, %s -- shape: %s, %s"%(x, y, w, h) + print("position: %s, %s -- shape: %s, %s"%(x, y, w, h)) position = (3,4) size = {'h': 10, 'w': 20} @@ -209,8 +200,8 @@ You can also pull the parameters out in the function as a tuple and a dict: .. code-block:: ipython def f(*args, **kwargs): - print "the positional arguments are:", args - print "the keyword arguments are:", 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) @@ -382,7 +373,7 @@ Another "gotcha" is using mutables as default arguments: In [11]: def fun(x, a=[]): ....: a.append(x) - ....: print a + ....: print(a) ....: This makes sense: maybe you'd pass in a list, but the default is an empty list. @@ -414,7 +405,7 @@ The standard practice for such a mutable default argument: ....: if a is None: ....: a = [] ....: a.append(x) - ....: print a + ....: print(a) In [16]: fun(3) [3] In [17]: fun(4) @@ -448,10 +439,10 @@ Lightning Talks .. rst-class:: medium | -| Darcy Balcarce +| Michael Cimino | | -| Eric Buer +| Pei Lin | @@ -586,7 +577,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,18 +620,19 @@ Example LAB === -See the list comps exercises... +List comps exercises: -Lightning Talks +:ref:`exercise_comprehensions` + + + +Lightning Talk ---------------- .. rst-class:: medium | -| Some person -| -| -| Some other person +| Tiffany Ku | @@ -899,221 +891,6 @@ Catch up! * 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:: - -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 three sets - - - loop through that sequence to build the sets up -- so no repeated code. - - 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!) ================================ Material to review for Homework: From a4b13faa7a836ae64921a12e191b2a6f1eddd542 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 3 Nov 2015 17:51:03 -0800 Subject: [PATCH 013/129] updated session 5 --- Examples/Session05/arg_test.py | 3 +- Examples/Session05/codingbat.py | 4 +- Examples/Session05/test_codingbat.py | 2 +- Examples/Session05/test_pytest_parameter.py | 15 +- Examples/Session05/test_random_pytest.py | 11 +- Examples/Session05/test_random_unitest.py | 9 +- Syllabus.rst | 2 + .../source/exercises/exceptions_lab.rst | 25 ++ slides_sources/source/exercises/index.rst | 1 + slides_sources/source/session05.rst | 417 +++++++++++++++--- slides_sources/source/session06.rst | 2 +- 11 files changed, 405 insertions(+), 86 deletions(-) create mode 100644 slides_sources/source/exercises/exceptions_lab.rst 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/Syllabus.rst b/Syllabus.rst index c1551b24..97bcddde 100644 --- a/Syllabus.rst +++ b/Syllabus.rst @@ -282,6 +282,8 @@ Exceptions. Week 5: November 3 ........................ +Exceptions + Advanced Argument passing Testing 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/index.rst b/slides_sources/source/exercises/index.rst index eec1f93a..d7dd1de4 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -41,6 +41,7 @@ Session 5: .. toctree:: :maxdepth: 1 + exceptions_lab comprehensions_lab Session 6: diff --git a/slides_sources/source/session05.rst b/slides_sources/source/session05.rst index cc45e973..68b1232f 100644 --- a/slides_sources/source/session05.rst +++ b/slides_sources/source/session05.rst @@ -9,11 +9,11 @@ Lightning Talks Today: .. rst-class:: medium -Michael Cimino + Michael Cimino -Pei Lin + Pei Lin -Tiffany Ku + Tiffany Ku ================ Review/Questions @@ -23,8 +23,8 @@ Review of Previous Class ------------------------ * Dictionaries - * Exceptions - * Files, etc. + * Sets + * File processing, etc. .. nextslide:: @@ -50,7 +50,7 @@ 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. Homework review @@ -58,23 +58,22 @@ Homework review Homework Questions? -My Solutions to ALL the exercises in the class repo in: +My Solutions to all the exercises in the class repo in: ``Solutions/Session04`` A few tidbits .... -.. nextslide:: Sorting stuff in dictionaries: +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: @@ -83,10 +82,84 @@ Other options: collections.OrderedDict - sorted() +Also other nifty stuff in the ``collections`` module: + +https://docs.python.org/3.5/library/collections.html + + +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. + +Strictly speaking, you only need to follow it for code in the standard library. + +But style matters -- consistent style makes your code easier to read and understand. + +So **follow PEP 8** + +*Exception* -- if you have a company style guide follow that instead. + +try the "pep8" module on your code:: + + $ python3 -m pip install pep8 + $ pep8 my_python_file (demo) +Naming things... +---------------- + +It matters what names you give your variables. + +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. + +So use names that make sense to the reader. + +Naming Guidelines +----------------- + +Only use single-letter names for things with limited scope: indexes and teh like: + +.. code-block:: python + + for i, item in enumerate(a_sequence): + do_something(i, item) + +**Don't** use a name like "item", when there is a meaning to what the item is: + +.. code-block:: python + + for name in all_the_names: + do_something_with(name) + +Use plurals for collections of things: + +.. code-block:: python + + names = ['Fred', 'George', ...] + +.. nextslide:: + +**Do** re-use names when the use is essentially the same, and you don't need the old one: + +.. code-block:: python + + line = line.strip() + line = line.replace(",", " ") + .... + +Here's a nice talk about naming: + +http://pyvideo.org/video/3792/name-things-once-0 + + Code Review ------------ @@ -103,6 +176,221 @@ Anyone look at my solutions? Anything in particular you'd like me to go over? +========== +Exceptions +========== + +Since there wasn't time last class... + +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: + +A number of you already did this -- so do it at home if you haven't + +:ref:`exercise_exceptions_lab` + ========================= Advanced Argument Passing ========================= @@ -115,7 +403,7 @@ When defining a function, you can specify only what you need -- in any order .. code-block:: ipython In [151]: def fun(x,y=0,z=0): - print x,y,z + print(x,y,z) .....: In [152]: fun(1,2,3) 1 2 3 @@ -170,7 +458,6 @@ Defaults are evaluated when the function is defined x is: 4 - Function arguments in variables ------------------------------- @@ -182,12 +469,12 @@ function arguments are really just .. code-block:: python def f(x, y, w=0, h=0): - print("position: %s, %s -- shape: %s, %s"%(x, y, w, h)) + print("position: {}, {} -- shape: {}, {}".format(x, y, w, h)) position = (3,4) size = {'h': 10, 'w': 20} - >>> f( *position, **size) + >>> f(*position, **size) position: 3, 4 -- shape: 20, 10 @@ -215,25 +502,25 @@ Passing a dict to str.format() Now that you know that keyword args are really a dict, you can do this nifty trick: -The ``format`` method takes keyword arguments: +The string ``format()`` method takes keyword arguments: .. code-block:: ipython - In [24]: u"My name is {first} {last}".format(last=u"Barker", first=u"Chris") - Out[24]: u'My name is Chris Barker' + In [24]: "My name is {first} {last}".format(last="Barker", first="Chris") + Out[24]: 'My name is Chris Barker' Build a dict of the keys and values: .. code-block:: ipython - In [25]: d = {u"last":u"Barker", u"first":u"Chris"} + In [25]: d = {"last":"Barker", "first":"Chris"} And pass to ``format()``with ``**`` .. code-block:: ipython - In [26]: u"My name is {first} {last}".format(**d) - Out[26]: u'My name is Chris Barker' + In [26]: "My name is {first} {last}".format(**d) + Out[26]: 'My name is Chris Barker' ===================================== A bit more on mutability (and copies) @@ -308,7 +595,7 @@ 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. -Same for dicts (and any container type) +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. @@ -359,7 +646,6 @@ I happened on this thread on stack overflow: http://stackoverflow.com/questions/3975376/understanding-dict-copy-shallow-or-deep - The OP is pretty confused -- can you sort it out? Make sure you understand the difference between a reference, a shallow copy, and a deep copy. @@ -376,7 +662,7 @@ Another "gotcha" is using mutables as default arguments: ....: print(a) ....: -This makes sense: maybe you'd pass in a list, but the default is an empty list. +This makes sense: maybe you'd pass in a specific list, but if not, the default is an empty list. But: @@ -414,7 +700,6 @@ The standard practice for such a mutable default argument: You get a new list every time the function is called - LAB ---- @@ -432,6 +717,7 @@ 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`` + - and print those Lightning Talks ---------------- @@ -446,16 +732,14 @@ Lightning Talks | - - ============================ List and Dict Comprehensions ============================ List comprehensions ------------------- -A bit of functional programming +A bit of functional programming consider this common ``for`` loop structure: @@ -474,7 +758,6 @@ This can be expressed with a single line using a "list comprehension" .. nextslide:: - What about nested for loops? .. code-block:: python @@ -512,7 +795,6 @@ You can add a conditional to the comprehension: new_list = [expr for var in a_list if something_is_true] - (demo) .. nextslide:: @@ -534,7 +816,7 @@ Examples: .. nextslide:: -Remember this from last week? +Remember this from earlier today? .. code-block:: python @@ -547,7 +829,6 @@ Remember this from last week? .... - Set Comprehensions ------------------ @@ -677,12 +958,11 @@ block. Standard Library: ``unittest`` ------------------------------- - The original testing system in Python. ``import unittest`` -More or less a port of Junit from Java +More or less a port of ``Junit`` from Java A bit verbose: you have to write classes & methods @@ -747,8 +1027,7 @@ This way, you can write your code in one file and test it from another: 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 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. @@ -769,28 +1048,29 @@ This way, you can write your code in one file and test it from another: Test discovery is both inflexible and brittle. -.. nextslide:: Other Options + And there is no built-in parameterized testing. -There are several other options for running tests in Python. +Other Options +------------- +There are several other options for running tests in Python. -* `Nose`_ -* `pytest`_ -* ... (many frameworks supply their own test runners) +* `Nose`: https://nose.readthedocs.org/ -We are going to play today with pytest +* `pytest`: http://pytest.org/latest/ -.. _Nose: https://nose.readthedocs.org/ -.. _pytest: http://pytest.org/latest/ +* ... (many frameworks supply their own test runners) +Both are very capable and widely used. I have a personal preference for pytest -- so we'll use it for this class -.. nextslide:: Installing ``pytest`` +Installing ``pytest`` +--------------------- The first step is to install the package: .. code-block:: bash - (cff2py)$ pip install pytest + $ python3 -m pip install pytest Once this is complete, you should have a ``py.test`` command you can run at the command line: @@ -806,11 +1086,12 @@ If you have any tests in your repository, that will find and run them. **Do you?** -.. nextslide:: Pre-existing Tests +Pre-existing Tests +------------------ Let's take a look at some examples. -``\Examples\Session05`` +``IntroToPython\Examples\Session05`` `` $ py.test`` @@ -836,8 +1117,7 @@ It follows some simple rules: * 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. +* Any classes that start with ``Test`` are treated similarly, with methods that begin with ``test_`` treated as tests. .. nextslide:: pytest @@ -870,10 +1150,11 @@ Pick an example from codingbat: Do a bit of test-driven development on it: - * run somethign on the web site. + * 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. +Do at least two of these... ========= Homework @@ -883,25 +1164,37 @@ Catch up! --------- -* First task -- catch up from last week. +* Finish the LABs from today + - Exceptions lab + +* Catch up from last week. + - Add Exception handling to mailroom - and add some tests - and list (and dict, and set) comprehensions... -* Then on to some exercises.... +* If you've done all that -- check out the collections module: + - https://docs.python.org/3.5/library/collections.html + - here's a good overview: https://pymotw.com/3/collections/ +==================================== +Material to review before next week: +==================================== -================================ -Material to review for Homework: -================================ + * Dive into Python3: 7.2 -- 7.3 + http://www.diveintopython3.net/iterators.html#defining-classes -Raymond Hettinger: + * Think Pyhton: 15 -- 18 + http://www.greenteapress.com/thinkpython/html/thinkpython016.html -https://youtu.be/HTLu2DFOdTg - -https://speakerdeck.com/pyconslides/pythons-class-development-toolkit-by-raymond-hettinger + * LPTHW: 40 -- 44 + http://learnpythonthehardway.org/book/ex40.html +[note that in py3 you dont need to inherit from object] +Talk by Raymond Hettinger: +https://youtu.be/HTLu2DFOdTg +https://speakerdeck.com/pyconslides/pythons-class-development-toolkit-by-raymond-hettinger diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst index 5d4bd0ad..af579adf 100644 --- a/slides_sources/source/session06.rst +++ b/slides_sources/source/session06.rst @@ -97,7 +97,7 @@ You don't actually need to use the result of a list comp: The collections module ----------------------- -The collections module has a numbe rof handy special purpose +The collections module has a number of handy special purpose collections: * defautltdict From 31c95352db88762ad950aa9629d1ff1888b7ab6d Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sat, 7 Nov 2015 20:26:41 -0800 Subject: [PATCH 014/129] restructured a bit -- moved Exceptions and Argument passing to later lectures --- Syllabus.rst | 9 +- .../source/exercises/comprehensions_lab.rst | 12 +- slides_sources/source/session04.rst | 233 +---------- slides_sources/source/session05.rst | 335 +-------------- slides_sources/source/session06.rst | 392 +++++++++++++++--- 5 files changed, 358 insertions(+), 623 deletions(-) diff --git a/Syllabus.rst b/Syllabus.rst index 97bcddde..96175504 100644 --- a/Syllabus.rst +++ b/Syllabus.rst @@ -276,16 +276,12 @@ Dictionaries, Sets and Mutability. Files and Text Processing -Exceptions. - Week 5: November 3 ........................ Exceptions -Advanced Argument passing - Testing List and Dict Comprehensions @@ -293,14 +289,17 @@ List and Dict Comprehensions Week 6: November 10 .................... +Advanced Argument passing + Lambda and Functions as Objects -Object Oriented Programming: classes, instances, and methods Week 7: November 17 ....................... +Object Oriented Programming: classes, instances, and methods + More OO -- Multiple inheritance, Properties, special methods diff --git a/slides_sources/source/exercises/comprehensions_lab.rst b/slides_sources/source/exercises/comprehensions_lab.rst index a556e5f2..5fac6b40 100644 --- a/slides_sources/source/exercises/comprehensions_lab.rst +++ b/slides_sources/source/exercises/comprehensions_lab.rst @@ -201,12 +201,12 @@ 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"} + food_prefs = {"name": "Chris", + "city": "Seattle", + "cake": "chocolate", + "fruit": "mango", + "salad": "greek", + "pasta": "lasagna"} .. nextslide:: Working with this dict: diff --git a/slides_sources/source/session04.rst b/slides_sources/source/session04.rst index a86a7a64..4e551a72 100644 --- a/slides_sources/source/session04.rst +++ b/slides_sources/source/session04.rst @@ -1,9 +1,9 @@ .. Foundations 2: Python slides file, created by Chris Barker: May 12, 2014. -******************************************************* -Session Four: Dictionaries, Sets, Exceptions, and Files -******************************************************* +******************************************* +Session Four: Dictionaries, Sets, and Files +******************************************* ================ @@ -176,7 +176,7 @@ in operational code should be:: if m < 0: raise ValueError -I'll cover Exceptions later this class... +I'll cover next week ... (Asserts get ignored if optimization is turned on!) @@ -659,231 +659,6 @@ Lightning Talk | -========== -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 ``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 - - ======================== File Reading and Writing ======================== diff --git a/slides_sources/source/session05.rst b/slides_sources/source/session05.rst index 68b1232f..496c9150 100644 --- a/slides_sources/source/session05.rst +++ b/slides_sources/source/session05.rst @@ -1,7 +1,7 @@ -***************************************************************** -Session Five: Advanced Argument passing, Testing, Comprehensions -***************************************************************** +************************************************* +Session Five: Exceptions, Testing, Comprehensions +************************************************* ====================== Lightning Talks Today: @@ -180,7 +180,7 @@ Anything in particular you'd like me to go over? Exceptions ========== -Since there wasn't time last class... +A really nifty python feature -- really handy! Exceptions ---------- @@ -391,333 +391,6 @@ A number of you already did this -- so do it at home if you haven't :ref:`exercise_exceptions_lab` -========================= -Advanced Argument Passing -========================= - -Keyword arguments ------------------ - -When defining a function, you can specify only what you need -- in any order - -.. 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 - - -.. nextslide:: - - -A Common Idiom: - -.. code-block:: python - - def fun(x, y=None): - if y is None: - do_something_different - go_on_here - - - -.. nextslide:: - -Can set defaults to variables - -.. code-block:: ipython - - In [156]: y = 4 - In [157]: def fun(x=y): - print("x is:", x) - .....: - In [158]: fun() - x is: 4 - - -.. nextslide:: - -Defaults are evaluated when the function is defined - -.. code-block:: ipython - - 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 - - -Function arguments in variables -------------------------------- - -function arguments are really just - -* a tuple (positional arguments) -* a dict (keyword arguments) - -.. code-block:: python - - def f(x, y, w=0, h=0): - print("position: {}, {} -- shape: {}, {}".format(x, y, w, h)) - - position = (3,4) - size = {'h': 10, 'w': 20} - - >>> f(*position, **size) - position: 3, 4 -- shape: 20, 10 - - - -Function parameters in variables --------------------------------- - -You can also pull the parameters out in the function as a tuple and a dict: - -.. code-block:: ipython - - 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} - -This can be very powerful... - -Passing a dict to str.format() -------------------------------- - -Now that you know that keyword args are really a dict, -you can do this nifty trick: - -The string ``format()`` method takes keyword arguments: - -.. code-block:: ipython - - In [24]: "My name is {first} {last}".format(last="Barker", first="Chris") - Out[24]: 'My name is Chris Barker' - -Build a dict of the keys and values: - -.. code-block:: ipython - - In [25]: d = {"last":"Barker", "first":"Chris"} - -And pass to ``format()``with ``**`` - -.. code-block:: ipython - - In [26]: "My name is {first} {last}".format(**d) - Out[26]: 'My name is Chris Barker' - -===================================== -A bit more on mutability (and copies) -===================================== - -mutable objects ----------------- - -We've talked about this: mutable objects can have their contents changed in place. - -Immutable objects can not. - -This has implications when you have a container with mutable objects in it: - -.. code-block:: ipython - - In [28]: list1 = [ [1,2,3], ['a','b'] ] - -one way to make a copy of a list: - -.. code-block:: ipython - - In [29]: list2 = list1[:] - - In [30]: list2 is list1 - Out[30]: False - -they are different lists. - -.. nextslide:: - -What if we set an element to a new value? - -.. code-block:: ipython - - In [31]: list1[0] = [5,6,7] - - In [32]: list1 - Out[32]: [[5, 6, 7], ['a', 'b']] - - In [33]: list2 - Out[33]: [[1, 2, 3], ['a', 'b']] - -So they are independent. - -.. nextslide:: - -But what if we mutate an element? - -.. code-block:: ipython - - In [34]: list1[1].append('c') - - In [35]: list1 - Out[35]: [[5, 6, 7], ['a', 'b', 'c']] - - 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. - -.. nextslide:: - -Why is that? - -.. code-block:: ipython - - In [38]: list1[1] is list2[1] - Out[38]: True - -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. - -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. - - -The copy module ----------------- - -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: - -.. code-block:: ipython - - In [39]: import copy - - In [40]: list3 = copy.copy(list2) - - In [41]: list3 - Out[41]: [[1, 2, 3], ['a', 'b', 'c']] - -This is also a shallow copy. - -.. nextslide:: - -But there is another option: - -.. code-block:: ipython - - In [3]: list1 - Out[3]: [[1, 2, 3], ['a', 'b', 'c']] - - In [4]: list2 = copy.deepcopy(list1) - - In [5]: list1[0].append(4) - - In [6]: list1 - Out[6]: [[1, 2, 3, 4], ['a', 'b', 'c']] - - In [7]: list2 - Out[7]: [[1, 2, 3], ['a', 'b', 'c']] - -``deepcopy`` recurses through the object, making copies of everything as it goes. - -.. nextslide:: - - -I happened on this thread on stack overflow: - -http://stackoverflow.com/questions/3975376/understanding-dict-copy-shallow-or-deep - -The OP is pretty confused -- can you sort it out? - -Make sure you understand the difference between a reference, a shallow copy, and a deep copy. - -Mutables as default arguments: ------------------------------- - -Another "gotcha" is using mutables as default arguments: - -.. code-block:: ipython - - In [11]: def fun(x, a=[]): - ....: a.append(x) - ....: print(a) - ....: - -This makes sense: maybe you'd pass in a specific list, but if not, the default is an empty list. - -But: - -.. code-block:: ipython - - In [12]: fun(3) - [3] - - In [13]: fun(4) - [3, 4] - -Huh?! - -.. nextslide:: - -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 solution: - -The standard practice for such a mutable default argument: - -.. code-block:: ipython - - 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] - -You get a new list every time the function is called - - -LAB ----- - -.. rst-class:: medium - - 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 -* Have it pull the parameters out with ``*args, **kwargs`` - - and print those Lightning Talks ---------------- diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst index af579adf..bacce287 100644 --- a/slides_sources/source/session06.rst +++ b/slides_sources/source/session06.rst @@ -1,25 +1,15 @@ -******************************************************** -Session Six: Functional and Object Oriented Programming -******************************************************** +******************************************************************** +Session Six: Advanced Argument Passing, lambda, functions as objects +******************************************************************** .. rst-class:: left medium - Lambda and Functional programming. + Advanced Argument passing - Object oriented programming: + Lambda - classes, instances, attributes, and subclassing - -===== -NOTE: -===== - -.. rst-class:: center large - - Veteran's Day: - - No class next week + Functions as objects ====================== Lightning Talks Today: @@ -27,14 +17,11 @@ Lightning Talks Today: .. rst-class:: medium - Aleksey Kramer - - Alexander R Galvin + Gabriel Meringolo - Gideon I Sylvan - - Hui Zhang + Joseph Cardenas + Marc Teale ================ Review/Questions @@ -43,11 +30,11 @@ Review/Questions Review of Previous Class ------------------------ -* Argument Passing: ``*args``, ``**kwargs`` +* Exceptions -* comprehensions +* Comprehensions -* testing (a bit more on that soon) +* Testing (a bit more on that soon) =============== Homework review @@ -105,42 +92,343 @@ collections: * deque * Counter -https://docs.python.org/2/library/collections.html +https://docs.python.org/3/library/collections.html -defaultdict ------------ +Did you all explore that a bit? -An alternative to ``dict.setdefault()`` +============================ +Test Driven development demo +============================ -Makes sense when you are buildng a dict where every value will be the same thing +In ``Examples/Session06/`` + +========================= +Advanced Argument Passing +========================= + +Keyword arguments +----------------- + +When defining a function, you can specify only what you need -- in any order + +.. 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 + + +.. nextslide:: -Carolyn found this in the ``collections`` package. Useful for the trigrams -assignment: + +A Common Idiom: .. code-block:: python - from collections import defaultdict + def fun(x, y=None): + if y is None: + do_something_different + go_on_here - trigrams = defaultdict(list) - ... - trigrams[pair].append(follower) -Counter -------- -``Counter``: +.. nextslide:: -Hui Zhang found this for counting how many students used which previous -languages. +Can set defaults to variables -See my example in ``/Solutions/Session05`` +.. code-block:: ipython + In [156]: y = 4 + In [157]: def fun(x=y): + print("x is:", x) + .....: + In [158]: fun() + x is: 4 -============================ -Test Driven development demo -============================ -In ``Examples/Session06/`` +.. nextslide:: + +Defaults are evaluated when the function is defined + +.. code-block:: ipython + + 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 + + +Function arguments in variables +------------------------------- + +function arguments are really just + +* a tuple (positional arguments) +* a dict (keyword arguments) + +.. code-block:: python + + def f(x, y, w=0, h=0): + print("position: {}, {} -- shape: {}, {}".format(x, y, w, h)) + + position = (3,4) + size = {'h': 10, 'w': 20} + + >>> f(*position, **size) + position: 3, 4 -- shape: 20, 10 + + + +Function parameters in variables +-------------------------------- + +You can also pull the parameters out in the function as a tuple and a dict: + +.. code-block:: ipython + + 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} + +This can be very powerful... + +Passing a dict to str.format() +------------------------------- + +Now that you know that keyword args are really a dict, +you can do this nifty trick: + +The string ``format()`` method takes keyword arguments: + +.. code-block:: ipython + + In [24]: "My name is {first} {last}".format(last="Barker", first="Chris") + Out[24]: 'My name is Chris Barker' + +Build a dict of the keys and values: + +.. code-block:: ipython + + In [25]: d = {"last":"Barker", "first":"Chris"} + +And pass to ``format()``with ``**`` + +.. code-block:: ipython + + In [26]: "My name is {first} {last}".format(**d) + Out[26]: 'My name is Chris Barker' + +===================================== +A bit more on mutability (and copies) +===================================== + +mutable objects +---------------- + +We've talked about this: mutable objects can have their contents changed in place. + +Immutable objects can not. + +This has implications when you have a container with mutable objects in it: + +.. code-block:: ipython + + In [28]: list1 = [ [1,2,3], ['a','b'] ] + +one way to make a copy of a list: + +.. code-block:: ipython + + In [29]: list2 = list1[:] + + In [30]: list2 is list1 + Out[30]: False + +they are different lists. + +.. nextslide:: + +What if we set an element to a new value? + +.. code-block:: ipython + + In [31]: list1[0] = [5,6,7] + + In [32]: list1 + Out[32]: [[5, 6, 7], ['a', 'b']] + + In [33]: list2 + Out[33]: [[1, 2, 3], ['a', 'b']] + +So they are independent. + +.. nextslide:: + +But what if we mutate an element? + +.. code-block:: ipython + + In [34]: list1[1].append('c') + + In [35]: list1 + Out[35]: [[5, 6, 7], ['a', 'b', 'c']] + + 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. + +.. nextslide:: + +Why is that? + +.. code-block:: ipython + + In [38]: list1[1] is list2[1] + Out[38]: True + +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. + +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. + + +The copy module +---------------- + +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: + +.. code-block:: ipython + + In [39]: import copy + + In [40]: list3 = copy.copy(list2) + + In [41]: list3 + Out[41]: [[1, 2, 3], ['a', 'b', 'c']] + +This is also a shallow copy. + +.. nextslide:: + +But there is another option: + +.. code-block:: ipython + + In [3]: list1 + Out[3]: [[1, 2, 3], ['a', 'b', 'c']] + + In [4]: list2 = copy.deepcopy(list1) + + In [5]: list1[0].append(4) + + In [6]: list1 + Out[6]: [[1, 2, 3, 4], ['a', 'b', 'c']] + + In [7]: list2 + Out[7]: [[1, 2, 3], ['a', 'b', 'c']] + +``deepcopy`` recurses through the object, making copies of everything as it goes. + +.. nextslide:: + + +I happened on this thread on stack overflow: + +http://stackoverflow.com/questions/3975376/understanding-dict-copy-shallow-or-deep + +The OP is pretty confused -- can you sort it out? + +Make sure you understand the difference between a reference, a shallow copy, and a deep copy. + +Mutables as default arguments: +------------------------------ + +Another "gotcha" is using mutables as default arguments: + +.. code-block:: ipython + + In [11]: def fun(x, a=[]): + ....: a.append(x) + ....: print(a) + ....: + +This makes sense: maybe you'd pass in a specific list, but if not, the default is an empty list. + +But: + +.. code-block:: ipython + + In [12]: fun(3) + [3] + + In [13]: fun(4) + [3, 4] + +Huh?! + +.. nextslide:: + +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 solution: + +The standard practice for such a mutable default argument: + +.. code-block:: ipython + + 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] + +You get a new list every time the function is called + + +LAB +---- + +.. rst-class:: medium + + 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 +* Have it pull the parameters out with ``*args, **kwargs`` + - and print those =================== @@ -156,7 +444,7 @@ lambda In [172]: f(2,3) Out[172]: 5 -Content can only be an expression -- not a statement +Content of function can only be an expression -- not a statement Anyone remember what the difference is? @@ -331,7 +619,7 @@ Can also use keyword arguments l.append(lambda x, e=i: x**e) .....: In [189]: for f in l: - print f(3) + print(f(3)) 1 3 9 @@ -372,7 +660,7 @@ Not clear? here's what you should get ## 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) + print(f(5)) .....: 5 6 @@ -597,8 +885,8 @@ Basic Structure of a real class: p = Point(3,4) ## access the attributes - print "p.x is:", p.x - print "p.y is:", p.y + print("p.x is:", p.x) + print("p.y is:", p.y) see: ``Examples/Session06/simple_classes.py`` @@ -810,7 +1098,7 @@ Overriding is as simple as creating a new attribute with the same name: class NewCircle(Circle): color = "blue" >>> nc = NewCircle - >>> print nc.color + >>> print(nc.color) blue From e08610ef2955537d477c3ccad8d8728409ba5dc8 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sat, 7 Nov 2015 20:39:49 -0800 Subject: [PATCH 015/129] updated examples to py3 --- Examples/Session06/cigar_party.py | 2 +- Examples/Session06/simple_classes.py | 34 ++++++++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/Examples/Session06/cigar_party.py b/Examples/Session06/cigar_party.py index 18878463..e107ca6a 100644 --- a/Examples/Session06/cigar_party.py +++ b/Examples/Session06/cigar_party.py @@ -15,7 +15,7 @@ def cigar_party(cigars, is_weekend): """ basic solution """ - if ( 40 <= cigars <= 60 ) or ( cigars >= 40 and is_weekend): + if (40 <= cigars <= 60) or (cigars >= 40 and is_weekend): return True else: return False diff --git a/Examples/Session06/simple_classes.py b/Examples/Session06/simple_classes.py index b170209d..0cf80bbc 100644 --- a/Examples/Session06/simple_classes.py +++ b/Examples/Session06/simple_classes.py @@ -8,50 +8,54 @@ import math -## create a point class +# create a point class class Point(object): def __init__(self, x, y): self.x = x self.y = y -## create an instance of that class -p = Point(3,4) +# create an instance of that class +p = Point(3, 4) -## access the attributes -print "p.x is:", p.x -print "p.y is:", p.y +# access the attributes +print("p.x is:", p.x) +print("p.y is:", p.y) class Point2(object): size = 4 - color= "red" + color = "red" + def __init__(self, x, y): self.x = x self.y = y -p2 = Point2(4,5) -print p2.size -print p2.color +p2 = Point2(4, 5) +print(p2.size) +print(p2.color) class Point3(object): size = 4 - color= "red" + color = "red" + def __init__(self, x, y): self.x = x self.y = y + def get_color(self): return self.color -p3 = Point3(4,5) -print p3.size -print p3.get_color() +p3 = Point3(4, 5) +print(p3.size) +print(p3.get_color()) class Circle(object): color = "red" styles = ['dashed'] + def __init__(self, diameter): self.diameter = diameter @@ -78,7 +82,7 @@ def grow(self, factor=2): self.diameter = self.diameter * math.sqrt(2) nc = NewCircle -print nc.color +print(nc.color) class CircleR(Circle): From 669aafe9546c19bb63d1d7c1ed9fe1fabbdf251b Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Mon, 9 Nov 2015 21:34:40 -0800 Subject: [PATCH 016/129] updating / rearranging session 6 --- Examples/{Session06 => Session07}/class.py | 0 .../{Session06 => Session07}/class_demo.py | 0 .../html_render/.DS_Store | Bin .../html_render/html_render.py | 0 .../html_render/run_html_render.py | 0 .../html_render/sample_html.html | 0 .../simple_classes.py | 0 slides_sources/source/exercises/index.rst | 10 +- slides_sources/source/session06.rst | 798 ++---------------- slides_sources/source/session08.rst | 21 + 10 files changed, 78 insertions(+), 751 deletions(-) rename Examples/{Session06 => Session07}/class.py (100%) rename Examples/{Session06 => Session07}/class_demo.py (100%) rename Examples/{Session06 => Session07}/html_render/.DS_Store (100%) rename Examples/{Session06 => Session07}/html_render/html_render.py (100%) rename Examples/{Session06 => Session07}/html_render/run_html_render.py (100%) rename Examples/{Session06 => Session07}/html_render/sample_html.html (100%) rename Examples/{Session06 => Session07}/simple_classes.py (100%) diff --git a/Examples/Session06/class.py b/Examples/Session07/class.py similarity index 100% rename from Examples/Session06/class.py rename to Examples/Session07/class.py diff --git a/Examples/Session06/class_demo.py b/Examples/Session07/class_demo.py similarity index 100% rename from Examples/Session06/class_demo.py rename to Examples/Session07/class_demo.py 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 100% rename from Examples/Session06/html_render/run_html_render.py rename to Examples/Session07/html_render/run_html_render.py 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/Session06/simple_classes.py b/Examples/Session07/simple_classes.py similarity index 100% rename from Examples/Session06/simple_classes.py rename to Examples/Session07/simple_classes.py diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index d7dd1de4..7f88520d 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -48,12 +48,18 @@ Session 6: ---------- .. toctree:: :maxdepth: 1 - - html_renderer + trapazoid Session 7: ---------- .. toctree:: :maxdepth: 1 + html_renderer + +Session 8: +----------- +.. toctree:: + :maxdepth: 1 + circle_class diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst index bacce287..1a272792 100644 --- a/slides_sources/source/session06.rst +++ b/slides_sources/source/session06.rst @@ -34,7 +34,7 @@ Review of Previous Class * Comprehensions -* Testing (a bit more on that soon) +* Testing =============== Homework review @@ -81,31 +81,21 @@ You don't actually need to use the result of a list comp: [ st.add(j) for j in range(21) if not j%i ] -The collections module ------------------------ - -The collections module has a number of handy special purpose -collections: - - * defautltdict - * namedtuple - * deque - * Counter - -https://docs.python.org/3/library/collections.html - -Did you all explore that a bit? - ============================ Test Driven development demo ============================ +We did some of this last class -- but i want to really drive it home :-) + In ``Examples/Session06/`` + ========================= Advanced Argument Passing ========================= +This is a very, very nift Python feature -- it really lets you write dynamic programs. + Keyword arguments ----------------- @@ -113,15 +103,15 @@ When defining a function, you can specify only what you need -- in any order .. code-block:: ipython - In [151]: def fun(x,y=0,z=0): + 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(1, z=3, y=2) - 1 2 3 + In [154]: fun(z=3, y=2) + 0 2 3 .. nextslide:: @@ -137,7 +127,6 @@ A Common Idiom: go_on_here - .. nextslide:: Can set defaults to variables @@ -168,6 +157,8 @@ Defaults are evaluated when the function is defined In [160]: fun() x is: 4 +This is a **very** important point -- I will repeat it! + Function arguments in variables ------------------------------- @@ -189,7 +180,6 @@ function arguments are really just position: 3, 4 -- shape: 20, 10 - Function parameters in variables -------------------------------- @@ -211,7 +201,7 @@ Passing a dict to str.format() ------------------------------- Now that you know that keyword args are really a dict, -you can do this nifty trick: +you know how this nifty trick works: The string ``format()`` method takes keyword arguments: @@ -233,6 +223,37 @@ And pass to ``format()``with ``**`` In [26]: "My name is {first} {last}".format(**d) Out[26]: 'My name is Chris Barker' +LAB +---- + +.. rst-class:: medium + + 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 +* Have it pull the parameters out with ``*args, **kwargs`` + - and print those + +Lightning Talks +---------------- + +.. rst-class:: medium + + | + | Gabriel Meringolo + | + | Joseph Cardenas + | + + ===================================== A bit more on mutability (and copies) ===================================== @@ -411,25 +432,6 @@ The standard practice for such a mutable default argument: You get a new list every time the function is called -LAB ----- - -.. rst-class:: medium - - 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 -* Have it pull the parameters out with ``*args, **kwargs`` - - and print those - =================== Anonymous functions @@ -669,728 +671,26 @@ Not clear? here's what you should get ### 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. - - -Lightning Talks ----------------- - -.. rst-class:: medium - -| -| Aleksey Kramer -| -| Alexander R Galvin -| - - -=========================== -Object Oriented Programming -=========================== - -Object Oriented Programming ---------------------------- - -More about Python implementation than OO design/strengths/weaknesses - -One reason for this: - -Folks can't even agree on what OO "really" means - -See: The Quarks of Object-Oriented Development - - - Deborah J. Armstrong - -http://agp.hx0.ru/oop/quarks.pdf - - -.. nextslide:: - -Is Python a "True" Object-Oriented Language? - -(Doesn't support full encapsulation, doesn't *require* -classes, etc...) - -.. nextslide:: - -.. rst-class:: center large - - I don't Care! - - -Good software design is about code re-use, clean separation of concerns, -refactorability, testability, etc... - -OO can help with all that, but: - * It doesn't guarantee it - * It can get in the way - -.. nextslide:: - -Python is a Dynamic Language - -That clashes with "pure" OO - -Think in terms of what makes sense for your project - -- not any one paradigm of software design. - - -.. nextslide:: - -So what is "object oriented programming"? - -| - "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" - -| - -http://en.wikipedia.org/wiki/Object-oriented_programming - -.. nextslide:: - -Even simpler: - - -"Objects are data and the functions that act on them in one place." - -This is the core of "encapsulation" - -In Python: just another namespace. - -.. nextslide:: - -The OO buzzwords: - - * data abstraction - * encapsulation - * modularity - * polymorphism - * inheritance - -Python does all of this, though it doesn't enforce it. - -.. nextslide:: - -You can do OO in C - -(see the GTK+ project) - - -"OO languages" give you some handy tools to make it easier (and safer): - - * polymorphism (duck typing gives you this anyway) - * inheritance - - -.. nextslide:: - -OO is the dominant model for the past couple decades - -You will need to use it: - -- It's a good idea for a lot of problems - -- You'll need to work with OO packages - -(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) - -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 - - The ``class`` statement - - ``class`` creates a new type object: - - .. code-block:: ipython - - In [4]: class C(object): - pass - ...: - In [5]: type(C) - Out[5]: type - - A class is a type -- interesting! - - It is created when the statement is run -- much like ``def`` - - You don't *have* to subclass from ``object``, but you *should* - - (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 - -.. nextslide:: - -Basic Structure of a real class: - -.. code-block:: python - - class Point(object): - # everything defined in here is in the class namespace - - def __init__(self, x, y): - self.x = x - self.y = y - - ## 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/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 - -.. 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 - - 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(object): - def a_function(self, x, y): - ... - - -Does this look familiar from C-style procedural programming? - - -.. nextslide:: - -Anything assigned to a ``self.`` attribute is kept in the instance -name space -- ``self`` *is* the instance. - -That's where all the instance-specific data is. - -.. code-block:: python - - 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. - -The class is one namespace, the instance is another. - - -.. code-block:: python - - class Point(object): - size = 4 - color= "red" - ... - def get_color(): - return self.color - >>> p3.get_color() - 'red' - - -class attributes are accessed with ``self`` also. - - -.. nextslide:: - -Typical methods: - -.. code-block:: python - - class Circle(object): - color = "red" - - def __init__(self, diameter): - self.diameter = diameter - - def grow(self, factor=2): - self.diameter = self.diameter * factor - - -Methods take some parameters, manipulate the attributes in ``self``. - -They may or may not return something useful. - -.. 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 ----- - -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`` - -We'll start with a single class, then add some sub-classes -to specialize the behavior - -Details in: - -:ref:`exercise_html_renderer` - - -Let's see if you can do step 1. in class... - - -Lightning Talks ----------------- - .. rst-class:: medium | -| Gideon Sylvan -| -| Hui Zhang +| Marc Teale | -======================= -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`` - -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:: python - - class Circle(object): - 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(object): - ... - 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. - -""" - -| -| [ThinkPython 18.10] -| -| ( Demo of class vs. instance attributes ) - - -=================== -More on Subclassing -=================== - -Overriding \_\_init\_\_ ------------------------ - -``__init__`` common method to override} - -You often need to call the super class ``__init__`` as well - -.. code-block:: python - - 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) - +============================== +Closures and function Currying +============================== -exception to: "don't change the method signature" rule. +"Closures" and "Currying" are cool CS terms for what is really just defining functions on the fly. -More subclassing ----------------- -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 - - - 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. - - -When to Subclass ----------------- - -"Is a" relationship: Subclass/inheritance - -"Has a" relationship: Composition - -.. 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 - - -That's about it -- really! - - -Type-Based dispatch -------------------- - -You'll see code that looks like this: - -.. code-block:: python - - if isinstance(other, A_Class): - Do_something_with_other - else: - Do_something_else - - -Usually better to use "duck typing" (polymorphism) -But when it's called for: - * ``isinstance()`` - * ``issubclass()`` -.. nextslide:: - -GvR: "Five Minute Multi- methods in Python": - -http://www.artima.com/weblogs/viewpost.jsp?thread=101605 - -http://www.python.org/getit/releases/2.3/mro/ - -http://python-history.blogspot.com/2010/06/method-resolution-order.html - - -Wrap Up -------- - -Thinking OO in Python: - -Think about what makes sense for your code: - -* Code re-use -* Clean APIs -* ... - -Don't be a slave to what OO is *supposed* to look like. - -Let OO work for you, not *create* work for you - -.. nextslide:: - -OO in Python: - -The Art of Subclassing: *Raymond Hettinger* - -http://pyvideo.org/video/879/the-art-of-subclassing - -"classes are for code re-use -- not creating taxonomies" - -Stop Writing Classes: *Jack Diederich* - -http://pyvideo.org/video/880/stop-writing-classes - -"If your class has only two methods -- and one of them is ``__init__`` --- you don't need a class" - - -======== -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. - -Write a second version using a comprehension. - -.. nextslide:: Hint - -``sys.argv`` hold the command line arguments the user typed in. If the -user types: - -.. code-block:: bash - - $ python the_script a_file_name - -Then: - -.. code-block:: python - - import sys - filename = sys.argv[1] - -will get ``filename == "a_file_name"`` - - -Html rendering system: ------------------------ - -:ref:`exercise_html_renderer` - -| -You will build an html generator, using: -* A Base Class with a couple methods -* Subclasses overriding class attributes -* Subclasses overriding a method -* Subclasses overriding the ``__init__`` -These are the core OO approaches diff --git a/slides_sources/source/session08.rst b/slides_sources/source/session08.rst index f1a9624c..9fb1d07e 100644 --- a/slides_sources/source/session08.rst +++ b/slides_sources/source/session08.rst @@ -38,6 +38,27 @@ Eric W Westman Ryan J Albright +Personal Project +----------------- + +The bulk of the homework for the rest of the class will be a personal project: + +* 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 an specific python features (i.e. classes): use + what is appropriate for your project + +* Due the Friday after the last class (December 11) + +| +| By next week, send me a project proposal: can be short and sweet. +| + + ========================= Emulating Standard types ========================= From 1734355d497c4f4a88ea6b0fac6175dba62519f6 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Mon, 9 Nov 2015 23:05:43 -0800 Subject: [PATCH 017/129] updated trapazoid exercise --- Examples/Session06/closure.py | 25 + exercises/circle_class.html | 438 ----------------- exercises/fib_and_lucas.html | 269 ---------- exercises/grid_printer.html | 341 ------------- exercises/html_renderer.html | 458 ------------------ exercises/index.html | 229 --------- exercises/kata_fourteen.html | 333 ------------- slides_sources/source/exercises/trapezoid.rst | 171 +++++++ slides_sources/source/session06.rst | 25 + 9 files changed, 221 insertions(+), 2068 deletions(-) create mode 100644 Examples/Session06/closure.py delete mode 100644 exercises/circle_class.html delete mode 100644 exercises/fib_and_lucas.html delete mode 100644 exercises/grid_printer.html delete mode 100644 exercises/html_renderer.html delete mode 100644 exercises/index.html delete mode 100644 exercises/kata_fourteen.html create mode 100644 slides_sources/source/exercises/trapezoid.rst diff --git a/Examples/Session06/closure.py b/Examples/Session06/closure.py new file mode 100644 index 00000000..2f0b7a8d --- /dev/null +++ b/Examples/Session06/closure.py @@ -0,0 +1,25 @@ +#!/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/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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -
-

Circle Class Excercise

-
-

Circle Class

-
-

Goal:

-

The goal is to create a class that represents a simple circle.

-

A Circle can be defined by either specifying the radius or the diameter, -and the user can query the circle for either its radius or diameter.

-

Other abilities of a Circle instance:

-
-
    -
  • Compute the circle’s area
  • -
  • Print the circle and get something nice
  • -
  • Be able to add two circles together
  • -
  • Be able to compare two circles to see which is bigger
  • -
  • Be able to compare to see if there are equal
  • -
  • (follows from above) be able to put them in a list and sort them
  • -
-
-

This exercise should use “new style classes” i.e. inherit from object

-

You will also use:

-
-
    -
  • properties
  • -
  • a classmethod
  • -
  • a define a bunch of “special methods”
  • -
-
-
-
-

General Instructions:

-
    -
  1. For each step, write a couple of unit tests that test the new features.
  2. -
  3. Run these tests (and they will fail the first time)
  4. -
  5. Add the code required for your tests to pass.
  6. -
-
-
-

Step 1:

-

create class called Circle – it’s signature should look like:

-
c = Circle(the_radius)
-
-
-

The radius is a required parameter (can’t have a circle without one!)

-

the resulting circle should have a attribute for the radius:

-
c.radius
-
-
-

So you can do:

-
>> c = Circle(4)
->> print c.radius
-4
-
-
-

Remember: tests first!

-
-
-

Step 2:

-

Add a “diameter” property, so the user can get the diameter of the circle:

-
>> c = Circle(4)
->> print c.diameter
-8
-
-
-
-
-

Step 3:

-

Set up the diameter property so that the user can set the diameter of the circle:

-
>> c = Circle(4)
->> c.diameter = 2
->> print c.diameter
-2
->> print c.radius
-1
-
-
-

NOTE that the radius has changed!

-
-
-

Step 4:

-

Add an area property so the user can get the area of the circle:

-
>> c = Circle(2)
->> print c.area
-12.566370
-
-
-

(pi can be found in the math module)

-

The user should not be able to set the area:

-
>> c = Circle(2)
->> c.area = 42
-AttributeError
-
-
-
-
-

Step 5:

-

Add an “alternate constructor” that lets the user create a Circle directly -with the diameter:

-
>> c = Circle.from_diameter(8)
->> print c.diameter
-8
->> print c.radius
-4
-
-
-
-
-

Step 6:

-

Add __str__ and __repr__ methods to your Circle class.

-

Now you can print it:

-
In [2]: c = Circle(4)
-
-In [3]: print c
-Circle with radius: 4.000000
-
-In [4]: repr(c)
-Out[4]: 'Circle(4)'
-
-In [5]: d = eval(repr(c))
-
-In [6]: d
-Out[6]: Circle(4)
-
-
-
-
-

Step 7:

-

Add some of the numeric protocol to your Circle:

-

You should be able to add two circles:

-
In [7]: c1 = Circle(2)
-
-In [8]: c2 = Circle(4)
-
-In [9]: c1 + c2
-Out[9]: Circle(6)
-
-
-

and multiply one times a number:

-
In [16]: c2 * 3
-Out[16]: Circle(12)
-
-
-

(what happens with 3 * c2 ? – can you fix that?)

-
-
-

Step 8:

-

add the ability to compare two circles:

-
In [10]: c1 > c2
-Out[10]: False
-
-In [11]: c1 < c2
-Out[11]: True
-
-In [12]: c1 == c2
-Out[12]: False
-
-In [13]: c3 = Circle(4)
-
-In [14]: c2 == c3
-Out[14]: True
-
-
-

Once the comparing is done, you should be able to sort a list of circles:

-
In [18]: print circles
-[Circle(6), Circle(7), Circle(8), Circle(4), Circle(0), Circle(2), Circle(3), Circle(5), Circle(9), Circle(1)]
-
-In [19]: circl
-circle      circle.py   circle.pyc  circles
-
-In [19]: circles.sort()
-
-In [20]: print circles
-[Circle(0), Circle(1), Circle(2), Circle(3), Circle(4), Circle(5), Circle(6), Circle(7), Circle(8), Circle(9)]
-
-
-

NOTE: make sure to write unit tests for all of this! Ideally before writing the code.

-
-
-

Step 8: Optional Features:

-
    -
  • See if you can make “reflected” numerics do the right thing:
  • -
-
a_circle * 3 == 3 * a_circle
-
-
-
    -
  • What else makes sense: division? others?
  • -
  • Add the “augmented assignment” operators, where they make sense:
  • -
-
a_circle += another_circle
-
-a_circle *= 2
-
-
-
    -
  • look through all the “magic methods” and see what makes sense for circles
  • -
-
-
-
- - -
-
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exercises/fib_and_lucas.html b/exercises/fib_and_lucas.html deleted file mode 100644 index d2706e3d..00000000 --- a/exercises/fib_and_lucas.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - Fibonacci Series Exercise — Introduction To Python 1.3 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -
-

Fibonacci Series Exercise

-
-

Computing the Fibonacci and Lucas Series

-
-

Goal:

-

The Fibonacci Series is a numeric series starting with the integers 0 and 1.

-

In this series, the next integer is determined by summing the previous two.

-

This gives us:

-
0, 1, 1, 2, 3, 5, 8, 13, ...
-
-
-

We will write a function that computes this series – then generalize it.

-
-
-

Step 1

-
    -
  • Create a new module series.py in the session02 folder in your student folder.
      -
    • In it, add a function called fibonacci.
    • -
    • The function should have one parameter n.
    • -
    • The function should return the nth value in the fibonacci series.
    • -
    -
  • -
  • Ensure that your function has a well-formed docstring
  • -
-

Note that the fibinacci series is naturally recusive – the value is -defined by previous values:

-

fib(n) = fib(n-2) + fib(n-1)

-
-
-

Lucas Numbers

-

The Lucas Numbers are a related series of integers that start with the -values 2 and 1 rather than 0 and 1. The resulting series looks like this:

-
2, 1, 3, 4, 7, 11, 18, 29, ...
-
-
-

In your series.py module, add a new function lucas that returns the -nth value in the lucas numbers series.

-

Ensure that your function has a well-formed docstring

-
-
-

Generalizing

-

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

-
-
-

Tests...

-

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.

-
-
-
- - -
-
-
- - -
- -
-

- © Copyright 2014, Christopher Barker, Cris Ewing, . - -

-
- Built with Sphinx using a theme provided by Read the Docs. - -
- -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exercises/grid_printer.html b/exercises/grid_printer.html deleted file mode 100644 index 0f9c5f70..00000000 --- a/exercises/grid_printer.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - - - - - - Grid Printer Exercise — Introduction To Python 1.3 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -
-

Grid Printer Exercise

-
-

Printing a Grid

-

(adapted from Downey, “Think Python”, ex. 3.5)

-
-

Goal:

-

Write a function that draws a grid like the following:

-
+ - - - - + - - - - +
-|         |         |
-|         |         |
-|         |         |
-|         |         |
-+ - - - - + - - - - +
-|         |         |
-|         |         |
-|         |         |
-|         |         |
-+ - - - - + - - - - +
-
-
-
-
-

hints

-

A couple features to get you started...

-
-
-

printing

-

To print more than one value on a line, you can pass multiple names into the print function:

-
print('+', '-')
-
-
-

If you don’t want a newline after something is printed, you tell python what you want the print to end with like so:

-
print('+', end=' ')
-print('-')
-
-
-

The output of these statements is '+ -'.

-

(that end parameter defaults to a newline...)

-

A print function with no arguments ends the current line and goes to the next line:

-
print()
-
-
-
-
-

Simple string manipulation:

-

You can put two strings together with the plus operator:

-
In [20]: "this" + "that"
-Out[20]: 'thisthat
-
-
-

Particularly useful if they have been assigned names:

-
In [21]: plus = '+'
-
-In [22]: minus = '-'
-
-In [23]: plus+minus+plus
-Out[23]: '+-+'
-
-
-

Note that you can string any number of operations together in an expression.

-

You can also multiply strings:

-
In [24]: '+' * 10
-Out[24]: '++++++++++'
-
-
-

And combine that with plus in a complex expression:

-
In [29]: first_name = 'Chris'
-
-In [30]: last_name = 'Barker'
-
-In [31]: 5 * '*' + first_name +' ' + last_name + 5 * '*'
-Out[31]: '*****Chris Barker*****'
-
-
-

Note that there are better ways to build up complex strings – we’ll get to that later.

-

Now you’ve got what you need to print that grid...

-
-
-
-

Part 2

-

Making it more general

-
-

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 -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.

-
-
-
-

Part 3:

-

Even more general...

-
-

A function with two parameters

-

Write a function that draws a similar grid with three rows and three columns.

-

(what to do about rounding?)

-

And while you are at it – n rows and columns...

-
-
-
- - -
-
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exercises/html_renderer.html b/exercises/html_renderer.html deleted file mode 100644 index 87a7865d..00000000 --- a/exercises/html_renderer.html +++ /dev/null @@ -1,458 +0,0 @@ - - - - - - - - - - - HTML Renderer Exercise — Introduction To Python 1.3 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -
-

HTML Renderer Exercise

-
-

HTML Renderer

-
-

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:

-

sample_html.html

-

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

-

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...)

-
-
-

Step 1:

-

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.

-

See: step 1. in run_html_render.py

-
-
-

Step 2:

-

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.

-
-
-

Step 3:

-

Create a <head> element – simple subclass.

-

Create a OneLineTag subclass of Element:

-
    -
  • 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.

-

See test_html_output3.html

-
-
-

Step 4:

-

Extend the Element class to accept a set of attributes as keywords to the -constructor, ie. (run_html_render.py)

-
Element("some text content", id="TheList", style="line-height:200%")
-
-
-

( remember **kwargs? )

-

The render method will need to be extended to render the attributes properly.

-

You can now render some <p> tags (and others) with attributes

-

See test_html_output4.html

-
-
-

Step 5:

-

Create a SelfClosingTag subclass of Element, to render tags like:

-
<hr /> and <br /> (horizontal rule and line break).
-
-
-

You will need to override the render method to render just the one tag and -attributes, if any.

-

Create a couple subclasses of SelfClosingTag for and <hr /> and <br />

-

See test_html_output5.html

-
-
-

Step 6:

-

Create a 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:

-
A(u"http://google.com", u"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 can now add a link to your web page.

-

See test_html_output6.html

-
-
-

Step 7:

-

Create Ul class for an unordered list (really simple subclass of Element)

-

Create Li class for an element in a list (also really simple)

-

Add a list to your web page.

-

Create a Header class – this one should take an integer argument for the -header level. i.e <h1>, <h2>, <h3>, called like

-
H(2, "The text of the header")
-
-
-

for an <h2> header

-

It can subclass from OneLineTag – overriding the __init__, then calling the superclass __init__

-

See test_html_output7.html

-
-
-

Step 8:

-

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 <meta charset="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 doctype and encoding are HTML 5 and you can check this at: http://validator.w3.org.

-

You now have a pretty full-featured html renderer – play with it, add some -new tags, etc....

-

See test_html_output8.html

-
-
-
-

HTML Primer

-

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

-

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.

-

Here is a good basic intro:

-

http://www.w3schools.com/html/html_basic.asp

-

And there are countless others online.

-

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), -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.

-
-
-

Basic tags

-
<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>
-
-
-
-
-

Attributes:

-

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:

-
<p style="text-align: center; font-style: oblique;">
-
-
-

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:

-
<meta charset="UTF-8" />
-
-
-

To make a link, you use an “anchor” tag: <a>. It required attributes to indicate what the link is:

-
<a href="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>:

-
<ul style="line-height:200%" id="TheList">
-    <li>
-        The first item in a list
-    </li>
-    <li style="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.

-
<h2>PythonClass - Class 6 example</h2>
-
-
-

I think that’s all you need to know!

-
-
-
- - -
-
- - -
-
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exercises/index.html b/exercises/index.html deleted file mode 100644 index f8f23bed..00000000 --- a/exercises/index.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - - - - - - Homework Materials — Introduction To Python 1.3 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/exercises/kata_fourteen.html b/exercises/kata_fourteen.html deleted file mode 100644 index 0f0ce847..00000000 --- a/exercises/kata_fourteen.html +++ /dev/null @@ -1,333 +0,0 @@ - - - - - - - - - - - Kata Fourteen: Tom Swift Under Milk Wood — Introduction To Python 1.3 documentation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - -
-
- - - - - - -
- -
-
-
-
- -
-

Kata Fourteen: Tom Swift Under Milk Wood

-

Adapted from Dave Thomas’s work:

-

http://codekata.com/kata/kata14-tom-swift-under-the-milkwood/

-
-

Trigrams

-

Trigrams can be used to mutate text into new, surreal, forms. But what -heuristics do we apply to get a reasonable result?

-
-

The Problem

-

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:

-
I wish I may I wish I might
-
-
-

You might generate:

-
"I wish" => ["I", "I"]
-"wish I" => ["may", "might"]
-"may I"  => ["wish"]
-"I may"  => ["I"]
-
-
-

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).

-

There is a copy of sherlock holmes right here:

-

sherlock.txt.

-

And a shorter copy for testing:

-

sherlock_small.txt.

-
-
-

Objectives

-

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/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst new file mode 100644 index 00000000..7b00e0f8 --- /dev/null +++ b/slides_sources/source/exercises/trapezoid.rst @@ -0,0 +1,171 @@ +.. _exercise_trapezoidal_rule: + +***************** +Trapazoidal Rule +***************** + +Passing functions around +========================= + + +.. rst-class:: large left + + Goal: + +.. rst-class:: medium 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''' + return 3 + + 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: + +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 + +In the function, you want to compute the following equation: + + .. math:: + + result = \frac{b-a}{2N}(f(x_1) + 2f(x_2) + 2f(x_3) + 2f(x_4) + \dotsb + 2f(x_N) + f(x_{N+1})). + +So you will need to create a list of x values from a to b (maybe 100 or so values to start), and compute the function for each of those values, then add them all up, multiply by the half of the difference between a and b. + +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`` + +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 + 4 + +But then you need to write a new function for any value of these parameters you might need. 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 + coef = {'a':1, 'b':3, 'c': 2} + trapz(quadratic, 2, 20, **coef) + +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. + + + + + + + + + + + + + + + + + + + diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst index 1a272792..9a815d56 100644 --- a/slides_sources/source/session06.rst +++ b/slides_sources/source/session06.rst @@ -685,10 +685,35 @@ Lightning Talk Closures and function Currying ============================== +Defining specilized 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 some examples: + +.. 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 define stored the start ``start_at`` From 1e3cf0753c3021fa503c35c29f6f1258170d3352 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 10 Nov 2015 16:06:30 -0800 Subject: [PATCH 018/129] updating for session 6 --- Examples/Session06/cigar_party.py | 28 - Examples/Session06/test_cigar_party.py | 19 +- slides_sources/source/conf.py | 2 +- slides_sources/source/exercises/index.rst | 4 +- .../source/exercises/string_formatting.rst | 1 + slides_sources/source/exercises/trapezoid.rst | 31 +- slides_sources/source/session06.rst | 252 ++++-- slides_sources/source/session07.rst | 728 +++++++++++++++++- 8 files changed, 944 insertions(+), 121 deletions(-) delete mode 100644 Examples/Session06/cigar_party.py diff --git a/Examples/Session06/cigar_party.py b/Examples/Session06/cigar_party.py deleted file mode 100644 index e107ca6a..00000000 --- a/Examples/Session06/cigar_party.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -""" -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. - -Return True if the party with the given values is successful, -or False otherwise. -""" - - -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_party3(cigars, is_weekend): - """ - conditional expression - """ - return (cigars >= 40) if is_weekend else (cigars >= 40 and cigars <= 60) diff --git a/Examples/Session06/test_cigar_party.py b/Examples/Session06/test_cigar_party.py index a03ca3c5..b3d76446 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(): @@ -51,4 +61,3 @@ def test_10(): def test_11(): assert cigar_party(39, True) is False - diff --git a/slides_sources/source/conf.py b/slides_sources/source/conf.py index db0a95c6..f31fc924 100644 --- a/slides_sources/source/conf.py +++ b/slides_sources/source/conf.py @@ -35,7 +35,7 @@ 'sphinx.ext.todo', 'sphinx.ext.coverage', # 'sphinx.ext.pngmath', - #'sphinx.ext.mathjax', + 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'IPython.sphinxext.ipython_console_highlighting', 'IPython.sphinxext.ipython_directive', diff --git a/slides_sources/source/exercises/index.rst b/slides_sources/source/exercises/index.rst index 7f88520d..83e0e8cd 100644 --- a/slides_sources/source/exercises/index.rst +++ b/slides_sources/source/exercises/index.rst @@ -48,7 +48,9 @@ Session 6: ---------- .. toctree:: :maxdepth: 1 - trapazoid + + lambda_magic + trapezoid Session 7: ---------- diff --git a/slides_sources/source/exercises/string_formatting.rst b/slides_sources/source/exercises/string_formatting.rst index 84261f1d..76f4cfe6 100644 --- a/slides_sources/source/exercises/string_formatting.rst +++ b/slides_sources/source/exercises/string_formatting.rst @@ -46,3 +46,4 @@ Trick: You can pass in a tuple of values to a function with a ``*``:: In [53]: "the first 3 numbers are: {:d}, {:d}, {:d}".format(*t) Out[53]: 'the first 3 numbers are: 1, 2, 3' + diff --git a/slides_sources/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst index 7b00e0f8..338ec591 100644 --- a/slides_sources/source/exercises/trapezoid.rst +++ b/slides_sources/source/exercises/trapezoid.rst @@ -1,7 +1,7 @@ .. _exercise_trapezoidal_rule: ***************** -Trapazoidal Rule +Trapezoidal Rule ***************** Passing functions around @@ -56,25 +56,27 @@ The Solution: Your function definition should look like: -def trapz(fun, a, b): - """ - Compute the area under the curve defined by - y = fun(x), for x between a and b +.. 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 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 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 + :param b: the end point for the integration + :type b: a numeric value + """ + pass In the function, you want to compute the following equation: - .. math:: +.. math:: result = \frac{b-a}{2N}(f(x_1) + 2f(x_2) + 2f(x_3) + 2f(x_4) + \dotsb + 2f(x_N) + f(x_{N+1})). @@ -125,6 +127,7 @@ So you can do: or .. code-block:: python + coef = {'a':1, 'b':3, 'c': 2} trapz(quadratic, 2, 20, **coef) diff --git a/slides_sources/source/session06.rst b/slides_sources/source/session06.rst index 9a815d56..4912fb27 100644 --- a/slides_sources/source/session06.rst +++ b/slides_sources/source/session06.rst @@ -3,13 +3,6 @@ Session Six: Advanced Argument Passing, lambda, functions as objects ******************************************************************** -.. rst-class:: left medium - - Advanced Argument passing - - Lambda - - Functions as objects ====================== Lightning Talks Today: @@ -80,14 +73,29 @@ 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:: + +Apropos of today's topics: + +Python functions are objects, so if you dont call them, you do'nt get an error, you jsut get the function object, ususally not what you want:: + + elif donor_name.lower == "exit": + +this is comparing the string ``lower`` method to the string "exit" and theyare never going to be equal! + +That should be:: + + 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 ============================ -We did some of this last class -- but i want to really drive it home :-) +We did some of this last class -- but I want to really drive it home :-) -In ``Examples/Session06/`` +In ``Examples/Session06/test_cigar_party.py`` ========================= @@ -450,7 +458,7 @@ Content of function can only be an expression -- not a statement Anyone remember what the difference is? -Called "Anonymous": it doesn't need a name. +Called "Anonymous": it doesn't get a name. .. nextslide:: @@ -493,6 +501,10 @@ You can do that with "regular" functions too: Functional Programming ====================== +No real consensus about what that means. + +But there are some "classic" methods available in Python. + map --- @@ -520,7 +532,7 @@ filter ------ ``filter`` "filters" a sequence of objects with a boolean function -- -It keeps only those for which the function is True +It keeps only those for which the function is True -- filtering our the rest. To get only the even numbers: @@ -540,7 +552,6 @@ If you pass ``None`` to ``filter()``, you get only items that evaluate to true: Out[2]: [1, 2.3, 'text', [1, 2], True] - reduce ------ @@ -567,7 +578,6 @@ or .. code-block:: ipython In [13]: import operator - In [14]: reduce(operator.mul, l) Out[14]: 20160 @@ -612,7 +622,24 @@ And "map-reduce" is a big concept these days for parallel processing of "Big Dat A bit more about lambda ------------------------ -Can also use keyword arguments +It is very useful for specifying sorting as well: + +.. code-block:: ipython + + In [55]: lst = [("Chris","Barker"), ("Fred", "Jones"), ("Zola", "Adams")] + + In [56]: lst.sort() + + In [57]: lst + Out[57]: [('Chris', 'Barker'), ('Fred', 'Jones'), ('Zola', 'Adams')] + + In [58]: lst.sort(key=lambda x: x[1]) + + In [59]: lst + Out[59]: [('Zola', 'Adams'), ('Chris', 'Barker'), ('Fred', 'Jones')] + +lambda in keyword arguments +---------------------------- .. code-block:: ipython @@ -632,60 +659,81 @@ Note when the keyword argument is evaluated: this turns out to be very handy! LAB === -lambda and keyword argument magic ------------------------------------ +Here's an exercise to try out some of this: + +:ref:`exercise_lambda_magic` + +Lightning Talk +-------------- -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. +.. rst-class:: medium -Use a for loop, ``lambda``, and a keyword argument +| +| Marc Teale +| -( Extra credit ): +============== +dict as switch +============== -Do it with a list comprehension, instead of a for loop +What to use instead of "switch-case"? -Not clear? here's what you should get +switch-case +----------- -.. nextslide:: Example calling code +A number of languages have a "switch-case" construct:: -.. code-block:: ipython + switch(argument) { + case 0: + return "zero"; + case 1: + return "one"; + case 2: + return "two"; + default: + return "nothing"; + }; - 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. +How do you spell this in python? -Lightning Talk --------------- +``if-elif`` chains +------------------- -.. rst-class:: medium +The obvious way to spell it is a chain of ``elif`` statements: -| -| Marc Teale -| +.. code-block:: python + + if argument == 0: + return "zero" + elif argument == 1: + return "one" + elif argument == 2: + return "two" + else: + return "nothing" + +And there is nothing wrong with that, but.... + +.. nextslide:: + +The ``elif`` chain is neither elegant nor efficient. There are a number of ways to spell it in python -- but one elgant one is to use a dict: + +.. code-block:: python + + arg_dict = {0:"zero", 1:"one", 2: "two"} + dict.get(argument, "nothing") + +Simple, elegant, and fast. + +You can do a dispatch table by putting functions as the value. +Example: Chris' mailroom2 solution. ============================== Closures and function Currying ============================== -Defining specilized functions on the fly +Defining specialized functions on the fly Closures -------- @@ -698,7 +746,7 @@ https://en.wikipedia.org/wiki/Closure_(computer_programming) but I even have trouble following that. -So let's go straight to some examples: +So let's go straight to an example: .. nextslide:: @@ -713,9 +761,107 @@ So let's go straight to some examples: What's going on here? -We have define stored the start ``start_at`` +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) + +=== +LAB +=== + +Let's use some of this ability to use functions a objects for something useful: + +:ref:`exercise_trapezoidal_rule` + +Some reading on these topics: + +http://www.pydanny.com/python-partials-are-fun.html + +https://pymotw.com/2/functools/ +http://www.programiz.com/python-programming/closure +https://www.clear.rice.edu/comp130/12spring/curry/ +======== +Homework +======== +Finish up the Labs diff --git a/slides_sources/source/session07.rst b/slides_sources/source/session07.rst index d47c51d3..43e19af5 100644 --- a/slides_sources/source/session07.rst +++ b/slides_sources/source/session07.rst @@ -46,39 +46,729 @@ Have you all got an HTML Renderer working? Do you have a feel for classes, subclassing, overriding methods, ...? -Personal Project ------------------ -The bulk of the homework for the rest of the class will be a personal project: -* 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 +Lightning Talks Today: +----------------------- + +.. rst-class:: medium + + three + + people's + + names + +=========================== +Object Oriented Programming +=========================== + +Object Oriented Programming +--------------------------- + +More about Python implementation than OO design/strengths/weaknesses + +One reason for this: + +Folks can't even agree on what OO "really" means + +See: The Quarks of Object-Oriented Development + + - Deborah J. Armstrong + +http://agp.hx0.ru/oop/quarks.pdf + + +.. nextslide:: + +Is Python a "True" Object-Oriented Language? + +(Doesn't support full encapsulation, doesn't *require* +classes, etc...) + +.. nextslide:: -* Due the Friday after the last class (December 12) +.. rst-class:: center large + + I don't Care! + + +Good software design is about code re-use, clean separation of concerns, +refactorability, testability, etc... + +OO can help with all that, but: + * It doesn't guarantee it + * It can get in the way + +.. nextslide:: + +Python is a Dynamic Language + +That clashes with "pure" OO + +Think in terms of what makes sense for your project + -- not any one paradigm of software design. + + +.. nextslide:: + +So what is "object oriented programming"? | -| By next week, send me a project proposal: can be short and sweet. + "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" + | +http://en.wikipedia.org/wiki/Object-oriented_programming -Lightning Talks Today: ------------------------ +.. nextslide:: + +Even simpler: + + +"Objects are data and the functions that act on them in one place." + +This is the core of "encapsulation" + +In Python: just another namespace. + +.. nextslide:: + +The OO buzzwords: + + * data abstraction + * encapsulation + * modularity + * polymorphism + * inheritance + +Python does all of this, though it doesn't enforce it. + +.. nextslide:: + +You can do OO in C + +(see the GTK+ project) + + +"OO languages" give you some handy tools to make it easier (and safer): + + * polymorphism (duck typing gives you this anyway) + * inheritance + + +.. nextslide:: + +OO is the dominant model for the past couple decades + +You will need to use it: + +- It's a good idea for a lot of problems + +- You'll need to work with OO packages + +(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) + +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 + + The ``class`` statement + + ``class`` creates a new type object: + + .. code-block:: ipython + + In [4]: class C(object): + pass + ...: + In [5]: type(C) + Out[5]: type + + A class is a type -- interesting! + + It is created when the statement is run -- much like ``def`` + + You don't *have* to subclass from ``object``, but you *should* + + (note on "new style" classes) + +Python Classes +-------------- + +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 + +.. nextslide:: + +Basic Structure of a real class: + +.. code-block:: python + + class Point(object): + # everything defined in here is in the class namespace + + def __init__(self, x, y): + self.x = x + self.y = y + + ## 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/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 + +.. 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 + + 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(object): + def a_function(self, x, y): + ... + + +Does this look familiar from C-style procedural programming? + + +.. nextslide:: + +Anything assigned to a ``self.`` attribute is kept in the instance +name space -- ``self`` *is* the instance. + +That's where all the instance-specific data is. + +.. code-block:: python + + 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. + +The class is one namespace, the instance is another. + + +.. code-block:: python + + class Point(object): + size = 4 + color= "red" + ... + def get_color(): + return self.color + >>> p3.get_color() + 'red' + + +class attributes are accessed with ``self`` also. + + +.. nextslide:: + +Typical methods: + +.. code-block:: python + + class Circle(object): + color = "red" + + def __init__(self, diameter): + self.diameter = diameter + + def grow(self, factor=2): + self.diameter = self.diameter * factor + + +Methods take some parameters, manipulate the attributes in ``self``. + +They may or may not return something useful. + +.. 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 +---- + +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`` + +We'll start with a single class, then add some sub-classes +to specialize the behavior + +Details in: + +:ref:`exercise_html_renderer` + + +Let's see if you can do step 1. in class... + + +Lightning Talks +---------------- .. rst-class:: medium - Andrew P Klock +| +| 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. + +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`` + +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:: python + + class Circle(object): + 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(object): + ... + 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. + +""" + +| +| [ThinkPython 18.10] +| +| ( Demo of class vs. instance attributes ) + + +=================== +More on Subclassing +=================== + +Overriding \_\_init\_\_ +----------------------- + +``__init__`` common method to override} + +You often need to call the super class ``__init__`` as well + +.. code-block:: python + + 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) + + + +exception to: "don't change the method signature" rule. + +More subclassing +---------------- +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 + + + 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. + + +When to Subclass +---------------- + +"Is a" relationship: Subclass/inheritance + +"Has a" relationship: Composition + +.. 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 + + +That's about it -- really! + + +Type-Based dispatch +------------------- + +You'll see code that looks like this: + +.. code-block:: python + + if isinstance(other, A_Class): + Do_something_with_other + else: + Do_something_else + + +Usually better to use "duck typing" (polymorphism) + +But when it's called for: + + * ``isinstance()`` + * ``issubclass()`` + +.. nextslide:: + +GvR: "Five Minute Multi- methods in Python": + +http://www.artima.com/weblogs/viewpost.jsp?thread=101605 + +http://www.python.org/getit/releases/2.3/mro/ + +http://python-history.blogspot.com/2010/06/method-resolution-order.html + + +Wrap Up +------- + +Thinking OO in Python: + +Think about what makes sense for your code: + +* Code re-use +* Clean APIs +* ... + +Don't be a slave to what OO is *supposed* to look like. + +Let OO work for you, not *create* work for you + +.. nextslide:: + +OO in Python: + +The Art of Subclassing: *Raymond Hettinger* + +http://pyvideo.org/video/879/the-art-of-subclassing + +"classes are for code re-use -- not creating taxonomies" + +Stop Writing Classes: *Jack Diederich* + +http://pyvideo.org/video/880/stop-writing-classes + +"If your class has only two methods -- and one of them is ``__init__`` +-- you don't need a class" + + +======== +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. + +Write a second version using a comprehension. + +.. nextslide:: Hint + +``sys.argv`` hold the command line arguments the user typed in. If the +user types: + +.. code-block:: bash + + $ python the_script a_file_name + +Then: + +.. code-block:: python + + import sys + filename = sys.argv[1] + +will get ``filename == "a_file_name"`` + + +Html rendering system: +----------------------- + +:ref:`exercise_html_renderer` + +| - Vinay Gupta +You will build an html generator, using: - Ousmane Conde +* A Base Class with a couple methods +* Subclasses overriding class attributes +* Subclasses overriding a method +* Subclasses overriding the ``__init__`` - Salim Hassan Hamed +These are the core OO approaches =================== From 4e4ddc4a3bce088572509cb243a0801bfb01fb5a Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 10 Nov 2015 17:21:50 -0800 Subject: [PATCH 019/129] updated trapezoidal rule exercise --- Examples/Session06/closure.py | 1 + slides_sources/source/exercises/trapezoid.rst | 103 ++++++++++++++++-- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/Examples/Session06/closure.py b/Examples/Session06/closure.py index 2f0b7a8d..a7e7d07d 100644 --- a/Examples/Session06/closure.py +++ b/Examples/Session06/closure.py @@ -21,5 +21,6 @@ def power(base, exponent): return base ** exponent # now some specialized versions: + square = partial(power, exponent=2) cube = partial(power, exponent=3) diff --git a/slides_sources/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst index 338ec591..3c24b7ae 100644 --- a/slides_sources/source/exercises/trapezoid.rst +++ b/slides_sources/source/exercises/trapezoid.rst @@ -20,11 +20,17 @@ Passing functions around Trapezoidal rule ---------------- -The "trapezoidal rule": +.. rst-class:: medium -https://en.wikipedia.org/wiki/Trapezoidal_rule + The "trapezoidal rule": -Is one of the easiest "quadrature" methods. otherwise known as computing a definite integral, or, simply, computing the area under a curve. + 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 -------- @@ -34,8 +40,10 @@ Your task is to write a ``trapz()`` function that will compute the area under an 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 -.. code-block::python def line(x): '''a very simple straight horizontal line''' return 3 @@ -74,18 +82,76 @@ Your function definition should look like: """ pass +.. nextslide:: + In the function, you want to compute the following equation: .. math:: - result = \frac{b-a}{2N}(f(x_1) + 2f(x_2) + 2f(x_3) + 2f(x_4) + \dotsb + 2f(x_N) + f(x_{N+1})). + result = \frac{b-a}{2N}(f(x_1) + 2f(x_2) + 2f(x_3) + 2f(x_4) + \dotsb + 2f(x_N) + f(x_{N+1})) + +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. -So you will need to create a list of x values from a to b (maybe 100 or so values to start), and compute the function for each of those values, then add them all up, multiply by the half of the difference between a and b. +Note that the first and last values are not doubled. + +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: + +A simple horizontal line -- see above. + +A sloped straight line: + +.. math:: + + \int_a^b y = mx + B = \frac{1}{2} m (b^2-a^2) + B (b-a) + +The sine function: + +.. math:: + + \int_a^b \sin(x) = -\cos(b) + \cos(a) + +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/Session06/test_trapz.py`` + + + Stage two: ---------- @@ -93,7 +159,7 @@ Some functions need extra parameters to do their thing. But the above will only .. math:: - y = a x^2 + bx + c + y = a x^2 + bx + c Requires values for a, b, and c in order to compute y from an given x. @@ -101,17 +167,23 @@ 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 + 4 + def quad1(x): + return 3 + x**2 + 2 + 4 + +But then you need to write a new function for any value of these parameters you might need. -But then you need to write a new function for any value of these parameters you might need. Instead, you can pass in a, b and c each time: +.. 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. +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: -------------------------- @@ -126,17 +198,24 @@ So you can do: 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) + 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. +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. From 9810fa793eaf4ea0eb86634c2827893e9fd9dd92 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 10 Nov 2015 17:46:30 -0800 Subject: [PATCH 020/129] added lambda magic --- .../source/exercises/lambda_magic.rst | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 slides_sources/source/exercises/lambda_magic.rst diff --git a/slides_sources/source/exercises/lambda_magic.rst b/slides_sources/source/exercises/lambda_magic.rst new file mode 100644 index 00000000..1c288a80 --- /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/Session06 + + From 16957fda753a7131e7c930d280eb227a323deb94 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sat, 14 Nov 2015 00:00:36 -0800 Subject: [PATCH 021/129] updated the trapezoid excercise --- slides_sources/ToDo.txt | 9 +++- slides_sources/source/exercises/trapezoid.rst | 51 ++++++++++++------- 2 files changed, 41 insertions(+), 19 deletions(-) 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/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst index 3c24b7ae..5b1e9bd9 100644 --- a/slides_sources/source/exercises/trapezoid.rst +++ b/slides_sources/source/exercises/trapezoid.rst @@ -45,8 +45,8 @@ Example: .. code-block:: python def line(x): - '''a very simple straight horizontal line''' - return 3 + '''a very simple straight horizontal line at y = 5''' + return 5 area = trapz(line, 0, 10) @@ -88,7 +88,7 @@ In the function, you want to compute the following equation: .. math:: - result = \frac{b-a}{2N}(f(x_1) + 2f(x_2) + 2f(x_3) + 2f(x_4) + \dotsb + 2f(x_N) + f(x_{N+1})) + 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: @@ -98,9 +98,15 @@ So you will need to: - add them all up - - multiply by the half of the difference between a and b. + - multiply by the half of the difference between a and b divided by the number of steps. -Note that the first and last values are not doubled. +.. 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? @@ -117,21 +123,30 @@ tests Do this using test-drive development. -A few examples: +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(b) + \cos(a) + \int_a^b \sin(x) = \cos(a) - \cos(b) Computational Accuracy ---------------------- @@ -159,27 +174,27 @@ Some functions need extra parameters to do their thing. But the above will only .. math:: - y = a x^2 + bx + c + y = A x^2 + Bx + C -Requires values for a, b, and c in order to compute y from an given x. +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: +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 + 4 + 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: +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 + def quadratic(x, A=0, B=0, C=0): + return A * x**2 + B * x + C Nice and general purpose. @@ -194,19 +209,19 @@ So you can do: .. code-block:: python - trapz(quadratic, 2, 20, a=1, b=3, c=2) + trapz(quadratic, 2, 20, A=1, B=3, C=2) or .. code-block:: python - trapz(quadratic, 2, 20, 1, 3, c=2) + trapz(quadratic, 2, 20, 1, 3, C=2) or .. code-block:: python - coef = {'a':1, 'b':3, 'c': 2} + coef = {'A':1, 'B':3, 'C': 2} trapz(quadratic, 2, 20, **coef) @@ -215,7 +230,7 @@ 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. +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. From fbc416605289b6763fb19d1409fe0e47a38a305a Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Sun, 15 Nov 2015 11:58:00 -0800 Subject: [PATCH 022/129] added a bit more explaination to trapezoidal rule exercise --- slides_sources/source/exercises/trapezoid.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/slides_sources/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst index 5b1e9bd9..0b83f4b7 100644 --- a/slides_sources/source/exercises/trapezoid.rst +++ b/slides_sources/source/exercises/trapezoid.rst @@ -198,7 +198,7 @@ Instead, you can pass in A, B and C each time: 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. +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: -------------------------- @@ -224,6 +224,12 @@ or coef = {'A':1, 'B':3, 'C': 2} trapz(quadratic, 2, 20, **coef) +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 last class -- how can you both accept and pass arbitrary arguments to/from a function? + Currying -------- From 3d8e86af5d6d80c4b83d970bc6e8e7ad96b93742 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Mon, 16 Nov 2015 19:35:12 -0800 Subject: [PATCH 023/129] added some links to the git tutorial --- slides_sources/source/exercises/trapezoid.rst | 27 ++++++++++++++++-- .../source/supplements/git_overview.rst | 28 ++++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/slides_sources/source/exercises/trapezoid.rst b/slides_sources/source/exercises/trapezoid.rst index 0b83f4b7..f95230d6 100644 --- a/slides_sources/source/exercises/trapezoid.rst +++ b/slides_sources/source/exercises/trapezoid.rst @@ -198,7 +198,9 @@ Instead, you can pass in A, B and C each time: 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. +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: -------------------------- @@ -224,12 +226,31 @@ or coef = {'A':1, 'B':3, 'C': 2} trapz(quadratic, 2, 20, **coef) -NOTE: Make sure this will work with ANY function, with ANY additional positional or keyword arguments -- not just this particular function. +.. 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 last class -- 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 -------- @@ -254,6 +275,8 @@ How accurate it is depends on how small the chunks are that you break the functi 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/supplements/git_overview.rst b/slides_sources/source/supplements/git_overview.rst index bd8fe302..8b822a8d 100644 --- a/slides_sources/source/supplements/git_overview.rst +++ b/slides_sources/source/supplements/git_overview.rst @@ -1,5 +1,12 @@ .. _supplement_git_states: +============ +git Overview +============ + +A Graphical Tutorial +==================== + A Picture of git ---------------- @@ -134,7 +141,7 @@ You can use this to switch between branches and make changes in isolation. Branching allows you to keep related sets of work separate from each-other. - In class here, you can use it to do your excercises for each session. + In class here, you can use it to do your exercises for each session. Simply create a new branch for each session from your repository master branch. @@ -197,3 +204,22 @@ code in any language and will cause errors. Once a conflict is resolved, you can ``git add`` the file back and then commit the merge. + +Other Resources +=============== + +.. rst-class:: left + Here are few good places to look for more info about using git: + + + **Pro git** + + The semi-offical documentation -- the first few chapters are worth going through: + + https://git-scm.com/book/en + + ** git Branching** + + Interactive tutorial about branching -- try it right in the browser! + + http://pcottle.github.io/learnGitBranching/ From 0542297f8c25992d7552b3330a402e2216700ef4 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Mon, 16 Nov 2015 23:52:59 -0800 Subject: [PATCH 024/129] updated Examples for py3 --- Examples/Session07/circle.py | 2 +- Examples/Session07/class.py | 6 +-- Examples/Session07/class_demo.py | 2 +- Examples/Session07/class_method.py | 10 ++--- Examples/Session07/properties_example.py | 4 +- Examples/Session07/simple_classes.py | 8 ++-- Examples/Session07/static_method.py | 6 +-- Examples/Session07/vector.py | 42 +++++++++--------- Syllabus.rst | 15 +++---- slides_sources/source/session07.rst | 54 ++++++++++++++++-------- 10 files changed, 82 insertions(+), 67 deletions(-) diff --git a/Examples/Session07/circle.py b/Examples/Session07/circle.py index a6545632..2f5563f2 100644 --- a/Examples/Session07/circle.py +++ b/Examples/Session07/circle.py @@ -6,5 +6,5 @@ import math -class Circle(object): +class Circle: pass diff --git a/Examples/Session07/class.py b/Examples/Session07/class.py index 1c131142..ac9a99b4 100644 --- a/Examples/Session07/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/Session07/class_demo.py b/Examples/Session07/class_demo.py index 33841a88..e66590d1 100644 --- a/Examples/Session07/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/Session07/class_method.py b/Examples/Session07/class_method.py index 1d893b94..93fba91f 100644 --- a/Examples/Session07/class_method.py +++ b/Examples/Session07/class_method.py @@ -5,14 +5,14 @@ """ -class C(object): +class C: def __init__(self, x, y): self.x = x self.y = y @classmethod def a_class_method(cls, y): - print "in a_class_method", cls + print("in a_class_method", cls) return cls(y, y**2) @@ -23,10 +23,10 @@ class C2(C): if __name__ == "__main__": c = C(3, 4) - print type(c), c.x, c.y + print(type(c), c.x, c.y) c2 = C.a_class_method(3) - print type(c2), c2.x, c2.y + print(type(c2), c2.x, c2.y) c3 = c2.a_class_method(2) - print type(c3), c3.x, c3.y + print(type(c3), c3.x, c3.y) diff --git a/Examples/Session07/properties_example.py b/Examples/Session07/properties_example.py index fa9c732e..4db1f814 100644 --- a/Examples/Session07/properties_example.py +++ b/Examples/Session07/properties_example.py @@ -8,7 +8,7 @@ """ -class C(object): +class C: def __init__(self): self._x = None @property @@ -24,5 +24,5 @@ def x(self): if __name__ == "__main__": c = C() c.x = 5 - print c.x + print(c.x) diff --git a/Examples/Session07/simple_classes.py b/Examples/Session07/simple_classes.py index 0cf80bbc..267f0d5e 100644 --- a/Examples/Session07/simple_classes.py +++ b/Examples/Session07/simple_classes.py @@ -9,7 +9,7 @@ # create a point class -class Point(object): +class Point: def __init__(self, x, y): self.x = x self.y = y @@ -22,7 +22,7 @@ def __init__(self, x, y): print("p.y is:", p.y) -class Point2(object): +class Point2: size = 4 color = "red" @@ -35,7 +35,7 @@ def __init__(self, x, y): print(p2.color) -class Point3(object): +class Point3: size = 4 color = "red" @@ -52,7 +52,7 @@ def get_color(self): print(p3.get_color()) -class Circle(object): +class Circle: color = "red" styles = ['dashed'] diff --git a/Examples/Session07/static_method.py b/Examples/Session07/static_method.py index 6865408c..ce476ab0 100644 --- a/Examples/Session07/static_method.py +++ b/Examples/Session07/static_method.py @@ -5,7 +5,7 @@ """ -class C(object): +class C: @staticmethod def a_static_method(a, b): @@ -13,7 +13,7 @@ def a_static_method(a, b): return a+b def test(self): - return self.a_static_method(2,3) + return self.a_static_method(2, 3) # if __name__ == "__main__": @@ -24,5 +24,3 @@ def test(self): # print c.a_static_method(4,5) # print c.test() - - diff --git a/Examples/Session07/vector.py b/Examples/Session07/vector.py index 56ee2404..8d9e2eac 100644 --- a/Examples/Session07/vector.py +++ b/Examples/Session07/vector.py @@ -1,47 +1,47 @@ """ -Vector type with +, * redefined as vector addition and dot product +Vector type with +, * redefined as Vector addition and dot product From Jon Jacky's Intro to Python course: http://staff.washington.edu/jon/python-course/ """ -class vector(list): +class Vector(list): def __repr__(self): """ String representation, uses list (superclass) representation """ - return 'vector(%s)' % super(vector, self).__repr__() + return 'Vector(%s)' % super(Vector, self).__repr__() def __add__(self, v): """ - redefine + as element-wise vector sum + redefine + as element-wise Vector sum """ assert len(self) == len(v) - return vector([x1 + x2 for x1, x2 in zip(self, v)]) + return Vector([x1 + x2 for x1, x2 in zip(self, v)]) def __mul__(self, v): """ - redefine * as vector dot product + redefine * as Vector dot product """ assert len(self) == len(v) return sum([x1 * x2 for x1, x2 in zip(self, v)]) l1 = [1, 2, 3] l2 = [4, 5, 6] -v1 = vector(l1) -v2 = vector(l2) +v1 = Vector(l1) +v2 = Vector(l2) if __name__ == '__main__': - print 'l1' - print l1 - print 'l1 + l2' - print l1 + l2 - # print l1 * l2 # TypeError - print 'zip(l1, l2)' - print zip(l1, l2) - print 'v1' - print v1 - print 'v1 + v2' - print v1 + v2 - print 'v1 * v2' - print v1 * v2 + print('l1') + print(l1) + print('l1 + l2') + print(l1 + l2) + # print(l1 * l2) # TypeError + print('zip(l1, l2)') + print(zip(l1, l2)) + print('v1') + print(v1) + print('v1 + v2') + print(v1 + v2) + print('v1 * v2') + print(v1 * v2) diff --git a/Syllabus.rst b/Syllabus.rst index 96175504..a520323d 100644 --- a/Syllabus.rst +++ b/Syllabus.rst @@ -286,6 +286,7 @@ Testing List and Dict Comprehensions + Week 6: November 10 .................... @@ -294,36 +295,34 @@ Advanced Argument passing Lambda and Functions as Objects - Week 7: November 17 ....................... Object Oriented Programming: classes, instances, and methods -More OO -- Multiple inheritance, Properties, special methods - Week 8: November 24 .................... +More OO -- Multiple inheritance, Properties, special methods. -More OO -- Emulating built-in types - -Iterators and Generators +Emulating built-in types Week 9: December 1 ................... +Iterators and Generators + Decorators Context Managers -Packages and packaging - Week 10: December 8 .................... +Packages and packaging + Unicode Wrap Up / Students Code review diff --git a/slides_sources/source/session07.rst b/slides_sources/source/session07.rst index 43e19af5..4e83d618 100644 --- a/slides_sources/source/session07.rst +++ b/slides_sources/source/session07.rst @@ -2,9 +2,9 @@ .. Foundations 2: Python slides file, created by hieroglyph-quickstart on Wed Apr 2 18:42:06 2014. -*********************** -Session Seven: More OO -*********************** +*************************** +Object Oriented Programming +*************************** .. rst-class:: medium centered @@ -42,10 +42,32 @@ Homework review Homework Questions? -Have you all got an HTML Renderer working? +Did you all get a trapedzoidal rule function working? Do you have a feel for classes, subclassing, overriding methods, ...? +Notes on Floating point +----------------------- + +Did anyone look at the isclose() function? + +How to make a range of numbers in floating point? + +Anyone do something like this?: + +.. code-block:: python + + s = [] + x = a + while x <= b: + s.append(x) + x += delta_x + + -- see my solution. + +Some notes about FP issues: + +https://docs.python.org/3.5/tutorial/floatingpoint.html Lightning Talks Today: @@ -53,11 +75,11 @@ Lightning Talks Today: .. rst-class:: medium - three + Eric Vegors - people's + Ian Cote - names + Masako Tebbetts =========================== Object Oriented Programming @@ -210,7 +232,7 @@ Python Classes .. code-block:: ipython - In [4]: class C(object): + In [4]: class C: pass ...: In [5]: type(C) @@ -220,10 +242,6 @@ Python Classes It is created when the statement is run -- much like ``def`` - You don't *have* to subclass from ``object``, but you *should* - - (note on "new style" classes) - Python Classes -------------- @@ -231,7 +249,7 @@ About the simplest class you can write .. code-block:: python - >>> class Point(object): + >>> class Point: ... x = 1 ... y = 2 >>> Point @@ -250,7 +268,7 @@ Basic Structure of a real class: .. code-block:: python - class Point(object): + class Point: # everything defined in here is in the class namespace def __init__(self, x, y): @@ -265,7 +283,7 @@ Basic Structure of a real class: print("p.y is:", p.y) -see: ``Examples/Session06/simple_classes.py`` +see: ``Examples/Session07/simple_classes.py`` .. nextslide:: @@ -1004,7 +1022,7 @@ properties In [28]: c = C() In [30]: c.x = 5 - In [31]: print c.x + In [31]: print(c.x) 5 Now the interface is like simple attribute access! @@ -1185,7 +1203,7 @@ argument ....: x = 2 ....: @classmethod ....: def a_class_method(cls, y): - ....: print "in a class method: ", cls + ....: print("in a class method: ", cls) ....: return y ** cls.x ....: In [42]: Classy.a_class_method(4) @@ -1307,7 +1325,7 @@ try: ``dir(2)`` or ``dir(list)`` Most classes should at lest have these special methods: ``object.__str__``: - Called by the str() built-in function and by the print statement to compute + Called by the str() built-in function and by the print function to compute the *informal* string representation of an object. ``object.__unicode__``: From c98a87796c871d4532e1829cdb79868daae6b20b Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 17 Nov 2015 00:34:59 -0800 Subject: [PATCH 025/129] updating Session 7 notes --- .../Session07/html_render/run_html_render.py | 29 ++- slides_sources/source/session07.rst | 182 +++++------------- 2 files changed, 64 insertions(+), 147 deletions(-) diff --git a/Examples/Session07/html_render/run_html_render.py b/Examples/Session07/html_render/run_html_render.py index 74e5c378..663e5ff8 100644 --- a/Examples/Session07/html_render/run_html_render.py +++ b/Examples/Session07/html_render/run_html_render.py @@ -7,37 +7,38 @@ """ -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() @@ -221,7 +222,3 @@ def render_page(page, filename): # page.append(body) # render_page(page, "test_html_output8.html") - - - - diff --git a/slides_sources/source/session07.rst b/slides_sources/source/session07.rst index 4e83d618..7c7884f5 100644 --- a/slides_sources/source/session07.rst +++ b/slides_sources/source/session07.rst @@ -318,7 +318,7 @@ The instance of the class is passed as the first parameter for every method. .. code-block:: python - class Point(object): + class Point: def a_function(self, x, y): ... @@ -351,10 +351,9 @@ Note: the methods defined by ``def`` are class attributes as well. The class is one namespace, the instance is another. - .. code-block:: python - class Point(object): + class Point: size = 4 color= "red" ... @@ -373,7 +372,7 @@ Typical methods: .. code-block:: python - class Circle(object): + class Circle: color = "red" def __init__(self, diameter): @@ -414,9 +413,7 @@ 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`` +page. We'll start with a single class, then add some sub-classes to specialize the behavior @@ -425,9 +422,7 @@ Details in: :ref:`exercise_html_renderer` - -Let's see if you can do step 1. in class... - +Let's get a start with step 1. in class... Lightning Talks ---------------- @@ -435,10 +430,11 @@ Lightning Talks .. rst-class:: medium | -| Gideon Sylvan +| Eric Vegors | -| Hui Zhang +| Ian Cote | +| Masako Tebbetts ======================= Subclassing/Inheritance @@ -475,8 +471,6 @@ The simplest subclass in Python: ``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 --------------------- @@ -484,7 +478,7 @@ Overriding is as simple as creating a new attribute with the same name: .. code-block:: python - class Circle(object): + class Circle: color = "red" ... @@ -505,7 +499,7 @@ Same thing, but with methods (remember, a method *is* an attribute in python) .. code-block:: python - class Circle(object): + class Circle: ... def grow(self, factor=2): """grows the circle's diameter by factor""" @@ -527,15 +521,9 @@ 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. +Whenever you override a method, the interface of the new method should be the same as the old. It should takethe 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. +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. """ @@ -552,13 +540,13 @@ More on Subclassing Overriding \_\_init\_\_ ----------------------- -``__init__`` common method to override} +``__init__`` common method to override You often need to call the super class ``__init__`` as well .. code-block:: python - class Circle(object): + class Circle: color = "red" def __init__(self, diameter): self.diameter = diameter @@ -569,7 +557,6 @@ You often need to call the super class ``__init__`` as well Circle.__init__(self, diameter) - exception to: "don't change the method signature" rule. More subclassing @@ -578,7 +565,7 @@ You can also call the superclass' other methods: .. code-block:: python - class Circle(object): + class Circle: ... def get_area(self, diameter): return math.pi * (diameter/2.0)**2 @@ -637,7 +624,7 @@ Python looks for it in this order: It can get more complicated... -http://www.python.org/getit/releases/2.3/mro/ +https://www.python.org/download/releases/2.3/mro/ http://python-history.blogspot.com/2010/06/method-resolution-order.html @@ -687,7 +674,7 @@ GvR: "Five Minute Multi- methods in Python": http://www.artima.com/weblogs/viewpost.jsp?thread=101605 -http://www.python.org/getit/releases/2.3/mro/ +https://www.python.org/download/releases/2.3/mro/ http://python-history.blogspot.com/2010/06/method-resolution-order.html @@ -725,55 +712,25 @@ http://pyvideo.org/video/880/stop-writing-classes -- you don't need a class" -======== -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. - -Write a second version using a comprehension. - -.. nextslide:: Hint - -``sys.argv`` hold the command line arguments the user typed in. If the -user types: - -.. code-block:: bash - - $ python the_script a_file_name +Lightning Talks +---------------- -Then: +.. rst-class:: medium -.. code-block:: python +| +| Andrew P Klock +| +| Vinay Gupta +| - import sys - filename = sys.argv[1] -will get ``filename == "a_file_name"`` +=== +LAB +=== +.. rst-class:: left medium -Html rendering system: ------------------------ + * html renderer: let's see how much more we can do! :ref:`exercise_html_renderer` @@ -795,7 +752,7 @@ More on Subclassing .. rst-class:: left - I pointed you to this Video last class: + This is a great talk (yes, I'm repeating): The Art of Subclassing: *Raymond Hettinger* @@ -803,7 +760,6 @@ More on Subclassing If you haven't watched it, It's well worth your time - What's a Subclass For? ---------------------- @@ -883,25 +839,8 @@ Real World Example: `FloatCanvas`_ .. _FloatCanvas: https://github.com/svn2github/wxPython/blob/master/3rdParty/FloatCanvas/floatcanvas/FloatCanvas.py#L485 -.. nextslide:: New-Style Classes - -All the class definitions we've been showing inherit from ``object``. - -This is referred to as a "new style" class. - -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. - -**Always Make New-Style Classes** - -(that is, always subclass from object...) - -The differences are subtle, and may not appear until they jump up to bite you. - - -.. nextslide:: ``super()`` +``super()`` +----------- ``super()``: use it to call a superclass method, rather than explicitly calling the unbound method on the superclass. @@ -921,7 +860,7 @@ You can do: class A(B): def __init__(self, *args, **kwargs) - super(A, self).__init__(*argw, **kwargs) + super().__init__(*argw, **kwargs) ... .. nextslide:: Caveats @@ -942,7 +881,7 @@ https://fuhm.net/super-harmful/ "super() considered super!" -- Raymond Hettinger -http://rhettinger.wordpress.com/2011/05/26/super-considered-super/} +http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ (Both worth reading....) @@ -959,7 +898,7 @@ Properties .. code-block:: ipython - In [5]: class C(object): + In [5]: class C: def __init__(self): self.x = 5 In [6]: c = C() @@ -986,7 +925,7 @@ But what if you need to add behavior later? .. code-block:: ipython - In [5]: class C(object): + In [5]: class C: ...: def __init__(self): ...: self.x = 5 ...: def get_x(self): @@ -1011,7 +950,7 @@ properties .. code-block:: ipython - class C(object): + class C: _x = None @property def x(self): @@ -1055,7 +994,7 @@ You do not need to define a setter. If you don't, you get a "read only" attribut .. code-block:: ipython - In [11]: class D(object): + In [11]: class D(): ....: def __init__(self, x=5): ....: self._x = 5 ....: @property @@ -1081,7 +1020,7 @@ a deleter is well: .. code-block:: ipython - In [11]: class D(object): + In [11]: class D(): ....: def __init__(self, x=5): ....: self._x = 5 ....: @property @@ -1099,8 +1038,9 @@ what you want. [demo: :download:`properties_example.py <../../Examples/Session07/properties_example.py>`] +=== LAB ----- +=== Let's use some of this to build a nice class to represent a Circle. @@ -1108,17 +1048,6 @@ For now, Let's do steps 1-4 of: :ref:`exercise_circle_class` -Lightning Talks ----------------- - -.. rst-class:: medium - -| -| Andrew P Klock -| -| Vinay Gupta -| - ======================== Static and Class Methods @@ -1215,7 +1144,8 @@ argument [demo: :download:`class_method.py <../../Examples/Session07/class_method.py>`] -.. nextslide:: Why? +Why? +---- .. rst-class:: build .. container:: @@ -1236,7 +1166,8 @@ argument in a class method: Out[45]: 64 -.. nextslide:: Alternate Constructors +Alternate Constructors +----------------------- Because of this friendliness to subclassing, class methods are often used to build alternate constructors. @@ -1423,23 +1354,12 @@ Look up the special methods you need and define them. There's more to read about the details of implementing these methods: -* https://docs.python.org/2/reference/datamodel.html#special-method-names +* https://docs.python.org/3.5/reference/datamodel.html#special-method-names * http://www.rafekettler.com/magicmethods.html - -Lightning Talks ----------------- - -.. rst-class:: medium - -| -| Ousmane Conde -| -| Salim Hassan Hamed -| - +=== LAB ----- +=== Let's complete our nifty Circle class: @@ -1452,8 +1372,8 @@ Steps 5-8 of: Homework ======== +Complete your html renderer. + Complete the Circle class Decide what you are going to do for your proejct, and send me a simple proposal. - - From 8f47060d9ab39534ea7d6f31ba922f80e4caec9a Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 17 Nov 2015 17:11:07 -0800 Subject: [PATCH 026/129] updated the html render exercise --- .../source/exercises/html_renderer.rst | 82 ++++++++++++++----- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/slides_sources/source/exercises/html_renderer.rst b/slides_sources/source/exercises/html_renderer.rst index 8074e47b..c2eaed1a 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. -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,22 +69,24 @@ 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. @@ -65,6 +95,14 @@ 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. +.. 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. See: step 1. in ``run_html_render.py`` @@ -72,25 +110,27 @@ 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...). Think about "Duck Typing" and EAFP. -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...). +.. nextslide:: -You should now be able to render a basic web page with an html tag around +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``: @@ -115,7 +155,7 @@ constructor, ie. (``run_html_render.py``) Element("some text content", id="TheList", style="line-height:200%") -( remember ``**kwargs``? ) +html elements can take essentially any attributes -- so you can't hard-cody thes particular ones. ( remember ``**kwargs``? ) The render method will need to be extended to render the attributes properly. @@ -140,13 +180,13 @@ 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__`` From 9eff39f91707a61bca09fea5d75ef43682501339 Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 17 Nov 2015 17:38:10 -0800 Subject: [PATCH 027/129] updated session 7 slides --- slides_sources/source/session07.rst | 566 +++------------------------- slides_sources/source/session08.rst | 495 ++++++++++++++++++++++++ 2 files changed, 542 insertions(+), 519 deletions(-) diff --git a/slides_sources/source/session07.rst b/slides_sources/source/session07.rst index 7c7884f5..3c928574 100644 --- a/slides_sources/source/session07.rst +++ b/slides_sources/source/session07.rst @@ -1,7 +1,4 @@ -.. Foundations 2: Python slides file, created by - hieroglyph-quickstart on Wed Apr 2 18:42:06 2014. - *************************** Object Oriented Programming *************************** @@ -10,13 +7,16 @@ Object Oriented Programming .. container:: - Multiple Inheritance + Classes + + Instances - Properties + Class and instance attributes - Class methods and static methods + Subclassing + + Overriding methods - Special (Magic) Methods ================ Review/Questions @@ -25,17 +25,12 @@ Review/Questions Review of Previous Class ------------------------ -* Object Oriented Programming: - - - classes - - - instances - - - attributes and methods +.. rst-class:: medium + Advanced Argument passing - - subclassing + Lambda - - overriding methods + Functions as Objects Homework review --------------- @@ -44,7 +39,10 @@ Homework Questions? Did you all get a trapedzoidal rule function working? -Do you have a feel for classes, subclassing, overriding methods, ...? +Anyone get the "passing through of arguments"? + +How about the adaptive solutions? + Notes on Floating point ----------------------- @@ -69,6 +67,13 @@ Some notes about FP issues: https://docs.python.org/3.5/tutorial/floatingpoint.html +Code Review +----------- + +Anyone unsatisfied with their solution -- or stuck? + +Let's do a code review! + Lightning Talks Today: ----------------------- @@ -85,6 +90,13 @@ Lightning Talks Today: Object Oriented Programming =========================== +A Core approach to organizing code. + +I'm going to go through this fast. + +So we can get to the actual coding. + + Object Oriented Programming --------------------------- @@ -422,7 +434,10 @@ Details in: :ref:`exercise_html_renderer` -Let's get a start with step 1. in class... +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. + Lightning Talks ---------------- @@ -712,18 +727,6 @@ http://pyvideo.org/video/880/stop-writing-classes -- you don't need a class" -Lightning Talks ----------------- - -.. rst-class:: medium - -| -| Andrew P Klock -| -| Vinay Gupta -| - - === LAB === @@ -734,14 +737,12 @@ LAB :ref:`exercise_html_renderer` -| -You will build an html generator, using: +Now we have a base class, and we can: -* A Base Class with a couple methods -* Subclasses overriding class attributes -* Subclasses overriding a method -* Subclasses overriding the ``__init__`` +* Subclass overriding class attributes +* Subclass overriding a method +* Subclass overriding the ``__init__`` These are the core OO approaches @@ -885,495 +886,22 @@ http://rhettinger.wordpress.com/2011/05/26/super-considered-super/ (Both worth reading....) -========== -Properties -========== - -.. rst-class:: left -.. container:: - - One of the strengths of Python is lack of clutter. - - Attributes are simple and concise: - - .. code-block:: ipython - - 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 - - -Getter and Setters? -------------------- - -But what if you need to add behavior later? - -.. rst-class:: build - -* do some calculation -* check data validity -* keep things in sync - - -.. nextslide:: - -.. code-block:: ipython - - 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 - - - This is ugly and verbose -- `Java`_? - -.. _Java: http://dirtsimple.org/2004/12/python-is-not-java.html - -properties ------------ - -.. code-block:: ipython - - class C: - _x = None - @property - def x(self): - return self._x - @x.setter - def x(self, value): - self._x = value - - In [28]: c = C() - In [30]: c.x = 5 - In [31]: print(c.x) - 5 - -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 - - @property - def x(self): - -means: make a property called x with this as the "getter". - -.. code-block:: python - - @x.setter - def x(self, value): - -means: make the "setter" of the 'x' property this new function - -.. nextslide:: "Read Only" Attributes - -You do not need to define a setter. If you don't, you get a "read only" attribute: - -.. code-block:: ipython - - 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 ---------- - -If you want to do something special when a property is deleted, you can define -a deleter is well: - -.. code-block:: ipython - - 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 - -If you leave this out, the property can't be deleted, which is usually -what you want. - -.. rst-class:: centered - -[demo: :download:`properties_example.py <../../Examples/Session07/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 [36]: class StaticAdder(object): - - ....: @staticmethod - ....: def add(a, b): - ....: return a + b - ....: - - In [37]: StaticAdder.add(3, 6) - Out[37]: 9 - -.. rst-class:: centered - -[demo: :download:`static_method.py <../../Examples/Session07/static_method.py>`] - - -.. nextslide:: Why? - -.. rst-class:: build -.. container:: - - Where are static methods useful? - - 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): - - .. code-block:: python - - 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 - - -Class Methods -------------- - -A class method gets the class object, rather than an instance, as the first -argument - -.. 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 - -.. rst-class:: centered - -[demo: :download:`class_method.py <../../Examples/Session07/class_method.py>`] - - -Why? ----- - -.. rst-class:: build -.. container:: - - Unlike static methods, class methods are quite common. - - They have the advantage of being friendly to subclassing. - - Consider this: - - .. code-block:: ipython - - 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 - - @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 - -(this is actually from the OrderedDict implementation in ``collections.py``) - -See also datetime.datetime.now(), etc.... - -.. nextslide:: Curious? - -Properties, Static Methods and Class Methods are powerful features of Pythons -OO model. - -They are implemented using an underlying structure called *descriptors* - -`Here is a low level look`_ at how the descriptor protocol works. - -The cool part is that this mechanism is available to you, the programmer, as -well. - -.. _Here is a low level look: https://docs.python.org/2/howto/descriptor.html - - -Extra Credit: use a class method to make an alternate constructor that takes -the diameter instead. - -=============== -Special Methods -=============== - -.. rst-class:: left -.. container:: - - Special methods (also called *magic* methods) are the secret sauce to Python's - Duck typing. - - Defining the appropriate special methods in your classes is how you make your - class act like standard classes. - -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 lest 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.__unicode__``: - Called by the unicode() built-in function. This converts an object to an - *informal* unicode representation. - - (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. - - (ideally: ``eval( repr(something) ) == something``) - - -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 - - 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) - -.. nextslide:: The Container Protocol - -Want to make a container type? Here's what you need: - -.. 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) - - -.. nextslide:: An Example - -Each of these methods supports a common Python operation. - -For example, to make '+' work with a sequence type in a vector-like fashion, -implement ``__add__``: - -.. code-block:: python - - 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 - -[a more complete example may be seen :download:`here <./supplements/vector.py>`] - - - -.. nextslide:: Summary - -Use special methods when you want your class to act like a "standard" class in -some way. - -Look up the special methods you need and define them. - -There's more to read about the details of implementing these methods: - -* https://docs.python.org/3.5/reference/datamodel.html#special-method-names -* http://www.rafekettler.com/magicmethods.html +======== +Homework +======== -=== -LAB -=== +Complete your html renderer. -Let's complete our nifty Circle class: +Watch those videos: -Steps 5-8 of: +Python class toolkit: *Raymond Hettinger* -- https://youtu.be/HTLu2DFOdTg -:ref:`exercise_circle_class` +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 -======== -Homework -======== +Stop Writing Classes: *Jack Diederich* -- http://pyvideo.org/video/880/stop-writing-classes -Complete your html renderer. +Read up on super() -Complete the Circle class -Decide what you are going to do for your proejct, and send me a simple proposal. diff --git a/slides_sources/source/session08.rst b/slides_sources/source/session08.rst index 9fb1d07e..a945a22a 100644 --- a/slides_sources/source/session08.rst +++ b/slides_sources/source/session08.rst @@ -2,6 +2,501 @@ Session Eight: Callable classes, Iterators, Generators ****************************************************** + +========== +Properties +========== + +.. rst-class:: left +.. container:: + + One of the strengths of Python is lack of clutter. + + Attributes are simple and concise: + + .. code-block:: ipython + + 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 + + +Getter and Setters? +------------------- + +But what if you need to add behavior later? + +.. rst-class:: build + +* do some calculation +* check data validity +* keep things in sync + + +.. nextslide:: + +.. code-block:: ipython + + 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 + + + This is ugly and verbose -- `Java`_? + +.. _Java: http://dirtsimple.org/2004/12/python-is-not-java.html + +properties +----------- + +.. code-block:: ipython + + class C: + _x = None + @property + def x(self): + return self._x + @x.setter + def x(self, value): + self._x = value + + In [28]: c = C() + In [30]: c.x = 5 + In [31]: print(c.x) + 5 + +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 + + @property + def x(self): + +means: make a property called x with this as the "getter". + +.. code-block:: python + + @x.setter + def x(self, value): + +means: make the "setter" of the 'x' property this new function + +.. nextslide:: "Read Only" Attributes + +You do not need to define a setter. If you don't, you get a "read only" attribute: + +.. code-block:: ipython + + 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 +--------- + +If you want to do something special when a property is deleted, you can define +a deleter is well: + +.. code-block:: ipython + + 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 + +If you leave this out, the property can't be deleted, which is usually +what you want. + +.. rst-class:: centered + +[demo: :download:`properties_example.py <../../Examples/Session07/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 [36]: class StaticAdder(object): + + ....: @staticmethod + ....: def add(a, b): + ....: return a + b + ....: + + In [37]: StaticAdder.add(3, 6) + Out[37]: 9 + +.. rst-class:: centered + +[demo: :download:`static_method.py <../../Examples/Session07/static_method.py>`] + + +.. nextslide:: Why? + +.. rst-class:: build +.. container:: + + Where are static methods useful? + + 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): + + .. code-block:: python + + 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 + + +Class Methods +------------- + +A class method gets the class object, rather than an instance, as the first +argument + +.. 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 + +.. rst-class:: centered + +[demo: :download:`class_method.py <../../Examples/Session07/class_method.py>`] + + +Why? +---- + +.. rst-class:: build +.. container:: + + Unlike static methods, class methods are quite common. + + They have the advantage of being friendly to subclassing. + + Consider this: + + .. code-block:: ipython + + 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 + + @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 + +(this is actually from the OrderedDict implementation in ``collections.py``) + +See also datetime.datetime.now(), etc.... + +.. nextslide:: Curious? + +Properties, Static Methods and Class Methods are powerful features of Pythons +OO model. + +They are implemented using an underlying structure called *descriptors* + +`Here is a low level look`_ at how the descriptor protocol works. + +The cool part is that this mechanism is available to you, the programmer, as +well. + +.. _Here is a low level look: https://docs.python.org/2/howto/descriptor.html + + +Extra Credit: use a class method to make an alternate constructor that takes +the diameter instead. + +=============== +Special Methods +=============== + +.. rst-class:: left +.. container:: + + Special methods (also called *magic* methods) are the secret sauce to Python's + Duck typing. + + Defining the appropriate special methods in your classes is how you make your + class act like standard classes. + +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 lest 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.__unicode__``: + Called by the unicode() built-in function. This converts an object to an + *informal* unicode representation. + + (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. + + (ideally: ``eval( repr(something) ) == something``) + + +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 + + 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) + +.. nextslide:: The Container Protocol + +Want to make a container type? Here's what you need: + +.. 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) + + +.. nextslide:: An Example + +Each of these methods supports a common Python operation. + +For example, to make '+' work with a sequence type in a vector-like fashion, +implement ``__add__``: + +.. code-block:: python + + 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 + +[a more complete example may be seen :download:`here <./supplements/vector.py>`] + + + +.. nextslide:: Summary + +Use special methods when you want your class to act like a "standard" class in +some way. + +Look up the special methods you need and define them. + +There's more to read about the details of implementing these methods: + +* https://docs.python.org/3.5/reference/datamodel.html#special-method-names +* http://www.rafekettler.com/magicmethods.html + +=== +LAB +=== + +Let's complete our nifty Circle class: + +Steps 5-8 of: + +:ref:`exercise_circle_class` + + +======== +Homework +======== + +Complete your html renderer. + +Complete the Circle class + +Decide what you are going to do for your proejct, and send me a simple proposal. + + .. rst-class:: large centered The tools of Pythonicity From 819fb31417a6d61da654f3cd220f7ce5e6bcd1ac Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 17 Nov 2015 17:48:59 -0800 Subject: [PATCH 028/129] added the html_render sample output. --- .../html_render/test_html_output1.html | 5 ++++ .../html_render/test_html_output2.html | 11 ++++++++ .../html_render/test_html_output3.html | 14 ++++++++++ .../html_render/test_html_output4.html | 11 ++++++++ .../html_render/test_html_output5.html | 12 +++++++++ .../html_render/test_html_output6.html | 15 +++++++++++ .../html_render/test_html_output7.html | 26 ++++++++++++++++++ .../html_render/test_html_output8.html | 27 +++++++++++++++++++ 8 files changed, 121 insertions(+) create mode 100644 Examples/Session07/html_render/test_html_output1.html create mode 100644 Examples/Session07/html_render/test_html_output2.html create mode 100644 Examples/Session07/html_render/test_html_output3.html create mode 100644 Examples/Session07/html_render/test_html_output4.html create mode 100644 Examples/Session07/html_render/test_html_output5.html create mode 100644 Examples/Session07/html_render/test_html_output6.html create mode 100644 Examples/Session07/html_render/test_html_output7.html create mode 100644 Examples/Session07/html_render/test_html_output8.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 +

+
+
    +
  • + The first item in a list +
  • +
  • + This is the second item +
  • +
  • + And this is a + link + to google +
  • +
+ + \ No newline at end of file diff --git a/Examples/Session07/html_render/test_html_output8.html b/Examples/Session07/html_render/test_html_output8.html new file mode 100644 index 00000000..3e2f249b --- /dev/null +++ b/Examples/Session07/html_render/test_html_output8.html @@ -0,0 +1,27 @@ + + + + + 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 +

+
+
    +
  • + The first item in a list +
  • +
  • + This is the second item +
  • +
  • + And this is a + link + to google +
  • +
+ + \ No newline at end of file From d40765c43dd111d543b7809d224ed67787ed86ef Mon Sep 17 00:00:00 2001 From: Chris Barker Date: Tue, 24 Nov 2015 00:08:04 -0800 Subject: [PATCH 029/129] updating Session 8 presentation -- more to do! --- Examples/Session01/schedule.txt | 3 +- Examples/{Session07 => Session08}/circle.py | 0 .../{Session07 => Session08}/class_method.py | 0 .../properties_example.py | 0 .../{Session07 => Session08}/static_method.py | 0 Examples/{Session07 => Session08}/vector.py | 0 .../{Session08 => Session09}/iterator_1.py | 0 Examples/{Session08 => Session09}/my_for.py | 0 .../test_generator.py | 0 .../{Session08 => Session09}/yield_example.py | 0 Syllabus.rst | 14 +- .../source/exercises/html_renderer.rst | 201 ++++++++++++++++-- slides_sources/source/session08.rst | 188 ++++++++-------- 13 files changed, 285 insertions(+), 121 deletions(-) rename Examples/{Session07 => Session08}/circle.py (100%) rename Examples/{Session07 => Session08}/class_method.py (100%) rename Examples/{Session07 => Session08}/properties_example.py (100%) rename Examples/{Session07 => Session08}/static_method.py (100%) rename Examples/{Session07 => Session08}/vector.py (100%) rename Examples/{Session08 => Session09}/iterator_1.py (100%) rename Examples/{Session08 => Session09}/my_for.py (100%) rename Examples/{Session08 => Session09}/test_generator.py (100%) rename Examples/{Session08 => Session09}/yield_example.py (100%) diff --git a/Examples/Session01/schedule.txt b/Examples/Session01/schedule.txt index 9d7c5ee7..2325408f 100644 --- a/Examples/Session01/schedule.txt +++ b/Examples/Session01/schedule.txt @@ -16,10 +16,11 @@ week 6: Marc Teale week 7: Eric Starr Vegors week 7: Ian Cote week 7: Masako Tebbetts -week 8: Kathleen Devlin +week 8: Kathleen Devlin (Moved to week 9) week 8: Robert Ryan Leslie week 8: Ryan Morin week 9: Erica Winberry week 9: Robert Jenkins +week 9: Kathleen Devlin week 10: Austin Scara week 10: Marty Pitts diff --git a/Examples/Session07/circle.py b/Examples/Session08/circle.py similarity index 100% rename from Examples/Session07/circle.py rename to Examples/Session08/circle.py diff --git a/Examples/Session07/class_method.py b/Examples/Session08/class_method.py similarity index 100% rename from Examples/Session07/class_method.py rename to Examples/Session08/class_method.py diff --git a/Examples/Session07/properties_example.py b/Examples/Session08/properties_example.py similarity index 100% rename from Examples/Session07/properties_example.py rename to Examples/Session08/properties_example.py diff --git a/Examples/Session07/static_method.py b/Examples/Session08/static_method.py similarity index 100% rename from Examples/Session07/static_method.py rename to Examples/Session08/static_method.py diff --git a/Examples/Session07/vector.py b/Examples/Session08/vector.py similarity index 100% rename from Examples/Session07/vector.py rename to Examples/Session08/vector.py diff --git a/Examples/Session08/iterator_1.py b/Examples/Session09/iterator_1.py similarity index 100% rename from Examples/Session08/iterator_1.py rename to Examples/Session09/iterator_1.py diff --git a/Examples/Session08/my_for.py b/Examples/Session09/my_for.py similarity index 100% rename from Examples/Session08/my_for.py rename to Examples/Session09/my_for.py diff --git a/Examples/Session08/test_generator.py b/Examples/Session09/test_generator.py similarity index 100% rename from Examples/Session08/test_generator.py rename to Examples/Session09/test_generator.py diff --git a/Examples/Session08/yield_example.py b/Examples/Session09/yield_example.py similarity index 100% rename from Examples/Session08/yield_example.py rename to Examples/Session09/yield_example.py diff --git a/Syllabus.rst b/Syllabus.rst index a520323d..3257c7fe 100644 --- a/Syllabus.rst +++ b/Syllabus.rst @@ -292,7 +292,9 @@ Week 6: November 10 Advanced Argument passing -Lambda and Functions as Objects +Lambda + +Functions as Objects Week 7: November 17 @@ -303,7 +305,7 @@ Object Oriented Programming: classes, instances, and methods Week 8: November 24 .................... -More OO -- Multiple inheritance, Properties, special methods. +More OO: Multiple inheritance, Properties, Special methods. Emulating built-in types @@ -313,17 +315,13 @@ Week 9: December 1 Iterators and Generators -Decorators - -Context Managers - Week 10: December 8 .................... -Packages and packaging +Decorators -Unicode +Context Managers Wrap Up / Students Code review diff --git a/slides_sources/source/exercises/html_renderer.rst b/slides_sources/source/exercises/html_renderer.rst index c2eaed1a..eaa1bf73 100644 --- a/slides_sources/source/exercises/html_renderer.rst +++ b/slides_sources/source/exercises/html_renderer.rst @@ -91,7 +91,7 @@ It should have a ``render(file_out, ind = "")`` method that renders the tag and - 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. @@ -116,12 +116,11 @@ 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. (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...). Think about "Duck Typing" and EAFP. +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. .. 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. +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`` @@ -149,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%") -html elements can take essentially any attributes -- so you can't hard-cody thes particular ones. ( 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. @@ -175,6 +174,10 @@ 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? + See ``test_html_output5.html`` Step 6: @@ -232,6 +235,153 @@ new tags, etc.... See ``test_html_output8.html`` +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 + 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 somethign 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 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(content)) + +This is my favorite. ``html_render_wrap.py`` in Solutions demonstrates with method. + +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. + +.. 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 with catch it, and try to simply write it out instead. So you may get somethign 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 it wil be clear where it is coming from. + HTML Primer ============ @@ -240,13 +390,11 @@ HTML Primer 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. +.. 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 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 ---- @@ -261,15 +409,19 @@ 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 @@ -305,19 +457,19 @@ There can be all sorts of stuff stored in attributes -- some required for specif 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