diff --git a/.gitignore b/.gitignore index ceafc7a..db97649 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__ .pyc _build +data + diff --git a/docs/Makefile b/docs/Makefile index 0aeae65..db2a023 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -175,3 +175,7 @@ pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." + +slides: + $(SPHINXBUILD) -b slides $(ALLSPHINXOPTS) $(BUILDDIR)/slides + @echo "Build finished. The HTML slides are in $(BUILDDIR)/slides." diff --git a/docs/_static/cf_logo.png b/docs/_static/cf_logo.png new file mode 100644 index 0000000..d9daf64 Binary files /dev/null and b/docs/_static/cf_logo.png differ diff --git a/docs/_static/color_git_prompt.png b/docs/_static/color_git_prompt.png new file mode 100644 index 0000000..b85ab44 Binary files /dev/null and b/docs/_static/color_git_prompt.png differ diff --git a/docs/_static/flake8_output.png b/docs/_static/flake8_output.png new file mode 100644 index 0000000..dc44e48 Binary files /dev/null and b/docs/_static/flake8_output.png differ diff --git a/docs/_static/pc_menu.png b/docs/_static/pc_menu.png new file mode 100644 index 0000000..1621fb6 Binary files /dev/null and b/docs/_static/pc_menu.png differ diff --git a/docs/_static/plugin_list.png b/docs/_static/plugin_list.png new file mode 100644 index 0000000..5b206bb Binary files /dev/null and b/docs/_static/plugin_list.png differ diff --git a/docs/_static/python.png b/docs/_static/python.png new file mode 100644 index 0000000..23a4c6e Binary files /dev/null and b/docs/_static/python.png differ diff --git a/docs/_static/simple_prompt.png b/docs/_static/simple_prompt.png new file mode 100644 index 0000000..db8b9d4 Binary files /dev/null and b/docs/_static/simple_prompt.png differ diff --git a/docs/_static/tab_completion.png b/docs/_static/tab_completion.png new file mode 100644 index 0000000..92cf8aa Binary files /dev/null and b/docs/_static/tab_completion.png differ diff --git a/docs/_static/transmogrifier.jpg b/docs/_static/transmogrifier.jpg new file mode 100644 index 0000000..73363de Binary files /dev/null and b/docs/_static/transmogrifier.jpg differ diff --git a/docs/_static/two_line_prompt.png b/docs/_static/two_line_prompt.png new file mode 100644 index 0000000..23d9cac Binary files /dev/null and b/docs/_static/two_line_prompt.png differ diff --git a/docs/_static/virtualenv_prompt.png b/docs/_static/virtualenv_prompt.png new file mode 100644 index 0000000..68902a6 Binary files /dev/null and b/docs/_static/virtualenv_prompt.png differ diff --git a/docs/concepts.rst b/docs/concepts.rst new file mode 100644 index 0000000..600548d --- /dev/null +++ b/docs/concepts.rst @@ -0,0 +1,19 @@ +Concepts and Learning Objectives +================================ + +* How to run the Python interpreter from the command-line + +* REPL (Read-Evaluation-Print-Loop), what they are, how they work +* Values +* Types +* Names +* Name registries (bindings, symbol tables) +* Assignment +* LHS +* RHS +* Learning a language (grammar,vocabulary, idioms) +* Literals +* Literals versus Names +* Debugging +* Remove bindings +* Conditional statements diff --git a/docs/conf.py b/docs/conf.py index 1471e48..088929b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,6 +36,7 @@ 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx', 'IPython.sphinxext.ipython_console_highlighting', 'IPython.sphinxext.ipython_directive', ] @@ -132,7 +133,7 @@ # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +html_logo = "_static/cf_logo.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -272,6 +273,53 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False +# -- Hieroglyph Slide Configuration ------------ + +extensions += [ + 'hieroglyph', +] + +slide_title = "Foundations 2: Python" +slide_theme = 'slides2' +slide_levels = 3 + +# Place custom static assets in the _static directory and uncomment +# the following lines to include them + +slide_theme_options = { + 'subtitle': 'Fundamentals of Python Programming', + 'custom_css': 'custom.css', + # 'custom_js': 'custom.js', + 'presenters': [ + # { + # 'name': u'Christopher Barker', + # 'email': u'PythonCHB@gmail.com', + # 'github': u'https://github.com/PythonCHB', + # 'company': u'' + # }, + # { + # 'name': u'Dan Hable', + # 'email': u'dhable@gmail.com', + # 'github': u'http://github.com/dhable', + # 'company': u'' + # }, + #{ + # 'name': 'Cris Ewing', + # 'twitter': '@crisewing', + # 'www': 'http://crisewing.com', + # 'github': 'http://github.com/cewing', + # 'company': 'Cris Ewing, Developer LLC' + #}, + { + 'name': 'Paul Pham', + 'twitter': '@cryptogoth', + 'github': 'http://github.com/ppham', + }, + ] +} + +# ---------------------------------------------- + # -- Options for Epub output ---------------------------------------------- @@ -341,3 +389,6 @@ # If false, no index is generated. #epub_use_index = True + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/': None} diff --git a/docs/session1.rst b/docs/session1.rst index d6d9560..dc65050 100644 --- a/docs/session1.rst +++ b/docs/session1.rst @@ -1,25 +1,1843 @@ -Session 1: Introductions -======================== - -* 7:00 Introduction to staff -* 7:30 Setting up dev environment -* 8:00 Pair programming, Hello World -* 8:30 Schedule, canvas - -Topics -====== - -* REPL -* Values -* Types -* Names -* Name registries (bindings, symbol tables) -* Assignment -* LHS -* RHS -* Learning a language (grammar,vocabulary, idioms) -* Literals -* Literals versus Names -* Debugging -* Remove bindings -* Conditional statements +************************** +Session One: Introductions +************************** + +| In which you are introduced to this class, your instructors, your environment +| and your new best friend, Python. + +.. image:: /_static/python.png + :align: center + :width: 38% + +.. rst-class:: credit + +`xkcd.com/353`_ + + +.. _xkcd.com/353: http://xkcd.com/353 + +Introductions +============= + +.. rst-class:: center large + +Now let's back up and meet each other. + +Your instructor +--------------- + +.. rst-class:: center large + +| Paul Pham +| @paulpham on Slack +| (paul at codefellows dot com) + +Seattleite for 8 years. +UW alum. +Taught at The Evergreen State College in Olympia. +I like startups and using technology to improve people's lives, +especially through education. + +Your TAs +--------------- + +.. rst-class:: center large + +| Grace Hatamyar +| @ghatamyar + +| Crystal Stellwagen +| @liraeldianne + + +How This Fits into CodeFellows +---------------------------- + +Prerequisites + +* Foundations I: Web Dev with HTML, CSS, Javascript +* Unix & Git for Everyone + +This Class + +* Foundations II: Python + +Where to Go From Here + +* Python 401 next year +(the new Development Accelerator) + +Outline of this Class +--------------------- + +* Session 1: Dev Environment, Python Syntax +* Session 2: Functions, Modules, Booleans +* Session 3: Sequences, Iteration and String Formatting +* Session 4: Dictionaries, Sets, Exceptions, and Files +* Session 5: Arguments, Comprehensions, Lambdas and Functional Programming +* Session 6: Intro to Object Oriented Programming +* Session 7: Testing, More OO +* Session 8: Optional Topics (Generators, Iterators, Decorators, and Context Managers) + +Based on a curriculum designed by + +| Cris Ewing and Chris Barker +| (cris at crisewing dot com) + +Puzzle Given +--------------- + +Every session, you'll be given a puzzle in the form of +a Python program to write. +By the end of class, you'll know everything you need to solve the puzzle. + +Today's puzzle: + +Write a Python program that prints "Hello, World!" if you call it with +no arguments, otherwise prints the correct translation of +"Hello, World!" in whatever language is given as the first argument +to the program. + +[demo] + +Class Meetings +------------- + +* Twice a week for 4 weeks +* 8 total class sessions +* Mondays and Wednesdays, 7-9pm +* The "Easy" + +Office Hours +------------- + +* 6pm before class, in the Easy +* Instructor + all TAs will be here +* Also by appointment with any TA + +Homework +------------- + +* 2-4 homework tasks per class session. +* Overall, about 20 homework tasks. +* Worth 5-10 points each. + +Rubric: + +* 0 points not turned in +* 1 points crashes, major syntax errors. +* 2 points crashes, minor syntax errors. +* 3 points runs, major logical errors +* 4 points runs, with minor logical or style errors +* 5 points, compiles and runs perfectly with good style. + +Grading Policy +------------- + +In order to pass the class: + +* Attend at least 6 out of 8 classes. +* Score 85% of the points in the class total. +* You can resubmit to get more points. +* Late homework will be accepted up to 1 week after the class has ended (July 15) + +Intense, Fast-paced +--------------- + +* Homework is assigned every class, due by the next class. +* You can't afford to miss more than one or two classes. +* It's easy to fall behind on the homework. +* Like learning a foreign language by moving to another country for four weeks. + +Who are you? +------------- + +.. rst-class:: center large + + Time for Python classmate speed-dating. + + +Course Materials Online +======================= + +Where to Find Your Stuff + +GitHub +------ + +There are two repositories in GitHub you will want to bookmark: + +Student Homework Repository: + https://github.com/codefellows/sea-c45-python + + Fork this repository to your own github account and do homework there. + +Course Materials Repository: + https://github.com/ppham/codefellows_f2_python + + Contains lecture material sources, supplemental materials and homework + assignments + + A rendered HTML copy of all these class materials may be found online at + http://codefellows.github.io/sea-c45-python + +Canvas +------ + +We will be using Canvas to track your homework submission. Grades will be +entered here as well: + +https://canvas.instructure.com/courses/961767 + + +Elsewhere +--------- + +Class email list: + Code Fellows provides an email list for us. We will use this list for + announcements. Please make sure that you are receiving the messages sent to + this list: + + sea-c45@codefellows.com + +Class `Slack `_ Channel: + The student repository README contains a link to the class chatroom. You can + sign into the `Codefellow Slack team ` website + or you can download the desktop client for your OS. + + Once you're signed in, join the `#sea-c45-python` channel. + + This is the official communication medium for the class, and where announcements will be made. + + +Introduction to Python +========================== + +.. rst-class:: center large + +Python Programming + +How I Learned Python, and Why I'm Glad I Did +---------------------------- + +All my friends were talking about it. + +I had a new project to do, and complete freedom to choose the technology. + +Python is now a standard tool for numerical and scientific computation. +(e.g. Machine Learning) + +Current and future dream job: +Industrial Light & Magic is hiring Python coders, presumably to work on the +new Star Wars movies. + +What is Python? +--------------- + +.. rst-class:: build + +* Dynamic +* Object oriented +* Byte-compiled +* Interpreted + + +.. nextslide:: + +.. rst-class:: center large + +But what does that mean? + + +Python Features +--------------- + +Features: + +.. rst-class:: build + +* Unlike C, C++, C\#, Java ... More like Ruby, Lisp, Perl, Javascript + ... + +* **Dynamic** -- no type declarations + + * Programs are shorter + * Programs are more flexible + * Less code means fewer bugs + +* **Interpreted** -- no separate compile, build steps - programming process is + simpler + + +What's a Dynamic language +------------------------- + +**Dynamic typing**. + +* Type checking and dispatch happen at run-time + +.. code-block:: ipython + + In [1]: x = a + b + +.. rst-class:: build + +* What is ``a``? +* What is ``b``? +* What does it mean to add them? +* ``a`` and ``b`` can change at any time before this process + +.. nextslide:: + +**Strong typing**. + +.. code-block:: ipython + + In [1]: a = 5 + + In [2]: type(a) + Out[2]: int + + In [3]: b = '5' + + In [4]: type(b) + Out[4]: str + +.. rst-class:: build + +* **everything** has a type. +* the *type* of a thing determines what it can do. + +Duck Typing +----------- + +.. rst-class:: center large + +"If it looks like a duck, and quacks like a duck -- it's probably a duck" + + +.. nextslide:: + +.. rst-class:: center large + +If an object behaves as expected at run-time, it's the right type. + + +Python Versions +--------------- + +Python 2.x + +.. rst-class:: build + +* "Classic" Python +* Evolved from original + +Python 3.x ("py3k") + +.. rst-class:: build + +* Updated version +* Removed the "warts" +* Allowed to break code + + +.. nextslide:: + +This class uses Python 3.x (3.4 is the latest as of this writing) +but we will point out the minor differences with Python 2.7, which +you will see in the wild. + +.. rst-class:: build + +* Adoption of Python 3 is growing fast + + * A few key packages still not supported (https://python3wos.appspot.com/) + * Most code in the wild is still 2.x + +* You *can* learn to write Python that is forward compatible from 2.x to 3.x +* We will be teaching from that perspective. +* If you find yourself needing to work with Python 2 and 3, there are ways to + write compatible code: + + * https://wiki.python.org/moin/PortingPythonToPy3k + * http://python3porting.com (particulary the chapters on modern idioms and + supporting Python 2 and 3) + * http://python-future.org/compatible_idioms.html + +Other Reasons Why Python is Awesome +----------------------------------- + +Keep your eye on the prize! + +* Built-in data types like lists, dictionaries, tuples that access simply by typing the right grouping symbols! `[] {} ()` +* Its father, Guido van Rossum, still hearts it, actively guides its development, +and tweets about how awesome it is. +* It is named after Monty Python, but it also enables a lot of snake puns. +* Short, succinct metaphors and a can-do attitude +* Easy to experiment and play with. Open the interpreter, start messing around. + +Your computer is your own laboratory or viewport into exploring a virtual world. + +Introduction to Your Environment +================================ + +.. rst-class:: Left +.. container:: + + There are three basic elements to your environment when working with Python: + + .. rst-class:: build + + * Your Command Line + * Your Interpreter + * Your Editor + + +Your Command Line (cli) +----------------------- + +Having some facility on the command line is important + +We won't cover this in class, so if you are not comfortable, please bone up at +home. + +I suggest running through the **cli** tutorial at "learn code the hard way": + +`http://cli.learncodethehardway.org/book`_ + +.. _http://cli.learncodethehardway.org/book: http://cli.learncodethehardway.org/book + +You can also read the materials from the Code Fellows Unix & Git workshop: + +`http://github.com/codefellows/sea-w29`_ + +.. _http://cewing.github.io/cf-uge: http://cewing.github.io/cf-uge + + +.. nextslide:: Command Line Enhancements + +There are a few things you can do to help make your command line a better place +to work. + +Part of your homework this week will be to do these things. + +More on this later. + + +Your Interpreter +---------------- + +Python comes with a built-in interpreter. + +You see it when you type ``python`` at the command line: + +.. code-block:: pycon + + $ python3 + Python 3.4.3 (default, Jun 1 2015, 09:58:35) + [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> + +That last thing you see, ``>>>`` is the "Python prompt". + +This is where you type code. + + +.. nextslide:: Python in the Interpreter + +Try it out: + +.. code-block:: pycon + + >>> print(u"hello world!") + hello world! + >>> 4 + 5 + 9 + >>> 2 ** 8 - 1 + 255 + >>> print(u"one string" + u" plus another") + one string plus another + >>> + + +.. nextslide:: Tools in the Interpreter + +When you are in an interpreter, there are a number of tools available to you. + +There is a help system: + +.. code-block:: pycon + + >>> help(str) + Help on class str in module __builtin__: + + class str(basestring) + | str(object='') -> string + | + | Return a nice string representation of the object. + | If the argument is a string, the return value is the same object. + ... + +You can type ``q`` to exit the help viewer. + +.. nextslide:: Tools in the Interpreter + +You can also use the ``dir`` builtin to find out about the attributes of a +given object: + +.. code-block:: pycon + + >>> bob = u"this is a string" + >>> dir(bob) + ['__add__', '__class__', '__contains__', '__delattr__', + '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', + '__getitem__', '__getnewargs__', '__getslice__', '__gt__', + ... + 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', + 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', + 'zfill'] + >>> help(bob.rpartition) + +This allows you quite a bit of latitude in exploring what Python is. + + +.. nextslide:: Advanced Interpreters + +In addition to the built-in interpreter, there are several more advanced +interpreters available to you. + +We'll be using one in this course called ``iPython`` + +More on this soon. + + +Your Editor +----------- + +Typing code in an interpreter is great for exploring. + +But for anything "real", you'll want to save the work you are doing in a more permanent +fashion. + +This is where an Editor fits in. + +.. nextslide:: Text Editors Only + +Any good text editor will do. + +.. rst-class:: build +.. container:: + + MS Word is **not** a text editor. + + Nor is *TextEdit* on a Mac. + + ``Notepad`` is a text editor -- but a crappy one. + + You need a real "programmers text editor" + + A text editor saves only what it shows you, with no special formatting + characters hidden behind the scenes. + +.. nextslide:: Minimum Requirements + + +At a minimum, your editor should have: + +.. rst-class:: build + +* Syntax Colorization +* Automatic Indentation + +In addition, great features to add include: + +.. rst-class:: build + +* Tab completion +* Code linting +* Jump-to-definition +* Interactive follow-along for debugging + +.. rst-class:: build +.. container:: + + Have an editor that does all this? Feel free to use it. + + If not, I suggest ``Sublime Text`` (2 or 3): + + http://www.sublimetext.com/ + + +Setting Up Your Environment +=========================== + +.. rst-class:: centered large + +Shared setup means reduced complications. + + +Our Class Environment +--------------------- + +We are going to work from a common environment in this class. + +We will take the time here in class to get this going. + +This helps to ensure that you will be able to work. + + +Step 1: Python 3.4 +------------------ + +.. rst-class:: large + +You have this already, right? + +.. code-block:: bash + + $ python + Python 3.4.3 (default, Jun 1 2015, 09:58:35) + [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> ^D + $ + +If not: + +* `For the mac <./supplements/python_for_mac.html>`_ +* `For linux <./supplements/python_for_linux.html>`_ +* `For windows <./supplements/python_for_windows.html>`_ + +Step 2: Pip +----------- + +Python comes with quite a bit ("batteries included"). + +Sometimes you need a bit more. + +Pip allows you to install Python packages to expand your system. + +pip comes preinstalled with Python 3.4. + +You can check to see if you have it installed by typing: + +.. code-block:: bash + + $ pip --version + pip 7.0.3 from /usr/local/lib/python3.4/site-packages (python 3.4) + +(or go to: http://pip.readthedocs.org/en/latest/installing.html) + +.. nextslide:: Using Pip + +Once you've installed pip, you use it to install Python packages by name: + +.. code-block:: bash + + $ pip install foobar + ... + +To find packages (and their proper names), you can search the python package +index (PyPI): + +https://pypi.python.org/pypi + + + +Step 3: Optional -- Virtualenv +------------------------------- + +Python packages come in many versions. + +Often you need one version for one project, and a different one for another. + +`Virtualenv`_ allows you to create isolated environments. + +You can then install potentially conflicting software safely. + +For this class, this is no big deal, but as you start to work on "real" +projects, it can be a key tool. + +.. _Virtualenv: http://www.virtualenv.org/ + +If you want to install it, here are some notes: + +`Intro to VirtualEnv <./supplements/virtualenv.html>`_ + + +Step 4: Fork Class Repository +------------------------------ + +`GitHub `_ is an industry-standard system for collaboration on +software projects -- particularly open source ones. + +We will use it this class to manage submitting and reviewing your work, etc. + +**Wait!** Don't have a gitHub account? Set one up now. + +Next, you'll make a copy of the class repository using ``git``. + +The canonical copy is in the CodeFellows organization on GitHub: + +https://github.com/codefellows/sea-c45-python + +Open that URL, and click on the *Fork* button at the top right corner. + +This will make a copy of this repository in *your* github account. + +.. nextslide:: Clone Your Fork + +From here, you'll want to make a clone of your copy on your local machine. + +At your command line, run the following commands: + +.. code-block:: bash + + $ cd your_working_directory_for_the_class + $ git clone https://github.com//sea-c45-python.git + +(you can copy and paste that link from the gitHub page) + +If you have an SSH key set up for gitHub, you'll want to do this instead: + +.. code-block:: bash + + git@github.com:/sea-c45-python.git + +**Remember**, should be replaced by your github account name. + +Brief Aside +----------- + +Remember our puzzle? +Let's go into our recently cloned class repo and see some starter code. + +.. code-block:: bash + + cd examples/session01 + python hello.py + +Now back to show! + +Step 5: Install Requirements +---------------------------- + +As this is an intro class, we are going to use almost entirely features of standand library. But there are a couple things you may want: + +**iPython** + +.. code-block:: bash + + $pip install ipython + +If you are using SublimeText, you may want: + +.. code-block:: bash + + $ pip install PdbSublimeTextSupport + +Introduction to iPython +======================= + +iPython Overview +------------------ + +You have now installed `iPython`_. + +iPython is an advanced Python interpreter that offers enhancements. + +You can read more about it in the `official documentation`_. + +Specifically, you'll want to pay attention to the information about + +`Using iPython for Interactive Work`_. + +.. _iPython: http://ipython.org +.. _official documentation: http://ipython.org/ipython-doc/stable/index.html +.. _Using iPython for Interactive Work: http://ipython.org/ipython-doc/stable/interactive/index.html + +.. ifslides:: + + Let's see a quick demo of what it can do for you. + + +The very basics of iPython +-------------------------- + +iPython can do a lot for you, but for starters, here are the key pieces you'll +want to know: + +Start it up + +.. code-block:: bash + + $ipython + + $ ipython + Python 2.7.6 (v2.7.6:3a1db0d2747e, Nov 10 2013, 00:42:54) + Type "copyright", "credits" or "license" for more information. + + IPython 2.0.0 -- An enhanced Interactive Python. + ? -> Introduction and overview of IPython's features. + %quickref -> Quick reference. + help -> Python's own help system. + object? -> Details about 'object', use 'object??' for extra details. + + +.. ifslides:: + + (live demo) + + +.. nextslide:: iPython basics + +This is the stuff I use every day: + +* command line recall: + + - hit the "up arrow" key + - if you have typed a bit, it will find the last command that starts the same way. + +* basic shell commands: + + - ``ls``, ``cd``, ``pwd`` + +* any shell command: + + - ``! the_shell_command`` + +* pasting from the clipboard: + + - ``%paste`` (this keeps whitespace cleaner for you) + + +.. nextslide:: iPython basics (cont) + +* getting help: + + - ``something?`` + +* tab completion: + + - ``something.`` + +* running a python file: + + - ``run the_name_of_the_file.py`` + + +That's it -- you can get a lot done with those. + +How to run a python file +-------------------------- + +A file with python code in it is a 'module' or 'script' + +(more on the distiction later on...) + +It should be named with the ``.py`` extension: ``some_name.py`` + +To run it, you have a couple options: + +1) call python on the command line, and pass in your module name + +.. code-block:: bash + + $ python the_name_of_the_script.py + +2) run ``iPython``, and run it from within iPython with the ``run`` command + +.. code-block:: ipython + + In [1]: run the_file.py + +.. ifslides:: + + .. rst-class:: centered + + [demo] + + + +Basic Python Syntax +=================== + +.. rst-class:: center mlarge + +| Expressions, Statements, +| Values, Types, and Symbols + + +Code structure +-------------- + +Each line is a piece of code. + +Comments: + +.. code-block:: ipython + + In [3]: # everything after a '#' is a comment + +Expressions: + +.. code-block:: ipython + + In [4]: # evaluating an expression results in a value + + In [5]: 3 + 4 + Out[5]: 7 + +.. nextslide:: + +Statements: + +.. code-block:: ipython + + In [6]: # statements do not return a value, may contain an expression + + In [7]: print(u"this") + this + + In [8]: line_count = 42 + + In [9]: + + +Printing +-------- + +In Python 2.x, printing is a statement. In Python 3, it was changed to a +function. + +.. rst-class:: build +.. container:: + + You can get the Python 3 behavior in Python 2.6+ using the ``__future__`` + module. + + .. code-block:: python + + from __future__ import print_function + + For purposes of writing cross-compatible code, this is a good idea. Please + use this idiom in your code. + +.. nextslide:: + +It's kind of obvious, but handy when playing with code: + +.. code-block:: ipython + + In [1]: from __future__ import print_function + In [2]: print(u"something") + something + +You can print multiple things: + +.. code-block:: ipython + + In [3]: print(u"the value is", 5) + the value is 5 + + +.. nextslide:: + +Python automatically adds a newline, which you can change with ``end`` argument: + + +.. code-block:: ipython + + In [12]: for i in range(5): + ....: print(u"the value is", end=' ') + ....: print(i) + ....: + the value is 0 + the value is 1 + the value is 2 + the value is 3 + the value is 4 + + +.. nextslide:: + +Any python object can be printed (though it might not be pretty...) + +.. code-block:: ipython + + In [1]: class Bar(object): + ...: pass + ...: + + In [2]: print(Bar) + + + +.. nextslide:: Code Blocks + +Blocks of code are delimited by a colon and indentation: + +.. code-block:: python + + def a_function(): + a_new_code_block + end_of_the_block + +.. code-block:: python + + for i in range(100): + print(i**2) + +.. code-block:: python + + try: + do_something_bad() + except: + fix_the_problem() + +Whitespace +-------- + +Python uses whitespace to delineate structure. + +This means that in Python, whitespace is **significant**. + +(but **ONLY** for newlines and indentation) + +The standard is to indent with **4 spaces**. + +**SPACES ARE NOT TABS** + +**TABS ARE NOT SPACES** + + +.. nextslide:: + +These two blocks look the same: + +.. code-block:: python + + for i in range(100): + print(i**2) + +.. code-block:: python + + for i in range(100): + print(i**2) + + +.. nextslide:: + +But they are not: + +.. code-block:: python + + for i in range(100): + \s\s\s\sprint(i**2) + +.. code-block:: python + + for i in range(100): + \tprint(i**2) + +**ALWAYS INDENT WITH 4 SPACES** + + +.. nextslide:: + +.. rst-class:: center large + +NEVER INDENT WITH TABS + +make sure your editor is set to use spaces only -- + +ideally even when you hit the key + +Values +------ + +.. rst-class:: build + +* Values are pieces of unnamed data: ``42, u'Hello, world',`` +* In Python, all values are objects + + * Try ``dir(42)`` - lots going on behind the curtain! + +* Every value belongs to a type + + * Try ``type(42)`` - the type of a value determines what it can do + +.. ifslides:: + + .. rst-class:: centered + + [demo] + +Literals for the Basic Value types: +------------------------------------ + +.. rst-class:: build + +Numbers: + - floating point: ``3.4`` + - integers: ``456`` + +Text: + - ``u"a bit of text"`` + - ``u'a bit of text'`` + - (either single or double quotes work -- why?) + +Boolean values: + - ``True`` + - ``False`` + +(There are intricacies to all of these that we'll get into later) + + +Values in Action +---------------- + +An expression is made up of values and operators + +.. rst-class:: build + +* An expression is evaluated to produce a new value: ``2 + 2`` + + * The Python interpreter can be used as a calculator to evaluate expressions + +* Integer vs. float arithmetic + + * `1 / 2` versus `1. / 2` + * (Python 3 smooths this out) + * Always use ``/`` when you want float results, ``//`` when you want floored (integer) results + +* Type conversions + + * This is the source of many errors, especially in handling text + * Python 3 will not implicitly convert bytes to unicode + +* Type errors - checked at run time only + +.. ifslides:: + + .. rst-class:: centered + + [demo] + + +Symbols +------- + +Symbols are how we give names to values (objects). + +.. rst-class:: build + +* Symbols must begin with an underscore or letter +* Symbols can contain any number of underscores, letters and numbers + + * this_is_a_symbol + * this_is_2 + * _AsIsThis + * 1butThisIsNot + * nor-is-this + +* Symbols don't have a type; values do + + * This is why python is 'Dynamic' + + +Symbols and Type +---------------- + +Evaluating the type of a *symbol* will return the type of the *value* to which +it is bound. + +.. code-block:: ipython + + In [19]: type(42) + Out[19]: int + + In [20]: type(3.14) + Out[20]: float + + In [21]: a = 42 + + In [22]: b = 3.14 + + In [23]: type(a) + Out[23]: int + + In [25]: a = b + + In [26]: type(a) + Out[26]: float + + +Assignment +---------- + +A *symbol* is **bound** to a *value* with the assignment operator: ``=`` + +.. rst-class:: build + +* This attaches a name to a value +* A value can have many names (or none!) +* Assignment is a statement, it returns no value + + +.. nextslide:: + +Evaluating the name will return the value to which it is bound + +.. code-block:: ipython + + In [26]: name = u"value" + + In [27]: name + Out[27]: u'value' + + In [28]: an_integer = 42 + + In [29]: an_integer + Out[29]: 42 + + In [30]: a_float = 3.14 + + In [31]: a_float + Out[31]: 3.14 + + +In-Place Assignment +------------------- + +You can also do "in-place" assignment with ``+=``. + +.. code-block:: ipython + + In [32]: a = 1 + + In [33]: a + Out[33]: 1 + + In [34]: a = a + 1 + + In [35]: a + Out[35]: 2 + + In [36]: a += 1 + + In [37]: a + Out[37]: 3 + +also: ``-=, *=, /=, **=, %=`` + +(not quite -- really in-place assignment for mutables....) + + +Multiple Assignment +------------------- + +You can assign multiple variables from multiple expressions in one statement + +.. code-block:: ipython + + In [48]: x = 2 + + In [49]: y = 5 + + In [50]: i, j = 2 * x, 3 ** y + + In [51]: i + Out[51]: 4 + + In [52]: j + Out[52]: 243 + + +Python evaluates all the expressions on the right before doing any assignments + + +Nifty Python Trick +------------------ + +Using this feature, we can swap values between two symbols in one statement: + +.. code-block:: ipython + + In [51]: i + Out[51]: 4 + + In [52]: j + Out[52]: 243 + + In [53]: i, j = j, i + + In [54]: i + Out[54]: 243 + + In [55]: j + Out[55]: 4 + +Multiple assignment and symbol swapping can be very useful in certain contexts + + +Equality +-------- + +You can test for the equality of certain values with the ``==`` operator + +.. code-block:: ipython + + In [77]: val1 = 20 + 30 + + In [78]: val2 = 5 * 10 + + In [79]: val1 == val2 + Out[79]: True + + In [80]: val3 = u'50' + + In [81]: val1 == val3 + Out[84]: False + +.. ifslides:: + + .. rst-class:: centered + + [demo] + + +Operator Precedence +------------------- + +Operator Precedence determines what evaluates first: + +.. code-block:: python + + 4 + 3 * 5 != (4 + 3) * 5 + +To force statements to be evaluated out of order, use parentheses. + + +Python Operator Precedence +-------------------------- + +Parentheses and Literals: + ``(), [], {}`` + + ``"", b'', u''`` + +Function Calls: + ``f(args)`` + +Slicing and Subscription: + ``a[x:y]`` + + ``b[0], c['key']`` + +Attribute Reference: + ``obj.attribute`` + +.. nextslide:: + +Exponentiation: + ``**`` + +Bitwise NOT, Unary Signing: + ``~x`` + + ``+x, -x`` + +Multiplication, Division, Modulus: + ``*, /, %`` + +Addition, Subtraction: + ``+, -`` + +.. nextslide:: + +Bitwise operations: + ``<<, >>,`` + + ``&, ^, |`` + +Comparisons: + ``<, <=, >, >=, !=, ==`` + +Membership and Identity: + ``in, not in, is, is not`` + +Boolean operations: + ``or, and, not`` + +Anonymous Functions: + ``lambda`` + + +String Literals +--------------- + +You define a ``string`` value by writing a *literal*: + +.. code-block:: ipython + + In [1]: u'a string' + Out[1]: u'a string' + + In [2]: u"also a string" + Out[2]: u'also a string' + + In [3]: u"a string with an apostrophe: isn't it cool?" + Out[3]: u"a string with an apostrophe: isn't it cool?" + + In [4]: u'a string with an embedded "quote"' + Out[4]: u'a string with an embedded "quote"' + +(what's the '``u``' about?) + + +Keywords +-------- + +Python defines a number of **keywords** + +These are language constructs. + +You *cannot* use these words as symbols. + +:: + + and del from not while + as elif global or with + assert else if pass yield + break except import print + class exec in raise + continue finally is return + def for lambda try + +.. nextslide:: + +If you try to use any of the keywords as symbols, you will cause a +``SyntaxError``: + +.. code-block:: ipython + + In [13]: del = u"this will raise an error" + File "", line 1 + del = u"this will raise an error" + ^ + SyntaxError: invalid syntax + +.. code-block:: ipython + + In [14]: def a_function(else=u'something'): + ....: print(else) + ....: + File "", line 1 + def a_function(else=u'something'): + ^ + SyntaxError: invalid syntax + + +__builtins__ +------------ + +Python also has a number of pre-bound symbols, called **builtins** + +Try this: + +.. code-block:: ipython + + In [6]: dir(__builtins__) + Out[6]: + ['ArithmeticError', + 'AssertionError', + 'AttributeError', + 'BaseException', + 'BufferError', + ... + 'unicode', + 'vars', + 'xrange', + 'zip'] + +.. nextslide:: + +You are free to rebind these symbols: + +.. code-block:: ipython + + In [15]: type(u'a new and exciting string') + Out[15]: unicode + + In [16]: type = u'a slightly different string' + + In [17]: type(u'type is no longer what it was') + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + ----> 1 type(u'type is no longer what it was') + + TypeError: 'unicode' object is not callable + +In general, this is a **BAD IDEA**. + + +Exceptions +---------- + +Notice that the first batch of ``__builtins__`` are all *Exceptions* + +Exceptions are how Python tells you that something has gone wrong. + +There are several exceptions that you are likely to see a lot of: + +.. rst-class:: build + +* ``NameError``: indicates that you have tried to use a symbol that is not bound to + a value. +* ``TypeError``: indicates that you have tried to use the wrong kind of object for + an operation. +* ``SyntaxError``: indicates that you have mis-typed something. +* ``AttributeError``: indicates that you have tried to access an attribute or + method that an object does not have (this often means you have a different + type of object than you expect) + +The ``if`` Statement +--------------------- + +In order to do anything interesting at all (including this week's homework), you need to be able to make a decision. + +.. nextslide:: + +.. code-block:: python + + In [12]: def test(a): + ....: if a == 5: + ....: print(u"that's the value I'm looking for!") + ....: elif a == 7: + ....: print(u"that's an OK number") + ....: else: + ....: print(u"that number won't do!") + + In [13]: test(5) + that's the value I'm looking for! + + In [14]: test(7) + that's an OK number + + In [15]: test(14) + that number won't do! + +There is more to it than that, but this will get you started. + + +Functions +--------- + +What is a function? + +.. rst-class:: build + +A function is a self-contained chunk of code + + +You use them when you need the same code to run multiple times, +or in multiple parts of the program. + +(DRY) + + +Or just to keep the code clean + + +Functions can take and return information + +.. nextslide:: + +Minimal Function does nothing + +.. code-block:: python + + def (): + + +.. nextslide:: + +Pass Statement (Note the indentation!) + +.. code-block:: python + + def minimal(): + pass + + +Functions: ``def`` +------------------ + +``def`` is a *statement*: + +.. rst-class:: build + + * it is executed + * it creates a local variable + + +.. nextslide:: + +function defs must be executed before the functions can be called: + +.. code-block:: ipython + + In [23]: unbound() + --------------------------------------------------------------------------- + NameError Traceback (most recent call last) + in () + ----> 1 unbound() + + NameError: name 'unbound' is not defined + +.. code-block:: ipython + + In [18]: def simple(): + ....: print(u"I am a simple function") + ....: + + In [19]: simple() + I am a simple function + + +Calling Functions +----------------- + +You **call** a function using the function call operator (parens): + +.. code-block:: ipython + + In [2]: type(simple) + Out[2]: function + In [3]: simple + Out[3]: + In [4]: simple() + I am a simple function + + +Functions: Call Stack +--------------------- + +functions call functions -- this makes an execution stack -- that's all a trace +back is + +.. code-block:: ipython + + In [5]: def exceptional(): + ...: print(u"I am exceptional!") + ...: print(1/0) + ...: + In [6]: def passive(): + ...: pass + ...: + In [7]: def doer(): + ...: passive() + ...: exceptional() + ...: + +You've defined three functions, one of which will *call* the other two. + + +Functions: Tracebacks +--------------------- + +.. code-block:: ipython + + In [8]: doer() + I am exceptional! + --------------------------------------------------------------------------- + ZeroDivisionError Traceback (most recent call last) + in () + ----> 1 doer() + + in doer() + 1 def doer(): + 2 passive() + ----> 3 exceptional() + 4 + + in exceptional() + 1 def exceptional(): + 2 print(u"I am exceptional!") + ----> 3 print(1/0) + 4 + + ZeroDivisionError: integer division or modulo by zero + + + +Functions: ``return`` +--------------------- + +Every function ends by returning a value + +This is actually the simplest possible function: + +.. code-block:: python + + def fun(): + return None + +.. nextslide:: + +if you don't explicilty put ``return`` there, Python will: + +.. code-block:: ipython + + In [9]: def fun(): + ...: pass + ...: + In [10]: fun() + In [11]: result = fun() + In [12]: print(result) + None + +note that the interpreter eats ``None`` + + +.. nextslide:: + +Only one return statement will ever be executed. + +Ever. + +Anything after a executed return statement will never get run. + +This is useful when debugging! + +.. code-block:: ipython + + In [14]: def no_error(): + ....: return u'done' + ....: # no more will happen + ....: print(1/0) + ....: + In [15]: no_error() + Out[15]: u'done' + + +.. nextslide:: + +However, functions *can* return multiple results: + +.. code-block:: ipython + + In [16]: def fun(): + ....: return (1, 2, 3) + ....: + In [17]: fun() + Out[17]: (1, 2, 3) + + +.. nextslide:: + +Remember multiple assignment? + +.. code-block:: ipython + + In [18]: x,y,z = fun() + In [19]: x + Out[19]: 1 + In [20]: y + Out[20]: 2 + In [21]: z + Out[21]: 3 + + +Functions: parameters +--------------------- + +In a ``def`` statement, the values written *inside* the parens are +**parameters** + +.. code-block:: ipython + + In [22]: def fun(x, y, z): + ....: q = x + y + z + ....: print(x, y, z, q) + ....: + +x, y, z are *local* symbols -- so is q + + +Functions: arguments +-------------------- + +When you call a function, you pass values to the function parameters as +**arguments** + +.. code-block:: ipython + + In [23]: fun(3, 4, 5) + 3 4 5 12 + +The values you pass in are *bound* to the symbols inside the function and used. + + +Enough For Now +-------------- + +That's it for our basic intro to Python + +Before next session, you'll use what you've learned here today to do some +exercises in Python programming + +Unicode Notes +------------- + +You might need this for the puzzle if you use foreign languages. + +To put unicode in your source file, put: + +.. code-block:: python + + #!/usr/bin/env python + # -*- coding: utf-8 -*- + +at the top of your file ... and be sure to save it as utf-8! +(file->save with encoding in Sublime) + +You also might want to put:: + + from __future__ import unicode_literals + +Additional notes on using Unicode in Python see: + + :ref:`unicode_supplement` + + +Puzzle Solved +======== + +Now it's time to solve our puzzle. Remember it? + +Write a Python program that prints "Hello, World!" if you call it with +no arguments, otherwise prints the correct translation of +"Hello, World!" in whatever language is given as the first argument +to the program. + +Partner up and let's get to work! + +Homework +======== + +.. rst-class:: center large + + Three Tasks due by Wednesday, check them out on Canvas. + +Homework Task 1: Python Pre-work + +Homework Task 2: Style Checking + +Homework Task 3: Gitting To Know You + +Homework Task 4: Break These Functions diff --git a/docs/session2.rst b/docs/session2.rst new file mode 100644 index 0000000..0cdb130 --- /dev/null +++ b/docs/session2.rst @@ -0,0 +1,1575 @@ +******************************************** +Session Two: Functions, Booleans and Modules +******************************************** + +.. ifslides:: + + .. rst-class:: center large + + Oh My! + + + +Review/Questions +================ + +Review of Previous Session +-------------------------- + +.. rst-class:: build + + * Values and Types + * REPL + * Expressions and Statements + * `dir` and `help` + * Unit tests + * `NameError`, `TypeError`, `AttributeError` + * Intro to functions + + +Homework Review +--------------- + +.. rst-class:: center large + +Any questions that are nagging? + +Today's Plan +================ + +* Github Upstream +* Functions +* Booleans +* Modules + + +Git Work +======== + +.. rst-class:: center large + +Let's get to know your fellow students! + +Python Speed-Dating +------------------------ + +Stand up and walk around. + +Choose a partner who has done HW 03. +Introduce yourselves! + +If you've done HW 03 "Gitting to Know You", +share the URL of your pull request with your partner. + +If you haven't done it yet, have your partner walk +through their example with you, and +complete it in class. + +Working with an Upstream +------------------------ + +You've created a fork of the class repository from the ``codefellows`` account +on GitHub. + +You are creating branches and pull requests on your forked repo. + +You won't ever be *pushing* to the original class repo, +but you want to *pull* changes from it. + +To do this, you use the git concept of an **upstream** repository. + +.. nextslide:: + +Since ``git`` is a *distributed* versioning system, there is no **central** +repository that serves as the one to rule them all. + +Instead, you work with *local* repositories, and *remotes* that they are +connected to. + +Cloned repositories get an *origin* remote for free: + +.. code-block:: bash + + $ git remote -v + origin https://github.com/cewing/sea-c45-python.git (fetch) + origin https://github.com/cewing/sea-c45-python.git (push) + +This shows that the local repo on my machine *originated* from the one in my gitHub account (the one it was cloned from) + +.. nextslide:: Adding a Remote + +You can add *remotes* at will, to connect your *local* repository to other +copies of it in different remote locations. + +This allows you to grab changes made to the repository in these other +locations. + +For our class, we will add an *upstream* remote to our local copy that points +to the original copy of the material in the ``codefellows`` account. + +.. code-block:: bash + + $ git remote add upstream https://github.com/codefellows/sea-c34-python.git + + $ git remote -v + origin https://github.com/cewing/sea-c45-python.git (fetch) + origin https://github.com/cewing/sea-c45-python.git (push) + upstream https://github.com/codefellows/sea-c45-python.git (fetch) + upstream https://github.com/codefellows/sea-c45-python.git (push) + +.. nextslide:: Fetching Everything. + +To get the updates from your new remote, you'll need first to fetch everything: + +.. code-block:: bash + + $ git fetch --all + Fetching origin + Fetching upstream + ... + +Then you can see the branches you have locally available: + +.. code-block:: bash + + $ git branch -a + * master + remotes/origin/HEAD -> origin/master + remotes/origin/gh-pages + remotes/origin/master + remotes/upstream/gh-pages + remotes/upstream/master + +(the gh-pages branch is used to publish these notes) + +.. nextslide:: Fetching Upstream Changes + +Finally, you can fetch and then merge changes from the upstream master. + +Start by making sure you are on your own master branch: + +.. code-block:: bash + + $ git checkout master + +This is **really really** important. Take the time to ensure you are where you +think you are. + +.. nextslide:: Merging Upstream Changes + +Then, fetch the upstream master branch and merge it into your master: + +.. code-block:: bash + + $ git fetch upstream master + From https://github.com/codefellows/sea-c34-python.git + * branch master -> FETCH_HEAD + + $ git merge upstream/master + Updating 3239de7..9ddbdbb + Fast-forward + Examples/README.rst | 4 ++++ + ... + create mode 100644 Examples/README.rst + ... + +NOTE: you can do that in one step with: + +.. code-block:: bash + + $ git pull upstream master + +.. nextslide:: Pushing to Origin + +Now all the changes from *upstream* are present in your local clone. + +In order to preserve them in your fork on GitHub, you'll have to push: + +.. code-block:: bash + + $ git status + On branch master + Your branch is ahead of 'origin/master' by 10 commits. + (use "git push" to publish your local commits) + $ git push origin master + Counting objects: 44, done. + ... + $ + +(A simple ``git push`` will usually do the right thing) + +.. nextslide:: Daily Workflow + +You can incorporate this into your daily workflow: :: + + $ git checkout master + $ git pull upstream master + $ git push + [do some work] + $ git commit -a + [add a good commit message] + $ git push + [make a pull request] + +Python Tutor +===================== +Open your browser to + +http://pythontutor.com + +Some Needed Plumbing +===================== + +.. rst-class:: center large + +Because there's a few things you just gotta have: + +* collections +* looping + +Collections and Looping +----------------- + +It turns out you can't really do much at all without at least a collection (container) type, +conditionals and looping... + + +.. nextslide:: Review: if + +``if`` and ``elif`` allow you to make decisions: + +.. code-block:: python + + if a: + print(u'a') + elif b: + print(u'b') + elif c: + print(u'c') + else: + print(u'that was unexpected') + + +.. nextslide:: Test Your Knowledge: if + +What's the difference between these two: + +.. code-block:: python + + if a: + print(u'a') + elif b: + print(u'b') + ## versus... + if a: + print(u'a') + if b: + print(u'b') + +Try it at http://pythontutor.com + +.. nextslide:: switch? + +Many languages have a ``switch`` construct: + +.. code-block:: js + + switch (expr) { + case "Oranges": + document.write("Oranges are $0.59 a pound.
"); + break; + case "Apples": + document.write("Apples are $0.32 a pound.
"); + break; + case "Mangoes": + case "Papayas": + document.write("Mangoes and papayas are $2.79 a pound.
"); + break; + default: + document.write("Sorry, we are out of " + expr + ".
"); + } + +.. nextslide:: switch? + +**Not Python** + +use ``if..elif..elif..else`` + +(or a dictionary, or subclassing....) + + +.. nextslide:: lists + +A way to store a bunch of stuff in order + +Pretty much like an "array" or "vector" in other languages + +.. code-block:: python + + a_list = [2, 3, 5, 9] + a_list_of_strings = [u'this', u'that', u'the', u'other'] + + +.. nextslide:: tuples + +Another way to store an ordered list of things + +.. code-block:: python + + a_tuple = (2, 3, 4, 5) + a_tuple_of_strings = (u'this', u'that', u'the', u'other') + + +Tuples are **not** the same as lists. + +The exact difference is a topic for next session. + + +.. nextslide:: for + +Sometimes called a 'determinate' loop + +When you need to do something to everything in a sequence + +.. code-block:: ipython + + In [10]: a_list = [2, 3, 4, 5] + + In [11]: for item in a_list: + ....: print(item) + ....: + 2 + 3 + 4 + 5 + +Try it at http://pythontutor.com + +.. nextslide:: range() and for + +Range builds lists of numbers automatically + +Use it when you need to do something a set number of times + +.. code-block:: ipython + + In [12]: range(6) + Out[12]: [0, 1, 2, 3, 4, 5] + + In [13]: for i in range(6): + ....: print(u'*', end=u' ') + ....: + * * * * * * + +Try it at http://pythontutor.com + +.. nextslide:: Intricacies + +This is enough to get you started. + +Each of these have intricacies special to python + +We'll get to those over the next couple of classes + + +Functions +========= + +Functions Puzzle +--------- + +In your local repo, after you've updated from upstream, +find the file `stackoverflow.py`. + +In it, you will find a function that calls itself. + +* What problems does this cause? +* Why do you think the problem occurs? +* How can you count the number of times a function can call itself? +* Modify the program to implement your solution. + + + +Review +------ + +Defining a function: + +.. code-block:: python + + def fun(x, y): + z = x + y + return z + + +x, y, z are *local* names + + +Local vs. Global +---------------- + +Symbols bound in Python have a *scope* + +That *scope* determines where a symbol is visible, or what value it has in a +given block. + +.. code-block:: ipython + + In [14]: x = 32 + In [15]: y = 33 + In [16]: z = 34 + In [17]: def fun(y, z): + ....: print(x, y, z) + ....: + In [18]: fun(3, 4) + 32 3 4 + + +x is global, y and z local to the function + +.. nextslide:: + +But, did the value of y and z change in the *global* scope? + +.. code-block:: ipython + + In [19]: y + Out[19]: 33 + + In [20]: z + Out[20]: 34 + +.. nextslide:: + +In general, you should use global bindings mostly for constants. + +In python we designate global constants by typing the symbols we bind to them +in ALL_CAPS + +.. code-block:: python + + INSTALLED_APPS = [u'foo', u'bar', u'baz'] + CONFIGURATION_KEY = u'some secret value' + ... + +This is just a convention, but it's a good one to follow. + + +.. nextslide:: Global Gotcha + +Take a look at this function definition: + +.. code-block:: ipython + + In [21]: x = 3 + + In [22]: def f(): + ....: y = x + ....: x = 5 + ....: print(x) + ....: print(y) + ....: + +What is going to happen when we call ``f`` + +.. nextslide:: Global Gotcha + +Try it and see: + +.. code-block:: ipython + + In [23]: f() + --------------------------------------------------------------------------- + UnboundLocalError Traceback (most recent call last) + in () + ----> 1 f() + + in f() + 1 def f(): + ----> 2 y = x + 3 x = 5 + 4 print(x) + 5 print(y) + + UnboundLocalError: local variable 'x' referenced before assignment + +Because you are binding the symbol ``x`` locally, it becomes a local and masks +the global value already bound. + + +Parameters +---------- + +So far we've seen simple parameter lists: + +.. code-block:: python + + def fun(x, y, z): + print(x, y, z) + +These types of parameters are called *positional* + +When you call a function, you **must** provide arguments for all *positional* +parameters *in the order they are listed* + + +.. nextslide:: + +You can provide *default values* for parameters in a function definition: + +.. code-block:: ipython + + In [24]: def fun(x=1, y=2, z=3): + ....: print(x, y, z) + ....: + +When parameters are given with default values, they become *optional* + +.. code-block:: ipython + + In [25]: fun() + 1 2 3 + + +.. nextslide:: + +You can provide arguments to a function call for *optional* parameters +positionally: + +.. code-block:: ipython + + In [26]: fun(6) + 6 2 3 + In [27]: fun(6, 7) + 6 7 3 + In [28]: fun(6, 7, 8) + 6 7 8 + +Or, you can use the parameter name as a *keyword* to indicate which you mean: + +.. code-block:: ipython + + In [29]: fun(y=4, x=1) + 1 4 3 + +.. nextslide:: + +Once you've provided a *keyword* argument in this way, you can no longer +provide any *positional* arguments: + +.. code-block:: ipython + + In [30]: fun(x=5, 6) + File "", line 1 + fun(x=5, 6) + SyntaxError: non-keyword arg after keyword arg + +.. nextslide:: Parameters and Unpacking + +This brings us to a fun feature of Python function definitions. + +You can define a parameter list that requires an **unspecified** number of +*positional* or *keyword* arguments. + +The key is the ``*`` (splat) or ``**`` (double-splat) operator: + +.. code-block:: ipython + + In [31]: def fun(*args, **kwargs): + ....: print(args, kwargs) + ....: + In [32]: fun(1) + (1,) {} + In [33]: fun(1, 2, zombies=u"brains") + (1, 2) {'zombies': u'brains'} + In [34]: fun(1, 2, 3, zombies=u"brains", vampires=u"blood") + (1, 2, 3) {'vampires': u'blood', 'zombies': u'brains'} + +**args** and **kwargs** are *conventional* names for these. + + +Documentation +------------- + +It's often helpful to leave information in your code about what you were +thinking when you wrote it. + +This can help reduce the number of `WTFs per minute`_ in reading it later. + +.. _WTFs per minute: http://www.osnews.com/story/19266/WTFs_m + +There are two approaches to this: + +.. rst-class:: build + +* Comments +* Docstrings + +.. nextslide:: Comments + +Comments go inline in the body of your code, to explain reasoning: + +.. code-block:: python + + if (frobnaglers > whozits): + # borangas are shermed to ensure frobnagler population + # does not grow out of control + sherm_the_boranga() + +You can use them to mark places you want to revisit later: + +.. code-block:: python + + for partygoer in partygoers: + for baloon in baloons: + for cupcake in cupcakes: + # TODO: Reduce time complexity here. It's killing us + # for large parties. + resolve_party_favor(partygoer, baloon, cupcake) + +.. nextslide:: Comments + +Be judicious in your use of comments. + +Use them when you need to. + +Make them useful. + +This is not useful: + +.. code-block:: python + + for sponge in sponges: + # apply soap to each sponge + worker.apply_soap(sponge) + +.. nextslide:: Docstrings + +In Python, ``docstrings`` are used to provide in-line documentation in a number +of places. + +The first place we will see is in the definition of ``functions``. + +To define a function you use the ``def`` keyword. + +If a ``string literal`` is the first thing in the function block following the +header, it is a ``docstring``: + +.. code-block:: python + + def complex_function(arg1, arg2, kwarg1=u'bannana'): + """Return a value resulting from a complex calculation.""" + # code block here + +You can then read this in an interpreter as the ``__doc__`` attribute of the +function object. + +.. nextslide:: Docstrings + +A ``docstring`` should: + +.. rst-class:: build + +* be a complete sentence in the form of a command describing what the function + does. + + * """Return a list of values based on blah blah""" is a good docstring + * """Returns a list of values based on blah blah""" is *not* + +* fit onto a single line. + + * If more description is needed, make the first line a complete sentence and + add more lines below for enhancement. + +* be enclosed with triple-quotes. + + * This allows for easy expansion if required at a later date + * Always close on the same line if the docstring is only one line. + +For more information see `PEP 257: Docstring Conventions`_. + +.. _PEP 257\: Docstring Conventions: http://legacy.python.org/dev/peps/pep-0257/ + + +Recursion +--------- + +You've seen functions that call other functions. + +If a function calls *itself*, we call that **recursion** + +Like with other functions, a call within a call establishes a *call stack* + +With recursion, if you are not careful, this stack can get *very* deep. + +Python has a maximum limit to how much it can recurse. This is intended to +save your machine from running out of RAM. + +.. nextslide:: Recursion can be Useful + +Recursion is especially useful for a particular set of problems. + +For example, take the case of the *factorial* function. + +In mathematics, the *factorial* of an integer is the result of multiplying that +integer by every integer smaller than it down to 1. + +:: + + 5! == 5 * 4 * 3 * 2 * 1 + +We can use a recursive function nicely to model this mathematical function + +.. ifslides:: + + .. rst-class:: centered + + [demo] + +Functions Puzzle Solved! +------------------------ + +Now it's time to solve the puzzle. Remember: + +In your local repo, after you've updated from upstream, +go to `session02` and find the file `stackoverflow.py`. + +In it, you will find a function that calls itself. + +* What problems does this cause? +* Why do you think the problem occurs? +* How can you count the number of times a function can call itself? +* Modify the program to implement your solution. + + + +Boolean Expressions +=================== + +Boolean Puzzle +--------- + +* Look up the ``%`` operator. What do these do? + + * ``10 % 7 == 3`` + * ``14 % 7 == 0`` + +* Write a program that prints the numbers from 1 to 100 inclusive. But for + multiples of three print "Fizz" instead of the number and for the multiples + of five print "Buzz". For numbers which are multiples of both three and five + print "FizzBuzz" instead. +* If you finish that, try your hand at writing solutions to one or more of the + problems in :download:`codingbat.rst <../code/session02/codingbat.rst>` + +.. nextslide:: + +Remember, Do These Steps + +* Read through the puzzle for that section. +* Pick a partner. Describe what your goal is. +* Read through the section `Booleans`. Try typing any code you see in `ipython` or `python` +* Come up with three questions as you are reading with your partner. +* We'll come around and help you. +* We'll regroup and you'll teach me the slides. +* We'll solve the puzzle together. + +Truthiness +---------- + +What is true or false in Python? + +.. rst-class:: build + +* The Booleans: ``True`` and ``False`` +* "Something or Nothing" +* http://mail.python.org/pipermail/python-dev/2002-April/022107.html + + +.. nextslide:: + +Determining Truthiness: + +.. code-block:: python + + bool(something) + + +.. nextslide:: What is False? + +.. rst-class:: build + +* ``None`` +* ``False`` +* **Nothing:** + +* zero of any numeric type: ``0, 0L, 0.0, 0j``. +* any empty sequence, for example, ``"", (), []``. +* any empty mapping, for example, ``{}`` . +* instances of user-defined classes, if the class defines a ``__nonzero__()`` + or ``__len__()`` method, when that method returns the integer zero or bool + value ``False``. + +* http://docs.python.org/library/stdtypes.html + +.. nextslide:: What is True? + +.. rst-class:: center large + +Everything Else + + +.. nextslide:: Pythonic Booleans + +Any object in Python, when passed to the ``bool()`` type object, will +evaluate to ``True`` or ``False``. + +When you use the ``if`` keyword, it automatically does this to the statement provided. + +Which means that this is redundant, and not Pythonic: + +.. code-block:: python + + if xx == True: + do_something() + # or even worse: + if bool(xx) == True: + do_something() + +Instead, use what Python gives you: + +.. code-block:: python + + if xx: + do_something() + + +and, or and not +---------------- + +Python has three boolean keywords, ``and``, ``or`` and ``not``. + +``and`` and ``or`` are binary expressions, and evaluate from left to right. + +``and`` will return the first operand that evaluates to False, or the last +operand if none are True: + +.. code-block:: ipython + + In [35]: 0 and 456 + Out[35]: 0 + +``or`` will return the first operand that evaluates to True, or the last +operand if none are True: + +.. code-block:: ipython + + In [36]: 0 or 456 + Out[36]: 456 + +.. nextslide:: + +On the other hand, ``not`` is a unary expression and inverts the boolean value +of its operand: + +.. code-block:: ipython + + In [39]: not True + Out[39]: False + + In [40]: not False + Out[40]: True + +.. nextslide:: Shortcutting + +Because of the return value of these keywords, you can write concise +statements: + +:: + + if x is false, + x or y return y, + else return x + + if x is false, + x and y return x + else return y + + if x is false, + not x return True, + else return False + + +.. nextslide:: Chaining + +.. code-block:: python + + a or b or c or d + a and b and c and d + + +The first value that defines the result is returned + +.. ifslides:: + + .. rst-class:: centered + + (demo) + + +.. nextslide:: Ternary Expressions + +This is a fairly common idiom: + +.. code-block:: python + + if something: + x = a_value + else: + x = another_value + +In other languages, this can be compressed with a "ternary operator":: + + result = a > b ? x : y; + +In python, the same is accomplished with the ternary expression: + +.. code-block:: python + + y = 5 if x > 2 else 3 + +PEP 308: +(http://www.python.org/dev/peps/pep-0308/) + + +Boolean Return Values +--------------------- + +Remember this puzzle from your CodingBat exercises? + +.. code-block:: python + + def sleep_in(weekday, vacation): + if weekday == True and vacation == False: + return False + else: + return True + +Though correct, that's not a particularly Pythonic way of solving the problem. + +Here's a better solution: + +.. code-block:: python + + def sleep_in(weekday, vacation): + return not (weekday == True and vacation == False) + + +.. nextslide:: + +And here's an even better one: + +.. code-block:: python + + def sleep_in(weekday, vacation): + return (not weekday) or vacation + + +.. nextslide:: bools are integers? + +In python, the boolean types are subclasses of integer: + +.. code-block:: ipython + + In [1]: True == 1 + Out[1]: True + In [2]: False == 0 + Out[2]: True + + +And you can even do math with them (though it's a bit odd to do so): + +.. code-block:: ipython + + In [6]: 3 + True + Out[6]: 4 + +.. ifslides:: + + .. rst-class:: center + + (demo) + + +Boolean Puzzle Solved +--------------------- + +Remember our puzzle: + +* Look up the ``%`` operator. What do these do? + + * ``10 % 7 == 3`` + * ``14 % 7 == 0`` + +* Write a program that prints the numbers from 1 to 100 inclusive. But for + multiples of three print "Fizz" instead of the number and for the multiples + of five print "Buzz". For numbers which are multiples of both three and five + print "FizzBuzz" instead. +* If you finish that, try your hand at writing solutions to one or more of the + problems in :download:`codingbat.rst <../code/session02/codingbat.rst>` + +Volunteer to upload your solution to Slack! + + +Code Structure, Modules, and Namespaces +======================================= + +.. rst-class:: center large + +Scopes within scopes, attributes within attributes + +Module Puzzle +-------------- + +Write a module (file) called `mystery.py` with a function +inside that solves one of the CodingBat exercises from +before: + +:download:`codingbat.rst <../code/session02/codingbat.rst>` + +Be sure to write a good docstring for your function describing +how to use it, like this example. + +.. nextslide:: Good Function Docstrings + +.. code-block:: python + + def square_root(n): + """ + Calculate the square root of a number. + + Args: + n: the number to get the square root of. + Returns: + the square root of n. + + """ + pass + +.. nextslide:: Check if it's Main + +Include a check to see if the module is being run, +or it is being imported. + +If it is being run, execute some test code that +calls your function. + +.. nextslide:: + +Remember, Do These Steps + +* Read through the puzzle for that section. +* Pick a partner. Describe what your goal is. +* Read through the section `Code Structure, Modules, Namespaces`. Try typing any code you see in `ipython` or `python` +* Come up with three questions as you are reading with your partner. +* We'll come around and help you. +* We'll regroup and you'll teach me the slides. +* We'll solve the puzzle together. + + +Code Structure +-------------- + +In Python, the structure of your code is determined by whitespace. + +How you *indent* your code determines how it is structured + +:: + + block statement: + some code body + some more code body + another block statement: + code body in + that block + +The colon that terminates a block statement is also important... + +.. nextslide:: One-liners + +You can put a one-liner after the colon: + +.. code-block:: ipython + + In [167]: x = 12 + In [168]: if x > 4: print(x) + 12 + +But this should only be done if it makes your code **more** readable. + + +.. nextslide:: Spaces vs. Tabs + +Whitespace is important in Python. + +An indent *could* be: + +* Any number of spaces +* A tab +* A mix of tabs and spaces: + +If you want anyone to take you seriously as a Python developer: + +.. rst-class:: centered + +**Always use four spaces -- really!** + +`(PEP 8)`_ + +.. _(PEP 8): http://legacy.python.org/dev/peps/pep-0008/ + + +.. nextslide:: Spaces Elsewhere + +Other than indenting -- space doesn't matter, technically. + +.. code-block:: python + + x = 3*4+12/func(x,y,z) + x = 3*4 + 12 / func (x, y, z) + +But you should strive for proper style. Read `PEP 8`_ and install a linter in +your editor. + +.. _PEP 8: http://legacy.python.org/dev/peps/pep-0008/ + + +Modules and Packages +-------------------- + +Python is all about *namespaces* -- the "dots" + +``name.another_name`` + +The "dot" indicates that you are looking for a name in the *namespace* of the +given object. It could be: + +* name in a module +* module in a package +* attribute of an object +* method of an object + + +.. nextslide:: Modules + +A module is simply a namespace. + +It might be a single file, or it could be a collection of files that define a +shared API. + +To a first approximation, you can think of the files you write that end in +``.py`` as modules. + +.. nextslide:: Packages + +A package is a module with other modules in it. + +On a filesystem, this is represented as a directory that contains one or more +``.py`` files, one of which **must** be called ``__init__.py``. + +When you have a package, you can import the package, or any of the modules +inside it. + +.. nextslide:: importing modules + +.. code-block:: python + + import modulename + from modulename import this, that + import modulename as a_new_name + from modulename import this as that + +.. ifslides:: + + .. rst-class:: centered + + (demo) + + +.. nextslide:: importing from packages + +.. code-block:: python + + import packagename.modulename + from packagename.modulename import this, that + from package import modulename + +.. ifslides:: + + .. rst-class:: centered + + (demo) + +http://effbot.org/zone/import-confusion.htm + +.. nextslide:: importing from packages + +.. code-block:: python + + from modulename import * + +.. rst-class:: centered large + +**Don't do this!** + + +Import +------ + +When you import a module, or a symbol from a module, the Python code is +*compiled* to **bytecode**. + +.. rst-class:: build +.. container:: + + The result is a ``module.pyc`` file. + + This process **executes all code at the module scope**. + + For this reason, it is good to avoid module-scope statements that have + global side-effects. + + +.. nextslide:: Re-import + +The code in a module is NOT re-run when imported again + +It must be explicitly reloaded to be re-run + +.. code-block:: python + + import modulename + reload(modulename) + +.. ifslides:: + + .. rst-class:: centered + + (demo) + + +.. nextslide:: Running a Module + +In addition to importing modules, you can run them. + +There are a few ways to do this: + +.. rst-class:: build + +* ``$ python hello.py`` -- must be in current working directory +* ``$ python -m hello`` -- any module on PYTHONPATH anywhere on the system +* ``$ ./hello.py`` -- put ``#!/usr/bin/env python`` at top of module + (Unix) +* ``In [149]: run hello.py`` -- at the IPython prompt -- running a module + brings its names into the interactive namespace + + +.. nextslide:: Running a Module + +Like importing, running a module executes all statements at the module level. + +.. rst-class:: build +.. container:: + + But there's an important difference. + + When you *import* a module, the value of the symbol ``__name__`` in the + module is the same as the filename. + + When you *run* a module, the value of the symbol ``__name__`` is + ``__main__``. + + This allows you to create blocks of code that are executed *only when you + run a module* + + .. code-block:: python + + if __name__ == '__main__': + # Do something interesting here + # It will only happen when the module is run + +.. nextslide:: "main" blocks + +This is useful in a number of cases. + +.. rst-class:: build +.. container:: + + You can put code here that lets your module be a utility script + + You can put code here that demonstrates the functions contained in your + module + + You can put code here that proves that your module works. + +.. ifslides:: + + [demo] + + +.. nextslide:: ``Assert`` + +Writing ``tests`` that demonstrate that your program works is an important part +of learning to program. + +The python ``assert`` statement is useful in writing ``main`` blocks that test +your code. + +.. code-block:: ipython + + In [1]: def add(n1, n2): + ...: return n1 + n2 + ...: + + In [2]: assert add(3, 4) == 7 + + In [3]: assert add(3, 4) == 10 + --------------------------------------------------------------------------- + AssertionError Traceback (most recent call last) + in () + ----> 1 assert add(3, 4) == 10 + + AssertionError: + +In-Class Lab +============ + +.. rst-class:: center large + +Import Interactions + +Exercises +--------- + +Experiment with importing different ways: + +.. code-block:: ipython + + In [3]: import math + + In [4]: math. + math.acos math.degrees math.fsum math.pi + math.acosh math.e math.gamma math.pow + math.asin math.erf math.hypot math.radians + math.asinh math.erfc math.isinf math.sin + math.atan math.exp math.isnan math.sinh + math.atan2 math.expm1 math.ldexp math.sqrt + math.atanh math.fabs math.lgamma math.tan + math.ceil math.factorial math.log math.tanh + math.copysign math.floor math.log10 math.trunc + math.cos math.fmod math.log1p + math.cosh math.frexp math.modf + +.. nextslide:: + +.. code-block:: ipython + + In [6]: math.sqrt(4) + Out[6]: 2.0 + In [7]: import math as m + In [8]: m.sqrt(4) + Out[8]: 2.0 + In [9]: from math import sqrt + In [10]: sqrt(4) + Out[10]: 2.0 + + +.. nextslide:: + +Experiment with importing different ways: + +.. code-block:: python + + import sys + print(sys.path) + import os + print(os.path) + + +You wouldn't want to import * those! + + -- check out + +.. code-block:: python + + os.path.split(u'/foo/bar/baz.txt') + os.path.join(u'/foo/bar', u'baz.txt') + +Module Puzzle Solved +-------------------- + +Now we will solve our Module Puzzle! + +Write a module (file) called `mystery.py` with a function +inside that solves one of the CodingBat exercises from +before: + +:download:`codingbat.rst <../code/session02/codingbat.rst>` + +Be sure to write a good docstring for your function describing +how to use it, like this example. + +.. nextslide:: Good Function Docstrings + +.. code-block:: python + + def square_root(n): + """ + Calculate the square root of a number. + + Args: + n: the number to get the square root of. + Returns: + the square root of n. + + """ + pass + +.. nextslide:: Check if it's Main + +Include a check to see if the module is being run, +or it is being imported. + +If it is being run, execute some test code that +calls your function. + +.. nextslide:: Live Demo + +Someone upload their file to Slack and volunteer. + +I'll go through the process of importing the module, +and we'll try to figure out what your function does, +and how to run it. + +Homework +======== + +.. rst-class:: center large + +Two Tasks by Monday + +Task 4 +------ + +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, ... + +Create a branch in your local repo called `task4` and switch to it (`git checkout task4`). + +Create a ``session02`` folder in your student folder. For +example, mine would have the path ``students/PaulPham/session02``. + +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, starting at 0. + +For example, ``fibonacci(n=0)`` should equal ``0``. +``fibonacci(n=1)`` should equal ``1``. +``fibonacci(n=2)`` should equal ``1``. And so forth. + +Ensure that your function has a well-formed ``docstring`` + +.. _Fibonacci Series: http://en.wikipedia.org/wiki/Fibbonaci_Series + +.. nextslide:: + +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, ... + +.. _Lucas Numbers: http://en.wikipedia.org/wiki/Lucas_number + +In your ``series.py`` module, add a new function ``lucas`` that returns the +``nth`` value in the *lucas numbers* + +Ensure that your function has a well-formed ``docstring`` + +.. nextslide:: + +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`` + +.. nextslide:: + +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 local repo (on branch `task4`) and commit frequently while working on +your implementation. Include good commit messages that explain concisely both +*what* you are doing and *why*. + +Add your files +to that branch, commit and push, then submit a pull request to +the main class repo. + +When you are finished, push your changes to your fork of the class repository +in GitHub. +Finally, submit your assignment in Canvas by giving the URL of the pull request. + +Task 5 +------ + +Read through the Session 03 slides. + +http://codefellows.github.io/sea-c34-python/session03.html + +There are three sections: + +* Sequences +* Iteration +* String Formatting + +For each section, come up with three questions and write some +Python code to help you answer them, one function per question. + +For each function, write a good ``docstring`` describing what +question you are trying to answer. + +Put the functions in three separate modules (files) called +`sequences.py`, `iteration.py`, and `string.py` in the +``session02`` subdirectory of your student directory, just as +you did for ``series.py`` up above. + +.. nextslide:: + +That is, you should have nine questions, and nine functions, total, +spread out across three files. + +Use everything you've learned +so far (including functions, booleans, and printing). + +Create a branch in your local repo called `task5` and switch to it (`git checkout task5`). + +Add your files +to that branch, commit and push, then submit a pull request to +the main class repo. + +Finally, submit your assignment in Canvas by giving the URL of the pull request. + + diff --git a/docs/session3.rst b/docs/session3.rst new file mode 100644 index 0000000..768c880 --- /dev/null +++ b/docs/session3.rst @@ -0,0 +1,1139 @@ +********************************************************* +Session Three: Sequences, Iteration and String Formatting +********************************************************* + +Review/Questions +================ + +Review of Previous Session +-------------------------- + +.. rst-class:: build + +* Functions +* Booleans +* Modules + +Corrections +--------------- + +* line breaks don't end a block +* squirrel party example +* unicode hello world +* stepping through code +* linter + +Course Logistics +--------------- + +* Attendance, grades, and homework due dates on Canvas +* Course Notes +* Use of Slack + +Survey Feedback +--------------- + + + +Homework Review +--------------- + +.. rst-class:: center large + +Any questions that are nagging? + + +Sequences +========= + +.. rst-class:: center large + +Ordered collections of objects + + +What is a Sequence? +------------------- + +Remember Duck Typing? A *sequence* can be considered as anything that supports +*at least* these operations: + +.. rst-class:: build + +* Indexing +* Slicing +* Membership +* Concatenation +* Length +* Iteration + + +Sequence Types +-------------- + +There are seven builtin types in Python that are *sequences*: + +* strings +* Unicode strings +* lists +* tuples +* bytearrays +* buffers +* array.arrays +* xrange objects (almost) + +For this class, you won't see much beyond the string types, lists, tuples -- the rest are pretty special purpose. + +But what we say today applies to all sequences (with minor caveats) + + +Indexing +-------- + +Items in a sequence may be looked up by *index* using the subscription +operator: ``[]`` + +Indexing in Python always starts at zero. + +.. code-block:: ipython + + In [98]: s = u"this is a string" + In [99]: s[0] + Out[99]: u't' + In [100]: s[5] + Out[100]: u'i' + + +.. nextslide:: + +You can use negative indexes to count from the end: + +.. code-block:: ipython + + In [105]: s = u"this is a string" + In [106]: s[-1] + Out[106]: u'g' + In [107]: s[-6] + Out[107]: u's' + +.. nextslide:: + +Indexing beyond the end of a sequence causes an IndexError: + +.. code-block:: ipython + + In [4]: s = [0, 1, 2, 3] + In [5]: s[4] + --------------------------------------------------------------------------- + IndexError Traceback (most recent call last) + in () + ----> 1 s[4] + + IndexError: list index out of range + + +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. + +``sequence[start:finish]`` returns all sequence[i] for which start <= i < finish: + +.. code-block:: ipython + + In [121]: s = u"a bunch of words" + In [122]: s[2] + Out[122]: u'b' + In [123]: s[6] + Out[123]: u'h' + In [124]: s[2:6] + Out[124]: u'bunc' + In [125]: s[2:7] + Out[125]: u'bunch' + +.. nextslide:: Helpful Hint + +Think of the indexes as pointing to the spaces between the items:: + + a b u n c h o f + | | | | | | | | | | + 0 1 2 3 4 5 6 7 8 9 + + + +.. nextslide:: Slicing + +You do not have to provide both ``start`` and ``finish``: + +.. code-block:: ipython + + In [6]: s = u"a bunch of words" + In [7]: s[:5] + Out[7]: u'a bun' + In [8]: s[5:] + Out[8]: u'ch of words' + +Either ``0`` or ``len(s)`` will be assumed, respectively. + +You can combine this with the negative index to get the end of a sequence: + +.. code-block:: ipython + + In [4]: s = u'this_could_be_a_filename.txt' + In [5]: s[:-4] + Out[5]: u'this_could_be_a_filename' + In [6]: s[-4:] + Out[6]: u'.txt' + + +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 last item in the slice **not** included? + +Because these lead to some nifty properties:: + + len(seq[a:b]) == b-a + + seq[:b] + seq[b:] == seq + + len(seq[:b]) == b + + len(seq[-b:]) == b + +There are very many fewer "off by one" errors as a result. + + +.. nextslide:: Slicing + +Slicing takes a third argument, ``step`` which controls which items are +returned: + +.. code-block:: ipython + + In [289]: string = u"a fairly long string" + In [290]: string[0:15] + Out[290]: u'a fairly long s' + In [291]: string[0:15:2] + Out[291]: u'afil ogs' + In [292]: string[0:15:3] + Out[292]: u'aallg' + In [293]: string[::-1] + Out[293]: u'gnirts gnol ylriaf a' + + +.. nextslide:: Slicing vs. Indexing + + +Though they share an operator, slicing and indexing have a few important +differences: + +Indexing will always return one object, slicing will return a sequence of +objects. + +Indexing past the end of a sequence will raise an error, slicing will not: + +.. code-block:: ipython + + In [129]: s = "a bunch of words" + In [130]: s[17] + ----> 1 s[17] + IndexError: string index out of range + In [131]: s[10:20] + Out[131]: ' words' + In [132]: s[20:30] + Out[132]: " + + +(demo) + +Membership +---------- + +All sequences support the ``in`` and ``not in`` membership operators: + +.. code-block:: ipython + + In [15]: s = [1, 2, 3, 4, 5, 6] + In [16]: 5 in s + Out[16]: True + In [17]: 42 in s + Out[17]: False + In [18]: 42 not in s + Out[18]: True + +.. nextslide:: Membership in Strings + +For strings, the membership operations are like ``substring`` operations in +other languages: + +.. code-block:: ipython + + In [20]: s = u"This is a long string" + In [21]: u"long" in s + Out[21]: True + +This does not work for sub-sequences of other types (can you think of why?): + +.. code-block:: ipython + + In [22]: s = [1, 2, 3, 4] + In [23]: [2, 3] in s + Out[23]: False + + +Concatenation +------------- + +Using ``+`` or ``*`` on sequences will *concatenate* them: + +.. code-block:: ipython + + In [25]: s1 = u"left" + In [26]: s2 = u"right" + In [27]: s1 + s2 + Out[27]: u'leftright' + In [28]: (s1 + s2) * 3 + Out[28]: u'leftrightleftrightleftright' + + +.. nextslide:: Multiplying and Slicing + +You can apply this concatenation to slices as well, leading to some nicely +concise code: + +from CodingBat: Warmup-1 -- front3 + +.. code-block:: python + + def front3(str): + if len(str) < 3: + return str+str+str + else: + return str[:3]+str[:3]+str[:3] + +This non-pythonic solution can also be expressed like so: + +.. code-block:: python + + def front3(str): + return str[:3] * 3 + +Length +------ + +All sequences have a length. You can get it with the ``len`` builtin: + +.. code-block:: ipython + + In [36]: s = u"how long is this, anyway?" + In [37]: len(s) + Out[37]: 25 + +Remember, Python sequences are zero-indexed, so the last index in a sequence is +``len(s) - 1``: + +.. code-block:: ipython + + In [38]: count = len(s) + In [39]: s[count] + ------------------------------------------------------------ + IndexError Traceback (most recent call last) + in () + ----> 1 s[count] + IndexError: string index out of range + +Even better: use ``s[-1]`` + + +Miscellaneous +------------- + +There are a more operations supported by all sequences + +.. nextslide:: Min and Max + +All sequences also support the ``min`` and ``max`` builtins: + +.. code-block:: ipython + + In [42]: all_letters = u"thequickbrownfoxjumpedoverthelazydog" + In [43]: min(all_letters) + Out[43]: u'a' + In [44]: max(all_letters) + Out[44]: u'z' + +Why are those the answers you get? (hint: ``ord(u'a')``) + + +.. nextslide:: Index + +All sequences also support the ``index`` method, which returns the index of the +first occurence of an item in the sequence: + +.. code-block:: ipython + + In [46]: all_letters.index(u'd') + Out[46]: 21 + +This causes a ``ValueError`` if the item is not in the sequence: + +.. code-block:: ipython + + In [47]: all_letters.index(u'A') + --------------------------------------------------------------------------- + ValueError Traceback (most recent call last) + in () + ----> 1 all_letters.index(u'A') + + ValueError: substring not found + +.. nextslide:: Count + +A sequence can also be queried for the number of times a particular item +appears: + +.. code-block:: ipython + + In [52]: all_letters.count(u'o') + Out[52]: 4 + In [53]: all_letters.count(u'the') + Out[53]: 2 + +This does not raise an error if the item you seek is not present: + +.. code-block:: ipython + + In [54]: all_letters.count(u'A') + Out[54]: 0 + + +Iteration +--------- + +.. rst-class:: center large + +More on this in a while. + + +Lists, Tuples... +================ + +.. rst-class:: center large + +The *other* sequence types. + +Lists +----- + +Lists can be constructed using list Literals (``[]``): + +.. code-block:: ipython + + In [1]: [] + Out[1]: [] + In [2]: [1,2,3] + Out[2]: [1, 2, 3] + In [3]: [1, 'a', 7.34] + Out[3]: [1, 'a', 7.34] + +Or by using the ``list`` type object as a constructor: + +.. code-block:: ipython + + In [6]: list() + Out[6]: [] + In [7]: list(range(4)) + Out[7]: [0, 1, 2, 3] + In [8]: list('abc') + Out[8]: ['a', 'b', 'c'] + + +.. nextslide:: List Elements + +The elements contained in a list need not be of a single type. + +Lists are *heterogenous*, *ordered* collections. + +Each element in a list is a value, and can be in multiple lists and have +multiple names (or no name) + +.. code-block:: ipython + + In [9]: name = u'Brian' + In [10]: a = [1, 2, name] + In [11]: b = [3, 4, name] + In [12]: a[2] + Out[12]: u'Brian' + In [13]: b[2] + Out[13]: u'Brian' + In [14]: a[2] is b[2] + Out[14]: True + + +Tuples +------ + +Tuples can be constructed using tuple literals (``()``): + +.. code-block:: ipython + + In [15]: () + Out[15]: () + In [16]: (1, 2) + Out[16]: (1, 2) + In [17]: (1, 'a', 7.65) + Out[17]: (1, 'a', 7.65) + In [18]: (1,) + Out[18]: (1,) + +.. nextslide:: Tuples and Commas... + +Tuples don't NEED parentheses... + +.. code-block:: ipython + + In [161]: t = (1,2,3) + In [162]: t + Out[162]: (1, 2, 3) + In [163]: t = 1,2,3 + In [164]: t + Out[164]: (1, 2, 3) + In [165]: type(t) + Out[165]: tuple + +.. nextslide:: Tuples and Commas... + +But they *do* need commas...! + +.. code-block:: ipython + + In [156]: t = ( 3 ) + In [157]: type(t) + Out[157]: int + 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: + +.. code-block:: ipython + + In [20]: tuple() + Out[20]: () + In [21]: tuple(range(4)) + Out[21]: (0, 1, 2, 3) + In [22]: tuple('garbanzo') + Out[22]: ('g', 'a', 'r', 'b', 'a', 'n', 'z', 'o') + + +.. nextslide:: Tuple Elements + +The elements contained in a tuple need not be of a single type. + +Tuples are *heterogenous*, *ordered* collections. + +Each element in a tuple is a value, and can be in multiple tuples and have +multiple names (or no name) + +.. code-block:: ipython + + In [23]: name = u'Brian' + In [24]: other = 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], end=' ') + ....: + False False True + +.. nextslide:: Lists vs. Tuples + +.. rst-class:: center large + +So Why Have Both? + + +Mutability +========== + +.. image:: /_static/transmogrifier.jpg + :width: 35% + :alt: Presto change-o + +.. rst-class:: credit + +image from flickr by `illuminaut`_, (CC by-nc-sa) + +.. _illuminaut: https://www.flickr.com/photos/illuminaut/3595530403 + + +Mutability in Python +-------------------- + +All objects in Python fall into one of two camps: + +* Mutable +* Immutable + +Objects which are mutable may be *changed in place*. + +Objects which are immutable may not be changed. + + +.. nextslide:: The Types We Know + +========= ======= +Immutable Mutable +========= ======= +Unicode List +String +Integer +Float +Tuple +========= ======= + + +.. nextslide:: Lists Are Mutable + +Try this out: + +.. code-block:: ipython + + In [28]: food = [u'spam', u'eggs', u'ham'] + In [29]: food + Out[29]: [u'spam', u'eggs', u'ham'] + In [30]: food[1] = u'raspberries' + In [31]: food + Out[31]: [u'spam', u'raspberries', u'ham'] + + +.. nextslide:: Tuples Are Not + +And repeat the exercise with a Tuple: + +.. code-block:: ipython + + In [32]: food = (u'spam', u'eggs', u'ham') + In [33]: food + Out[33]: (u'spam', u'eggs', u'ham') + In [34]: food[1] = u'raspberries' + --------------------------------------------------------------------------- + TypeError Traceback (most recent call last) + in () + ----> 1 food[1] = u'raspberries' + + TypeError: 'tuple' object does not support item assignment + + +.. nextslide:: Watch When Binding + +This property means you need to be aware of what you are doing with your lists: + +.. code-block:: ipython + + In [36]: original = [1, 2, 3] + In [37]: altered = original + In [38]: for i in range(len(original)): + ....: if True: + ....: altered[i] += 1 + ....: + +Perhaps we want to check to see if altered has been updated, as a flag for +whatever condition caused it to be updated. + +What is the result of this code? + +.. nextslide:: Perhaps Not What You Expect + +Our ``altered`` list has been updated: + +.. code-block:: ipython + + In [39]: altered + Out[39]: [2, 3, 4] + +But so has the ``original`` list: + +.. code-block:: ipython + + In [40]: original + Out[40]: [2, 3, 4] + +Why? + + +.. nextslide:: Other Gotchas + +Easy container setup, or deadly trap? + +(note: you can nest lists to make a 2D-ish array) + +.. code-block:: ipython + + In [13]: bins = [ [] ] * 5 + + In [14]: bins + Out[14]: [[], [], [], [], []] + + In [15]: words = [u'one', u'three', u'rough', u'sad', u'goof'] + + In [16]: for word in words: + ....: bins[len(word)-1].append(word) + ....: + +So, what is going to be in ``bins`` now? + +.. nextslide:: There is Only **One** Bin + +.. code-block:: ipython + + In [65]: bins + Out[65]: + [[u'one', u'three', u'rough', u'sad', u'goof'], + [u'one', u'three', u'rough', u'sad', u'goof'], + [u'one', u'three', u'rough', u'sad', u'goof'], + [u'one', u'three', u'rough', u'sad', u'goof'], + [u'one', u'three', u'rough', u'sad', u'goof']] + +We multiplied a sequence containing a single *mutable* object. + +We got a list containing five pointers to a single *mutable* object. + + +.. nextslide:: Mutable Default Argument + +Watch out especially for passing mutable objects as default values for function parameters: + +.. code-block:: ipython + + In [71]: def accumulator(count, list=[]): + ....: for i in range(count): + ....: list.append(i) + ....: return list + ....: + In [72]: accumulator(5) + Out[72]: [0, 1, 2, 3, 4] + In [73]: accumulator(7) + Out[73]: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6] + + +Mutable Sequence Methods +======================== + +.. rst-class:: left + +In addition to all the methods supported by sequences we've seen above, mutable sequences (the List), have a number of other methods that are +used to change the list. + +You can find all these in the Standard Library Documentation: + +http://docs.python.org/2/library/stdtypes.html#mutable-sequence-types + +Assignment +----------- + +You've already seen changing a single element of a list by assignment. + +Pretty much the same as "arrays" in most languages: + +.. code-block:: ipython + + In [100]: list = [1, 2, 3] + In [101]: list[2] = 10 + In [102]: list + Out[102]: [1, 2, 10] + + +Growing the List +---------------- + +``.append()``, ``.insert()``, ``.extend()`` + +.. code-block:: ipython + + In [74]: food = [u'spam', u'eggs', u'ham'] + In [75]: food.append(u'sushi') + In [76]: food + Out[76]: [u'spam', u'eggs', u'ham', u'sushi'] + In [77]: food.insert(0, u'beans') + In [78]: food + Out[78]: [u'beans', u'spam', u'eggs', u'ham', u'sushi'] + In [79]: food.extend([u'bread', u'water']) + In [80]: food + Out[80]: [u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water'] + + +.. nextslide:: More on Extend + +You can pass any sequence to ``.extend()``: + +.. code-block:: ipython + + In [85]: food + Out[85]: [u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water'] + In [86]: food.extend(u'spaghetti') + In [87]: food + Out[87]: + [u'beans', u'spam', u'eggs', u'ham', u'sushi', u'bread', u'water', + u's', u'p', u'a', u'g', u'h', u'e', u't', u't', u'i'] + + +Shrinking the List +------------------ + +``.pop()``, ``.remove()`` + +.. code-block:: ipython + + In [203]: food = ['spam', 'eggs', 'ham', 'toast'] + In [204]: food.pop() + Out[204]: 'toast' + In [205]: food.pop(0) + Out[205]: 'spam' + In [206]: food + Out[206]: ['eggs', 'ham'] + In [207]: food.remove('ham') + In [208]: food + Out[208]: ['eggs'] + +.. nextslide:: Removing Chunks of a List + +You can also delete *slices* of a list with the ``del`` keyword: + +.. code-block:: ipython + + In [92]: nums = range(10) + In [93]: nums + Out[93]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + In [94]: del nums[1:6:2] + In [95]: nums + Out[95]: [0, 2, 4, 6, 7, 8, 9] + In [96]: del nums[-3:] + In [97]: nums + Out[97]: [0, 2, 4, 6] + + +Copying Lists +------------- + +You can make copies of part of a list using *slicing*: + +.. code-block:: ipython + + In [227]: food = ['spam', 'eggs', 'ham', 'sushi'] + In [228]: some_food = food[1:3] + In [229]: some_food[1] = 'bacon' + In [230]: food + Out[230]: ['spam', 'eggs', 'ham', 'sushi'] + In [231]: some_food + Out[231]: ['eggs', 'bacon'] + +If you provide *no* arguments to the slice, it makes a copy of the entire list: + +.. code-block:: ipython + + In [232]: food + Out[232]: ['spam', 'eggs', 'ham', 'sushi'] + In [233]: food2 = food[:] + In [234]: food is food2 + Out[234]: False + + +.. nextslide:: Shallow Copies + +The copy of a list made this way is a *shallow copy*. + +The list is itself a new object, but the objects it contains are not. + +*Mutable* objects in the list can be mutated in both copies: + +.. code-block:: ipython + + In [249]: food = ['spam', ['eggs', 'ham']] + In [251]: food_copy = food[:] + In [252]: food[1].pop() + Out[252]: 'ham' + In [253]: food + Out[253]: ['spam', ['eggs']] + In [256]: food.pop(0) + Out[256]: 'spam' + In [257]: food + Out[257]: [['eggs']] + In [258]: food_copy + Out[258]: ['spam', ['eggs']] + + +.. nextslide:: Copies Solve Problems + +Consider this common pattern: + +.. code-block:: python + + for x in somelist: + 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. + + +.. nextslide:: The Problem + +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) + .....: + +.. rst-class:: build +.. container:: + + What is the expected outcome of this code? + + .. code-block:: ipython + + In [124]: list + Out[124]: [1, 3, 5, 7, 9] + + Was that what you expected? + +.. nextslide:: The Solution + +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]: [] + + +.. nextslide:: Just Say It, Already + +Okay, so we've done this a bunch already, but let's state it out loud. + +You can iterate over a sequence. + +.. code-block:: python + + for element in sequence: + do_something(element) + + +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. + +``.reverse()`` + +.. code-block:: ipython + + In [129]: food = [u'spam', u'eggs', u'ham'] + In [130]: food.reverse() + In [131]: food + Out[131]: [u'ham', u'eggs', u'spam'] + +``.sort()`` + +.. code-block:: ipython + + In [132]: food.sort() + In [133]: food + Out[133]: [u'eggs', u'ham', u'spam'] + +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: + +.. code-block:: ipython + + In [137]: def third_letter(string): + .....: return string[2] + .....: + In [138]: food.sort(key=third_letter) + In [139]: food + Out[139]: [u'spam', u'eggs', u'ham'] + + + +List Performance +---------------- + +.. rst-class:: build + +* indexing is fast and constant time: O(1) +* x in s 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) + + * append(), pop() + +* 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 + +http://wiki.python.org/moin/TimeComplexity + + +Choosing Lists or Tuples +------------------------ + +Here are a few guidelines on when to choose a list or a tuple: + +* If it needs to mutable: list + +* If it needs to be immutable: tuple + + * (safety when passing to a function) + +Otherwise ... taste and convention + + +.. nextslide:: Convention + +Lists are Collections (homogeneous): +-- contain values of the same type +-- simplifies iterating, sorting, etc + +tuples are mixed types: +-- Group multiple values into one logical thing +-- Kind of like simple C structs. + + +.. nextslide:: Other Considerations + +.. rst-class:: build + +* Do the same operation to each element? + + * list + +* Small collection of values which make a single logical item? + + * tuple + +* To document that these values won't change? + + * tuple + +* Build it iteratively? + + * list + +* Transform, filter, etc? + + * list + + +More Documentation +------------------ + +For more information, read the list docs: + +http://docs.python.org/2/library/stdtypes.html#mutable-sequence-types + +(actually any mutable sequence....) + + +Iteration +========= + +.. rst-class:: build + +Repetition, Repetition, Repetition, Repe... + + +For Loops +--------- + +We've seen simple iteration over a sequence with ``for ... in``: + +.. code-block:: ipython + + In [170]: for x in "a string": + .....: print(x) + .....: + a + + s + t + r + i + n + g + + +.. nextslide:: No Indexing Required + +Contrast this with other languages, where you must build and use an ``index``: + +.. code-block:: javascript + + for(var i=0; i 50: + .....: break + .....: + 0 1 2 3 4 5... 46 47 48 49 50 51 + +.. nextslide:: Continue + +The ``continue`` keyword will skip later statements in the loop block, but +allow iteration to continue: + +.. code-block:: ipython + + In [143]: for in in range(101): + .....: if i > 50: + .....: break + .....: if i < 25: + .....: continue + .....: print(i), + .....: + 25 26 27 28 29 ... 41 42 43 44 45 46 47 48 49 50 + +.. nextslide:: Else + +For loops can also take an optional ``else`` block. + +Executed only when the loop exits normally (not via break): + +.. code-block:: ipython + + In [147]: for x in range(10): + .....: if x == 11: + .....: break + .....: else: + .....: print(u'finished') + finished + In [148]: for x in range(10): + .....: if x == 5: + .....: print(x) + .....: break + .....: else: + .....: print(u'finished') + 5 + +This is a really nice unique Python feature! + +While Loops +----------- + +The ``while`` keyword is for when you don't know how many loops you need. + +It continues to execute the body until condition is not ``True``:: + + while a_condition: + some_code + in_the_body + +.. nextslide:: ``while`` vs. ``for`` + +``while`` is more general than ``for`` + +-- you can always express ``for`` as ``while``, + +but not always vice-versa. + +``while`` is more error-prone -- requires some care to terminate + +loop body must make progress, so condition can become ``False`` + +potential error -- infinite loops: + +.. code-block:: python + + i = 0; + while i < 5: + print(i) + + +.. nextslide:: Terminating a while Loop + +Use ``break``: + +.. code-block:: ipython + + In [150]: while True: + .....: i += 1 + .....: if i > 10: + .....: break + .....: print(i, end=' ') + .....: + 1 2 3 4 5 6 7 8 9 10 + +.. nextslide:: Terminating a while Loop + +Set a flag: + +.. code-block:: ipython + + In [156]: import random + In [157]: keep_going = True + In [158]: while keep_going: + .....: num = random.choice(range(5)) + .....: print(num) + .....: if num == 3: + .....: keep_going = False + .....: + 3 + +.. nextslide:: Terminating a While Loop + +Use a condition: + +.. code-block:: ipython + + In [161]: while i < 10: + .....: i += random.choice(range(4)) + .....: print(i) + .....: + 0 0 2 3 4 6 8 8 8 9 12 + + +Similarities +------------ + +Both ``for`` and ``while`` loops can use ``break`` and ``continue`` for +internal flow control. + +Both ``for`` and ``while`` loops can have an optional ``else`` block + +In both loops, the statements in the ``else`` block are only executed if the +loop terminates normally (no ``break``) + + +User Input +============== + +.. rst-class:: left + +For some of your homework, you'll need to interact with a user at the +command line. + +.. rst-class:: left + +There's a nice builtin function to do this - ``input``: + +.. rst-class:: left + +.. code-block:: python + + In [196]: fred = raw_input(u'type something-->') + type something-->;alksdjf + In [197]: fred + Out[197]: ';alksdjf' + +.. rst-class:: left + +This will display a prompt to the user, allowing them to input text and +allowing you to bind that input to a symbol. + +Pair Programming +================ + +With a partner, write a guessing game that repeatedly asks the user +to guess a number from 1 to 100 until they get the number correct. +If the guess is too high, print "Too high!". +If the guess is too low, print "Too low!". +Otherwise, print "Congrats! You're a winner." + + +String Features +================= + +.. rst-class:: center large + + Fun with Strings + + +Manipulations +------------- + +``split`` and ``join``: + +.. code-block:: ipython + + In [167]: csv = "comma, separated, values" + In [168]: csv.split(', ') + Out[168]: ['comma', 'separated', 'values'] + In [169]: psv = '|'.join(csv.split(', ')) + In [170]: psv + Out[170]: 'comma|separated|values' + + +.. nextslide:: Case Switching + +.. code-block:: ipython + + In [171]: sample = u'A long string of words' + In [172]: sample.upper() + Out[172]: u'A LONG STRING OF WORDS' + In [173]: sample.lower() + Out[173]: u'a long string of words' + In [174]: sample.swapcase() + Out[174]: u'a LONG STRING OF WORDS' + In [175]: sample.title() + Out[175]: u'A Long String Of Words' + + +.. nextslide:: Testing + +.. code-block:: ipython + + In [181]: number = u"12345" + In [182]: number.isnumeric() + Out[182]: True + In [183]: number.isalnum() + Out[183]: True + In [184]: number.isalpha() + Out[184]: False + In [185]: fancy = u"Th!$ $tr!ng h@$ $ymb0l$" + In [186]: fancy.isalnum() + Out[186]: False + + +Ordinal values +-------------- + +"ASCII" values: 1-127 + +"ANSI" values: 1-255 + +To get the value: + +.. code-block:: ipython + + In [109]: for i in 'Chris': + .....: print(ord(i), end=' ') + 67 104 114 105 115 + In [110]: for i in (67,104,114,105,115): + .....: print(chr(i), end=' ') + C h r i s + + +Building Strings +---------------- + +You can, but please don't do this: + +.. code-block:: python + + 'Hello ' + name + '!' + +Do this instead: + +.. code-block:: python + + 'Hello %s!' % 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 + + +.. nextslide:: String Formatting + +The string format operator: ``%`` + +.. code-block:: ipython + + In [261]: u"an integer is: %i" % 34 + Out[261]: u'an integer is: 34' + In [262]: u"a floating point is: %f" % 34.5 + Out[262]: u'a floating point is: 34.500000' + In [263]: u"a string is: %s" % u"anything" + Out[263]: u'a string is: anything' + +.. nextslide:: More Placeholders + +Multiple placeholders: + +.. code-block:: ipython + + In [264]: u"the number %s is %i" % (u'five', 5) + Out[264]: u'the number five is 5' + In [266]: u"the first 3 numbers are: %i, %i, %i" % (1,2,3) + Out[266]: u'the first 3 numbers are: 1, 2, 3' + +The counts must agree: + +.. code-block:: ipython + + In [187]: u"string with %i formatting %s" % (1, ) + --------------------------------------------------------------------------- + ... + TypeError: not enough arguments for format string + + +.. nextslide:: + +Named placeholders: + +.. code-block:: ipython + + In [191]: u"Hello, %(name)s, whaddaya know?" % {u'name': "Joe"} + Out[191]: u'Hello, Joe, whaddaya know?' + +You can use values more than once, and skip values: + +.. code-block:: ipython + + In [193]: u"Hi, %(name)s. Howzit, %(name)s?" % {u'name': u"Bob", u'age': 27} + Out[193]: u'Hi, Bob. Howzit, Bob?' + + +.. 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]: u"Hello, {}, how's your {}".format(u"Bob", u"wife") + Out[194]: u"Hello, Bob, how's your wife" + In [195]: u"Hi, {name}. How's your {relation}?".format(name=u'Bob', relation=u'wife') + Out[195]: u"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. + +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 + + +A couple other nifty utilties with for loops: + +**tuple unpacking:** + +remember this? + +.. code-block:: python + + x, y = 3, 4 + +You can do that in a for loop, also: + +.. code-block:: ipython + + In [3]: from __future__ import print_function + In [4]: l = [(1, 2), (3, 4), (5, 6)] + In [5]: for i, j in l: + print("i:%i, j:%i" % (i, j)) + + i:1, j:2 + i:3, j:4 + i:5, j:6 + +Looping through two loops at once: +---------------------------------- + +**zip:** + +.. code-block:: ipython + + In [10]: l1 = [1, 2, 3] + In [11]: l2 = [3, 4, 5] + In [12]: for i, j in zip(l1, l2): + ....: print("i:%i, j:%i" % (i, j)) + ....: + i:1, j:3 + i:2, j:4 + i:3, j:5 + + + +Homework comments +----------------- + +Building up a long string. + +The obvious thing to do is something like:: + + msg = u"" + for piece in list_of_stuff: + msg += piece + +But: strings are immutable -- python needs to create a new string each time you +add a piece -- not efficient:: + + msg = [] + for piece in list_of_stuff: + msg.append(piece) + u" ".join(msg) + +appending to lists is efficient -- and so is the join() method of strings. + +.. nextslide:: + +What is ``assert`` for? + +Testing -- NOT for issues expected to happen operationally:: + + assert m >= 0 + +in operational code should be:: + + if m < 0: + raise ValueError + +I'll cover Exceptions later this class... + +(Asserts get ignored if optimization is turned on!) + + +A little warm up +================= + +Fun with strings +------------------ + +* Rewrite: ``the first 3 numbers are: %i, %i, %i"%(1,2,3)`` + + - 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" `` + +Homework Review +====================== + +Someone volunteer to have their HW 8 debugged in class. + +Design critique in class. + + +Today's Puzzle: Trigrams +======================== + +N-grams are a way to study word associations + +https://books.google.com/ngrams + +.. nextslide:: + +* Coding Kata 14 - Dave Thomas + + http://codekata.com/kata/kata14-tom-swift-under-the-milkwood/ + + and in this doc: + + http://codefellows.github.io/sea-c45-python/supplements/kata_fourteen.html + +* Use "The Travels of Marco Polo the Venetian" as input: + + http://codefellows.github.io/sea-c34-python/_downloads/marco-polo.txt + +.. nextslide:: + +* Our task today: read in the words from a large text file, + create a dictionary of trigrams. + +* Write pseudo code and create a design. + +* Use dictionaries, exceptions, file reading/writing. + + + +Dictionaries and Sets +===================== + +Dictionary +---------- + +Python calls it a ``dict`` + +Other languages call it: + +* dictionary +* associative array +* map +* hash table +* hash +* key-value pair + + +Dictionary Constructors +----------------------- + +.. code-block:: python + + >>> {'key1': 3, 'key2': 5} + {'key1': 3, 'key2': 5} + >>> dict([('key1', 3),('key2', 5)]) + {'key1': 3, 'key2': 5} + >>> dict(key1=3, key2=5) + {'key1': 3, 'key2': 5} + >>> d = {} + >>> d['key1'] = 3 + >>> d['key2'] = 5 + >>> d + {'key1': 3, 'key2': 5} + +Dictionary Indexing +------------------- + +.. code-block:: python + + >>> d = {'name': 'Brian', 'score': 42} + >>> d['score'] + 42 + >>> d = {1: 'one', 0: 'zero'} + >>> d[0] + 'zero' + >>> d['non-existing key'] + Traceback (most recent call last): + File "", line 1, in + KeyError: 'non-existing key' + + +.. nextslide:: + +Keys can be any **immutable** object: + +* number +* string +* tuple + +.. code-block:: ipython + + In [325]: d[3] = 'string' + In [326]: d[3.14] = 'pi' + In [327]: d['pi'] = 3.14 + In [328]: d[ (1,2,3) ] = 'a tuple key' + In [329]: d[ [1,2,3] ] = 'a list key' + TypeError: unhashable type: 'list' + + +Actually -- any "hashable" type. + + +.. nextslide:: Hashing + +Hash functions convert arbitrarily large data to a small proxy (usually int) + +.. rst-class:: build +.. container:: + + Always return the same proxy for the same input + + MD5, SHA, etc + + Dictionaries hash the key to an integer proxy and use it to find the key + and value. + + Key lookup is efficient because the hash function leads directly to a + bucket with very few keys (often just one) + + What would happen if the proxy changed after storing a key? + + Hashability requires immutability + + Key lookup is very efficient + + Same average time regardless of size + + +.. nextslide:: Dictionary indexing + + +Note: Python name look-ups are implemented with dict -- it's highly optimized + +.. rst-class:: build +.. container:: + + Key to value: + + * lookup is one way + + Value to key: + + * requires visiting the whole dict + + If you need to check dict values often, create another dict or set + + (up to you to keep them in sync) + + +Dictionary Ordering (not) +------------------------- + +Dictionaries have no defined order + +.. code-block:: ipython + + In [352]: d = {'one':1, 'two':2, 'three':3} + In [353]: d + Out[353]: {'one': 1, 'three': 3, 'two': 2} + In [354]: d.keys() + Out[354]: ['three', 'two', 'one'] + +.. rst-class:: build +.. container:: + + You will be fooled by what you see into thinking that the order of pairs + can be relied on. + + It cannot. + +Dictionary Iterating +-------------------- + +``for`` iterates over the keys + +.. code-block:: ipython + + In [15]: d = {'name': 'Brian', 'score': 42} + + In [16]: for x in d: + ....: print(x) + ....: + score + name + + +(note the different order...) + +dict keys and values +-------------------- + +.. code-block:: ipython + + In [20]: d = {'name': 'Brian', 'score': 42} + + In [21]: d.keys() + Out[21]: ['score', 'name'] + + In [22]: d.values() + Out[22]: [42, 'Brian'] + + In [23]: d.items() + Out[23]: [('score', 42), ('name', 'Brian')] + + +dict keys and values +-------------------- + +Iterating on everything + +.. code-block:: ipython + + In [26]: d = {'name': 'Brian', 'score': 42} + + In [27]: for k, v in d.items(): + ....: print("%s: %s" % (k,v)) + ....: + score: 42 + name: Brian + + +Dictionary Performance +----------------------- + +* indexing is fast and constant time: O(1) + +* Membership (``x in s``) constant time: O(1) + +* visiting all is proportional to n: O(n) + +* inserting is constant time: O(1) + +* deleting is constant time: O(1) + +http://wiki.python.org/moin/TimeComplexity + + +Other dict operations: +---------------------- + +See them all here: + +https://docs.python.org/2/library/stdtypes.html#mapping-types-dict + +Is it in there? + +.. code-block:: ipython + + In [5]: d + Out[5]: {'that': 7, 'this': 5} + + In [6]: 'that' in d + Out[6]: True + + In [7]: 'this' not in d + Out[7]: False + +Membership is on the keys. + +.. nextslide:: Getting Something + +(like indexing) + +.. code-block:: ipython + + In [9]: d.get('this') + Out[9]: 5 + +.. rst-class:: build +.. container:: + + But you can specify a default + + .. code-block:: ipython + + In [11]: d.get(u'something', u'a default') + Out[11]: u'a default' + + Never raises an Exception (default default is None) + +.. nextslide:: Iterating + +.. code-block:: ipython + + In [13]: for item in d.iteritems(): + ....: print item + ....: + ('this', 5) + ('that', 7) + In [15]: for key in d.iterkeys(): + ....: print key + ....: + this + that + In [16]: for val in d.itervalues(): + ....: print val + ....: + 5 + 7 + +the ``iter*`` methods *don't actually create the lists*. + +.. nextslide:: Popping + +gets the value at a given key while removing it + +.. rst-class:: build +.. container:: + + Pop just a key + + .. code-block:: ipython + + In [19]: d.pop('this') + Out[19]: 5 + In [20]: d + Out[20]: {'that': 7} + + pop out an arbitrary key, value pair + + .. code-block:: ipython + + In [23]: d.popitem() + Out[23]: ('that', 7) + In [24]: d + Out[24]: {} + +.. nextslide:: Handy Method + +``setdefault(key[, default])`` + +gets the value if it's there, sets it if it's not + +.. code-block:: ipython + + In [26]: d = {} + + In [27]: d.setdefault(u'something', u'a value') + Out[27]: u'a value' + In [28]: d + Out[28]: {u'something': u'a value'} + In [29]: d.setdefault(u'something', u'a different value') + Out[29]: u'a value' + In [30]: d + Out[30]: {u'something': u'a value'} + +.. nextslide:: + +dict View objects: + +Like ``keys()``, ``values()``, ``items()``, but maintain a link to the original dict + +.. code-block:: ipython + + In [47]: d + Out[47]: {u'something': u'a value'} + In [48]: item_view = d.viewitems() + In [49]: item_view + Out[49]: dict_items([(u'something', u'a value')]) + In [50]: d['something else'] = u'another value' + + In [51]: item_view + Out[51]: dict_items([('something else', u'another value'), (u'something', u'a value')]) + +Cheeseburger Therapy +==================== + +Four new sessions were requested on Monday and Tuesday night. + +Unfortunately, we couldn't respond in time! + +If you'd still like to try it out, please start a new +session tonight from 9-10pm. + +Homeworks, due Next Monday +========================== + +HW 11: Mailroom Madness +HW 12: Dictionaries and Files +HW 13: Trigrams diff --git a/docs/session5.rst b/docs/session5.rst new file mode 100644 index 0000000..d84c7e4 --- /dev/null +++ b/docs/session5.rst @@ -0,0 +1,1691 @@ + +.. Foundations 2: Python slides file, created by + hieroglyph-quickstart on Wed Apr 2 18:42:06 2014. + + +*************************************************************************** +Session Five: Exceptions, Files, Arguments, Comprehensions +*************************************************************************** + + +Review/Questions +================ + +.. rst-class:: left +.. container:: + + .. rst-class:: build + + * Dictionaries + * String Formatting + * Exceptions + * Files, etc. + + +Homework Review +--------------- + +Homework Questions? + +Solutions to the dict/set lab, and some others in the class repo in: +``Solutions`` + +A few tidbits: + +.. nextslide:: Sorting Dictionaries: + +The ``dict`` isn't sorted, so what if you want to do something in a sorted way? + +.. rst-class:: build +.. container:: + + The "old" way: + + .. code-block:: python + + keys = d.keys() + keys.sort() + for key in keys: + ... + + .. code-block:: python + + collections.OrderedDict + sorted() + + (demo) + +Exceptions +========== + +.. rst-class:: left +.. container:: + + 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(u"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(u"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(u"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 + +.. rst-class:: build +.. container:: + + 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 + +.. rst-class:: build +.. container:: + + 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... + +.. rst-class:: build +.. container:: + + .. 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. + +.. rst-class:: build +.. container:: + + Example (for last week's ackerman homework):: + + if (not isinstance(m, int)) or (not isinstance(n, int)): + raise ValueError + + Is the *value* of 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) + + +File Reading and Writing +======================== + +Files +----- + +Text Files + +.. code-block:: python + + import io + f = io.open('secrets.txt', encoding='utf-8') + secret_data = f.read() + f.close() + +``secret_data`` is a (unicode) string + +``encoding`` defaults to ``sys.getdefaultencoding()`` -- often NOT what you +want. + +(There is also the regular ``open()`` built in, but it won't handle Unicode for +you...) + +.. nextslide:: + +Binary Files + +.. code-block:: python + + f = io.open('secrets.bin', 'rb') + secret_data = f.read() + f.close() + +``secret_data`` is a byte string + +(with arbitrary bytes in it -- well, not arbitrary -- whatever is in the file.) + +(See the ``struct`` module to unpack formatted binary data) + + +.. nextslide:: + +File Opening Modes + +.. code-block:: python + + f = io.open('secrets.txt', [mode]) + 'r', 'w', 'a' + '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 on the +Python docs. But these BSD docs make it pretty clear: + +http://www.manpagez.com/man/3/fopen/ + +**Gotcha** -- 'w' modes always clear the file + +.. nextslide:: Text File Notes + +Text is default + +* Newlines are translated: ``\r\n -> \n`` +* -- reading and writing! +* Use \*nix-style in your code: ``\n`` +* ``io.open()`` returns various "stream" objects -- but they act like file + objects. +* In text mode, io.open() defaults to "Universal" newline mode. + + +Gotcha: + +* no difference between text and binary on \*nix +* breaks on Windows + + +.. nextslide:: Other parameters to ``io.open()``: + +``io.open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True)`` + +* ``file`` is generally a file name or full path + +* ``mode`` is the mode for opening: 'r', 'w', etc. + +* ``buffering`` controls the buffering mode (0 for no buffering) + +* ``encoding`` sets the unicode encoding -- only for text files -- when set, + you can ONLY write unicode object to the file. + +* ``errors`` sets the encoding error mode: 'strict', 'ignore', 'replace',... + +* ``newline`` controls Universal Newline mode: lets you write DOS-type files on + \*nix, for instance (text mode only). + +* ``closedfd`` controls close() behavior if a file descriptor, rather than a + name is passed in (advanced usage!) + +(https://docs.python.org/2/library/io.html?highlight=io.open#io.open) + + +File Reading +------------ + +Reading part of a file + +.. code-block:: python + + header_size = 4096 + f = open('secrets.txt') + secret_header = f.read(header_size) + secret_rest = f.read() + f.close() + +.. nextslide:: + + +Common Idioms + +.. code-block:: python + + for line in io.open('secrets.txt'): + print line + +.. rst-class:: build +.. container:: + + (the file object is an iterator!) + + .. code-block:: python + + f = io.open('secrets.txt') + while True: + line = f.readline() + if not line: + break + do_something_with_line() + + +File Writing +------------ + +.. code-block:: python + + outfile = io.open('output.txt', 'w') + for i in range(10): + outfile.write("this is line: %i\n"%i) + + +File Methods +------------ + +Commonly Used Methods + +.. code-block:: python + + f.read() f.readline() f.readlines() + + f.write(str) f.writelines(seq) + + f.seek(offset) f.tell() + + f.flush() + + f.close() + + +File Like Objects +----------------- + + +Many classes implement the file interface: + +.. rst-class:: build + +* loggers +* ``sys.stdout`` +* ``urllib.open()`` +* pipes, subprocesses +* StringIO + +https://docs.python.org/2/library/stdtypes.html#file-objects + +StringIO +-------- + +.. code-block:: python + + In [417]: import StringIO + In [420]: f = StringIO.StringIO() + In [421]: f.write(u"somestuff") + In [422]: f.seek(0) + In [423]: f.read() + Out[423]: 'somestuff' + +(handy for testing file handling code...) + + +Paths and Directories +===================== + +Paths +----- + +Paths are generally handled with simple strings (or Unicode strings) + +Relative paths: + +.. code-block:: python + + u'secret.txt' + u'./secret.txt' + +Absolute paths: + +.. code-block:: python + + u'/home/chris/secret.txt' + + +Either work with ``open()`` , etc. + +(working directory only makes sense with command-line programs...) + +os module +---------- + +.. code-block:: python + + os.getcwd() -- os.getcwdu() (u for Unicode) + chdir(path) + os.path.abspath() + os.path.relpath() + + +.. nextslide:: os.path module + +.. code-block:: python + + os.path.split() + os.path.splitext() + os.path.basename() + os.path.dirname() + os.path.join() + + +(all platform independent) + +.. nextslide:: directories + +.. code-block:: python + + os.listdir() + os.mkdir() + os.walk() + +(higher level stuff in ``shutil`` module) + +pathlib +------- + +``pathlib`` is a new 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 + +.. nextslide:: + +All the stuff in os.path and more: + +.. code-block:: ipython + + In [64]: import pathlib + In [65]: pth = pathlib.Path('./') + 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') + In [68]: for f in pth.iterdir(): + print f + junk2.txt + junkfile.txt + ... + +Puzzle and Mid-point Activities +=============================== + +* Check in attendance. +* Copy and paste your HW 12 (Dictionary and Files) homework code from +Interactive Python textbook into Canvas. +* Puzzle: Fizzbuzz + +* Look up the ``%`` operator. What is the value of the following? + + * ``10 % 7 == 3`` + * ``14 % 7 == 0`` + +* Write a program that prints the numbers from 1 to 100 inclusive. But for + multiples of three print "Fizz" instead of the number and for the multiples + of five print "Buzz". For numbers which are multiples of both three and five + print "FizzBuzz" instead. + + +Advanced Argument Passing +========================= + +Keyword arguments +----------------- + +When defining a function, you can specify only what you need -- in any order + +.. code-block:: ipython + + In [150]: from __future__ import print_function + In [151]: def fun(x, y=0, z=0): + .....: print(x, y, z, end=" ") + .....: + 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(u"x is: %s" % x) + .....: + In [158]: fun() + x is: 4 + +.. nextslide:: But Remember + +Defaults are evaluated when the function is defined + +.. code-block:: ipython + + In [156]: y = 4 + In [157]: def fun(x=y): + print(u"x is: %s" % 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: + +.. rst-class:: build +.. container:: + + * a tuple (positional arguments) + * a dict (keyword arguments) + + .. code-block:: python + + In [1]: def f(x, y, w=0, h=0): + ...: msg = u"position: %s, %s -- shape: %s, %s" + ...: print(msg % (x, y, w, h)) + ...: + In [2]: position = (3, 4) + In [3]: size = {'h': 10, 'w': 20} + In [4]: 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 + + In [10]: def f(*args, **kwargs): + ....: print(u"the positional arguments are: %s" % unicode(args)) + ....: print(u"the optional arguments are: %s" % unicode(kwargs)) + ....: + In [11]: f(2, 3, this=5, that=7) + the positional arguments are: (2, 3) + the optional arguments are: {'this': 5, 'that': 7} + +Passing a dict to the ``string.format()`` method +------------------------------------------------ + +Now that you know that keyword args are really a dict, you can do this nifty +trick: + +.. rst-class:: build +.. container:: + + .. container:: + + The ``format`` method takes keyword arguments: + + .. code-block:: ipython + + In [24]: u"My name is {first} {last}".format(last=u"Ewing", first=u"Cris") + Out[24]: u'My name is Cris Ewing' + + .. container:: + + Build a dict of the keys and values: + + .. code-block:: ipython + + In [25]: d = {u"last": u"Ewing", u"first": u"Cris"} + + .. container:: + + And pass to ``format()``with ``**`` + + .. code-block:: ipython + + In [26]: u"My name is {first} {last}".format(**d) + Out[26]: u'My name is Cris Ewing' + +LAB +--- + +Let's do this right now: + +.. rst-class:: build +.. container:: + + keyword arguments + + .. rst-class:: build + + * Write a function that has four optional parameters (with defaults): + + - foreground_color + - background_color + - link_color + - visited_link_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`` + + +A bit more on mutability (and copies) +===================================== + +.. rst-class:: left + +We've talked about this: mutable objects can have their contents changed in +place. + +.. rst-class:: left build +.. container:: + + 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. + +mutable objects +--------------- + +What if we set an element to a new value? + +.. rst-class:: build +.. container:: + + .. 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? + +.. rst-class:: build +.. container:: + + .. 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']] + + uh oh! mutating an element in one list mutated the one in the other list. + +.. nextslide:: + +Why is that? + +.. rst-class:: build +.. container:: + + .. 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) + + 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). + +.. rst-class:: build +.. container:: + + 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: + +.. rst-class:: build +.. container:: + + .. 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 + +.. rst-class:: build +.. container:: + + 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: + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [11]: def fun(x, a=[]): + ....: a.append(x) + ....: print(a) + ....: + + This makes sense: maybe you'd pass in a list, but the default is an empty list. + + .. container:: + + But: + + .. code-block:: ipython + + In [12]: fun(3) + [3] + + In [13]: fun(4) + [3, 4] + + Huh?! + +.. nextslide:: + +Remember: + +.. rst-class:: build + +* the default argument is defined when the function is created +* there will be *only one list* +* every time the function is called, the *same one list* is used. + +.. rst-class:: build +.. container:: + + 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 + + +List and Dict Comprehensions +============================ + +.. rst-class:: left +.. container:: + + A bit of functional programming + + .. rst-class:: build + .. container:: + + consider this common ``for`` loop structure: + + .. code-block:: python + + new_list = [] + for variable in a_list: + new_list.append(expression) + + This can be expressed with a single line using a "list comprehension" + + .. code-block:: python + + new_list = [expression for variable in a_list] + +List Comprehensions +------------------- + +What about nested for loops? + +.. rst-class:: build +.. container:: + + .. code-block:: python + + new_list = [] + for var in a_list: + for var2 in a_list2: + new_list.append(expression) + + Can also be expressed in one line: + + .. code-block:: python + + new_list = [exp for var in a_list for var2 in a_list2] + + You get the "outer product", i.e. all combinations. + + (demo) + +.. nextslide:: + +But usually you at least have a conditional in the loop: + +.. rst-class:: build +.. container:: + + .. code-block:: python + + new_list = [] + for variable in a_list: + if something_is_true: + new_list.append(expression) + + You can add a conditional to the comprehension: + + .. code-block:: python + + new_list = [expr for var in a_list if something_is_true] + + (demo) + +.. nextslide:: + +Examples: + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [341]: [x ** 2 for x in range(3)] + Out[341]: [0, 1, 4] + + In [342]: [x + y for x in range(3) for y in range(5, 7)] + Out[342]: [5, 6, 6, 7, 7, 8] + + In [343]: [x * 2 for x in range(6) if not x % 2] + Out[343]: [0, 4, 8] + + Remember this from last week? + + .. code-block:: python + + [name for name in dir(__builtin__) if "Error" in name] + ['ArithmeticError', + 'AssertionError', + 'AttributeError', + .... + + +Set Comprehensions +------------------ + +You can do it with sets, too: + +.. rst-class:: build +.. container:: + + .. code-block:: python + + new_set = {value for value in a_sequence} + + + the same as this ``for`` loop: + + .. code-block:: python + + new_set = set() + for value in a_sequence: + new_set.add(value) + +.. nextslide:: + +Example: finding all the vowels in a string... + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [19]: s = "a not very long string" + + In [20]: vowels = set('aeiou') + + In [21]: { let for let in s if let in vowels } + Out[21]: {'a', 'e', 'i', 'o'} + + Side note: why did I do ``set('aeiou')`` rather than just `aeiou`\ ? + + +Dict Comprehensions +------------------- + +Also with dictionaries + +.. rst-class:: build +.. container:: + + .. code-block:: python + + new_dict = { key:value for key, value in a_sequence} + + + the same as this ``for`` loop: + + .. code-block:: python + + new_dict = {} + for key, value in a_sequence: + new_dict[key] = value + +.. nextslide:: + +Example + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [22]: {i: "this_%i" % i for i in range(5)} + Out[22]: {0: 'this_0', 1: 'this_1', 2: 'this_2', + 3: 'this_3', 4: 'this_4'} + + Can you do the same thing with the ``dict()`` constructor? + + +Anonymous functions +=================== + +.. rst-class:: center large + +λ + +lambda +------ + +.. code-block:: ipython + + In [171]: f = lambda x, y: x+y + In [172]: f(2,3) + Out[172]: 5 + +.. rst-class:: build +.. container:: + + Content can only be an expression -- not a statement + + Anyone remember what the difference is? + + Called "Anonymous": it doesn't need a name. + +.. nextslide:: + +It's a python object, it can be stored in a list or other container + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [6]: l = [lambda x, y: x + y] + + In [7]: l + Out[7]: [>] + + In [8]: type(l[0]) + Out[8]: function + + + And you can call it: + + .. code-block:: ipython + + In [9]: l[0](3,4) + Out[9]: 7 + + +Functions as first class objects +--------------------------------- + +You can do that with "regular" functions too: + +.. code-block:: ipython + + In [12]: def fun(x,y): + ....: return x + y + ....: + In [13]: l = [fun] + In [14]: type(l[0]) + Out[14]: function + In [15]: l[0](3, 4) + Out[15]: 7 + + +Functional Programming +====================== + +map +--- + +``map``: "maps" a function onto a sequence of objects -- It applies the +function to each item in the list, returning another list + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [23]: l = [2, 5, 7, 12, 6, 4] + In [24]: def fun(x): + return x * 2 + 10 + In [25]: map(fun, l) + Out[25]: [14, 20, 24, 34, 22, 18] + + + But if it's a small function, and you only need it once: + + .. code-block:: ipython + + In [26]: map(lambda x: x * 2 + 10, l) + Out[26]: [14, 20, 24, 34, 22, 18] + + +filter +------ + +``filter``: "filters" a sequence of objects with a boolean function -- It keeps +only those for which the function is True + +.. rst-class:: build +.. container:: + + To get only the even numbers: + + .. code-block:: ipython + + In [27]: l = [2, 5, 7, 12, 6, 4] + In [28]: filter(lambda x: not x % 2, l) + Out[28]: [2, 12, 6, 4] + +reduce +------ + +``reduce``: "reduces" a sequence of objects to a single object with a function +that combines two arguments + +.. rst-class:: build +.. container:: + + To get the sum: + + .. code-block:: ipython + + In [30]: l = [2, 5, 7, 12, 6, 4] + In [31]: reduce(lambda x, y: x + y, l) + Out[31]: 36 + + To get the product: + + .. code-block:: ipython + + In [32]: reduce(lambda x,y: x*y, l) + Out[32]: 20160 + +Comprehensions +-------------- + +Couldn't you do all this with comprehensions? + +.. rst-class:: build +.. container:: + + Yes: + + .. code-block:: ipython + + In [33]: [x + 2 + 10 for x in l] + Out[33]: [14, 17, 19, 24, 18, 16] + In [34]: [x for x in l if not x % 2] + Out[34]: [2, 12, 6, 4] + + (Except Reduce) + + But Guido thinks almost all uses of reduce are really ``sum()`` + +Functional Programming +---------------------- + +Comprehensions and map, filter, reduce are all "functional programming" +approaches} + +.. rst-class:: build +.. container:: + + ``map, filter`` and ``reduce`` pre-date comprehensions in Python's history + + Some people like that syntax better + + And "map-reduce" is a big concept these days for parallel processing of "Big + Data" in NoSQL databases. + + (Hadoop, EMR, MongoDB, etc.) + +More About Lambda +----------------- + +Can also use keyword arguments + +.. rst-class:: build +.. container:: + + .. code-block:: ipython + + In [186]: l = [] + In [187]: for i in range(3): + .....: l.append(lambda x, e=i: x**e) + .....: + In [189]: for f in l: + .....: print(f(3)) + 1 + 3 + 9 + + Note when the keyword argument is evaluated + + This turns out to be very handy! + + +Homework +======== + +.. rst-class:: build +.. container:: + + Of course there's homework + +Task 13: List Comprehensions +---------------------------- + +In your student folder, create a subdirectory called ``session05``. +Create a new branch called ``task13`` and switch to it (``git checkout -b task13``). + +Within the ``session05`` subdirectory, create a new file called ``list_comp.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 create a pull request titled ``Task 13 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Note: this is a bit of a "backwards" exercise -- given some code, you figure +out what it does. + +.. rst-class:: build +.. container:: + + In canvas, you'll take a quiz where each of these questions is worth 1 + point. + + You can take the quiz repeatedly if you have trouble. + + .. code-block:: python + + >>> feast = ['lambs', 'sloths', 'orangutans', 'breakfast cereals', 'fruit bats'] + >>> comprehension = [delicacy.capitalize() for delicacy in feast] + + .. container:: + + 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] + +.. rst-class:: build +.. container:: + + .. container:: + + 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 ] + +.. rst-class:: build +.. container:: + + .. container:: + + What is the output of: + + .. code-block:: python + + >>> comprehension[0] + ??? + + >>> len(comprehension[2]) + ??? + + (figure it out first!) + +.. 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] + +.. rst-class:: build +.. container:: + + 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'} + +.. rst-class:: build +.. container:: + + 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} + +.. rst-class:: build +.. container:: + + 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 + +(submit this one to gitHub for credit on this assignment) + +.. rst-class:: build +.. container:: + + 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 + + +Task 14: Dictionary and Set Comprehensions +------------------------------------------ + +In your ``session05`` directory, write the following code +into a new file called ``dict_comp.py``. + +Add the file to your local working repository +and commit changes frequently +while working on the following tasks. When you are done, push your changes to +GitHub and create a pull request +titled ``Task 14 pull request from Your Name`` where you should substitute your name for ``Your Name``. + + +Let's revisiting the dict/set lab -- see how much you can do with +comprehensions instead. + +.. rst-class:: build +.. container:: + + Specifically, look at these: + + First a slightly bigger, more interesting (or at least bigger..) dict: + + .. code-block:: python + + food_prefs = {"name": u"Cris", + u"city": u"Seattle", + u"cake": u"lemon", + u"fruit": u"pomegranate", + u"salad": u"chop", + u"pasta": u"lasagna"} + + (make a dictionary that includes your answers, not mine) + +.. nextslide:: Working with this dict: + +1. Print the dict by passing it to a string format method, so that you + get something like:: + + "Cris is from Seattle, and he likes lemon cake, pomegranate fruit, + chop 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 the numbers from zero through twenty + that are 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!) + +Task 15: Lambda and Keyword Argument Magic +------------------------------------------ + +In your ``session05`` directory, write the following code +into a new file called ``lambda.py``. + +Add the file to your local working repository +and commit changes frequently +while working on the following tasks. When you are done, push your changes to +GitHub and create a pull request +titled ``Task 15 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +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, a ``lambda``, and a keyword argument + +( Extra credit ): + +Do it with a list comprehension, instead of a ``for`` loop + +.. nextslide:: Example calling code + +Not clear? here's what you should get: + +.. 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: a function that adds 0 to the input + In [98]: the_list[1](2) + Out[98]: 3 + ## the 1st element of the list: a function that adds 1 to the input + In [100]: for f in the_list: + .....: print(f(5), end=" ") + .....: + 5 + 6 + 7 + 8 + + + +Task 16: Investigate Session 6 +------------------------------ + +Read through the Session 6 slides. + +http://codefellows.github.io/sea-c34-python/session06.html + +There are four sections. For each one, come up with the following +numbers of questions. + +* Object-Oriented Programming (2 questions) +* Python Classes (1 question) +* Subclassing and More Subclassing (4 questions) + +Write some +Python code to answer these questions, one function per question. + +For each function, write a good ``docstring`` describing what +question you are trying to answer. + +Put the functions in four separate modules (files) called +`oop.py`, `classes.py`, and `subclasses.py` in the +``session05`` subdirectory of your student directory. + +.. nextslide:: + +That is, you should have seven questions, and seven functions, total, +spread out across three files. + +Use everything you've learned +so far as needed (including lists, tuples, slicing, iteration, functions, booleans, printing, modules, assertions, dictionaries, +sets, exceptions, file reading/writing, and paths). + +Create a branch in your local repo called `task16` and switch to it (`git checkout task16`). + +Add your files +to that branch, commit and push, then create a pull request to +the main class repo, +titled ``Task 16 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Finally, submit your assignment in Canvas by giving the URL of the pull request. + +Recommended Reading +--------------------- + +* LPTHW: Ex 40 - 45 + +http://learnpythonthehardway.org/book/ + +* Dive Into Python: chapter 4, 5 + +http://www.diveintopython.net/toc/index.html + diff --git a/docs/session6.rst b/docs/session6.rst new file mode 100644 index 0000000..8f9cd92 --- /dev/null +++ b/docs/session6.rst @@ -0,0 +1,868 @@ + +.. Foundations 2: Python slides file, created by + hieroglyph-quickstart on Wed Apr 2 18:42:06 2014. + +************************************************* +Session Six: Intro to Object Oriented Programming +************************************************* + +Classes, instances, attributes, and subclassing + + +Review/Questions +================ + +Review of Previous Class +------------------------ + +.. rst-class:: build + +* Argument Passing: ``*args``, ``**kwargs`` +* comprehensions +* ``lambda`` +* Solutions to the FizzBuzz problem. + +Homework review +--------------- + +* LBYL vs. EAFP + +http://stackoverflow.com/questions/404795/lbyl-vs-eafp-in-java + +* Line Indentations + +* + +Other Homework Questions? + +.. rst-class:: build +.. container:: + +Review of Survey Feedback +------------------------- + +* Introductory readings +* More up-front explanation in class +* More connection between homework and in-class exercises +* Faster homework grading + +Questions +------------------------- + +* How will this prepare me for the dev accelerator? +* What about independent projects for my software portfolio? +* Do we have to worry about proper Git / GitHub technique? + +Resources +------------------------- + +Beginner-Friendly Textbooks + +* `Interactive Python `_ +* `Dive Into Python `_ +* `Learn Python the Hard Way `_ + +.. nextslide:: + +Portfolio Projects, Building Community + +http://newcoder.io/ + +Calling Twitter APIs (thanks @mhazani!) + + +.. nextslide:: + +Preparation for Dev Accelerator Code Challenge + +Django Resources + +* `Tango With Django `_ +* `The official Django tutorial `_ + + +Object Oriented Programming +=========================== + +.. rst-class:: left +.. container:: + + Object-oriented programming narrows the "semantic gap". + + You can model real world objects with software objects. + + We'll talk more about Python implementation than OO design/strengths/weaknesses + +More Detailed Reading: +---------------------- + +`Dive Into Python, 5.3-5.5 on Classes `_ +`Learn Python the Hard Way < + + +Object Oriented Programming +--------------------------- + +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! + +.. rst-class:: build +.. container:: + + 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 + +.. rst-class:: build +.. container:: + + 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: + +.. rst-class:: build +.. container:: + + "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: + +.. rst-class:: build +.. container:: + + .. rst-class:: build + + * data abstraction + * encapsulation + * modularity + * polymorphism + * inheritance + + Python does all of this, though it doesn't enforce them. + +.. rst-class:: build +.. container:: + + "OO languages" give you some handy tools to make it easier (and safer): + + .. rst-class:: build + + * polymorphism (duck typing gives you this anyway) + * inheritance + +.. nextslide:: + +OO has been the dominant model for the past couple decades + +.. rst-class:: build +.. container:: + + 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 + +.. rst-class:: build + +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 large + + Note that in python, functions are first class objects, so a method *is* an + attribute + +Python Classes +============== + +.. rst-class:: left +.. container:: + + The ``class`` statement + + .. rst-class:: build + .. container:: + + ``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): + # everything attached to self is in the instance namespace + 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. + +.. rst-class:: build +.. container:: + + 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:: ``self`` + +What is this ``self`` thing? + +.. rst-class:: build +.. container:: + + The instance of the class is passed as the first parameter for every method. + + Using ``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:: The Instance Namespace + +Anything assigned to a ``self.`` attribute is kept in the *instance* +name space -- ``self`` *is* the instance. + +.. rst-class:: build +.. container:: + + 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:: The Class Namespace + +Anything assigned in the class scope is a class attribute + +.. rst-class:: build +.. container:: + + Every *instance* of the class shares the same one. + + Note: the methods defined by ``def`` are class attributes as well. + + .. container:: + + 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:: Class Methods + +Typical methods: + +.. rst-class:: build +.. container:: + + .. 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) + +.. rst-class:: build +.. container:: + + Huh???? I only gave 2 + + ``self`` is implicitly passed in for you by python. + + (demo of bound vs. unbound methods) + +.. nextslide:: + +Using ``self`` explicitly like this can seem a bit confusing + +.. rst-class:: build +.. container:: + + But like most of Python's quirks, there's a rationale behind it + + Our BDFL has made the decision that ``self`` will stay, and written + extensively about why: + + http://neopythonic.blogspot.com/2008/10/why-explicit-self-has-to-stay.html + +LAB / Homework +-------------- + +Let's say you need to render some html.. + +.. rst-class:: build +.. container:: + + The goal is to build a set of classes that render an html page. + + ``Examples/Session06/sample_html.html`` + + We'll start with a single class, then add some sub-classes to specialize the behavior + + Details in: + + :ref:`homework_html_renderer` + + Let's see if we can do step 1. in class... + +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:: + +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. + +.. nextslide:: + +A Program Design Suggestion + + 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__`` +----------------------- + +Wanting or needing to override ``__init__`` is very common + +.. rst-class:: build +.. container:: + + You often need to call the super class ``__init__`` as well + + Think "everything the parent does, plus this stuff too" + + .. 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 +---------------- + +.. rst-class:: build +.. container:: + + "Is a" relationship: Subclass/inheritance + + "Has a" relationship: Composition + +.. nextslide:: + +"Is a" vs "Has a" + +.. rst-class:: build +.. container:: + + 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: + +.. rst-class:: build + +* Is it an instance attribute? +* Is it a class attribute? +* Is it a superclass attribute? +* Is it a super-superclass attribute? +* ... + +.. rst-class:: build +.. container:: + + 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... + +.. rst-class:: build +.. container:: + + Python classes are: + + .. rst-class:: build + + * 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 + +.. rst-class:: build +.. container:: + + Usually better to use "duck typing" (polymorphism) + + But when it's called for: + + .. rst-class:: build + + * ``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: + +.. rst-class:: build +.. container:: + + Think about what makes sense for your code: + + .. rst-class:: build + + * 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: + +.. rst-class:: build +.. container:: + + .. container:: + + **The Art of Subclassing**: *Raymond Hettinger* + + http://pyvideo.org/video/879/the-art-of-subclassing + + "classes are for code re-use -- not creating taxonomies" + + .. container:: + + **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 +======== + +Task 17: HTML Renderer +---------------------- + +.. rst-class:: left +.. container:: + + Build an html rendering system: + + :ref:`homework_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 + +Create a directory called ``session06`` in your student directory. +Create a branch in your local repo called `task17` and switch to it (`git checkout -b task17`). + +Add your files +to that branch, commit frequently, and push to it as you work, +writing good commit messages. +Then create a pull request to the main class repo, +titled ``Task 17 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Task 18: Investigate Session 7 +------------------------------ + +Read through the Session 7 slides. + +http://codefellows.github.io/sea-c34-python/session07.html + +There are five sections. For each one, come up with one question. + +* Testing (1 question) +* Multiple Inheritance (1 question) +* Properties (1 question) +* Class and Static Methods (1 question) +* Special (Magic) Methods (1 question) + +Write some +Python code to answer these questions, one function per question. + +For each function, write a good ``docstring`` describing what +question you are trying to answer. + +Put the functions in four separate modules (files) called +`testing.py`, `multiple.py`, `properties.py`, `static.py`, and +`special.py` in the +``session06`` subdirectory of your student directory. + +.. nextslide:: + +That is, you should have seven questions, and seven functions, total, +spread out across three files. + +You may use everything you've learned +so far as needed (including lists, tuples, slicing, iteration, functions, booleans, printing, modules, assertions, dictionaries, +sets, exceptions, file reading/writing, paths, lambdas, keyword/variable arguments, comprehensions, and object-oriented programming). + +Create a branch in your local repo called `task18` and switch to it (`git checkout task18`). + +Add your files +to that branch, commit and push, then create a pull request to +the main class repo, +titled ``Task 18 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Finally, submit your assignment in Canvas by giving the URL of the pull request. diff --git a/docs/session7.rst b/docs/session7.rst new file mode 100644 index 0000000..274870b --- /dev/null +++ b/docs/session7.rst @@ -0,0 +1,1194 @@ + +.. Foundations 2: Python slides file, created by + hieroglyph-quickstart on Wed Apr 2 18:42:06 2014. + +******************************* +Session Seven: Testing, More OO +******************************* + +.. rst-class:: centered + +| Testing, +| Multiple Inheritance, +| Properties, +| Class and Static Methods, +| Special (Magic) Methods + + +Review/Questions +================ + +Review of Previous Class +------------------------ + +* Did anyone look more deeply into Unicode? + + - Any questions about that? + +* Object Oriented Programming + + - Questions about concept? + + - Questions about Python implimentation? + +Homework review +--------------- + +Homework Questions? + +How is progress going on the HTML Renderer? + + +A Quick Note +------------ + +One issue that seems vexing is how to make a script "executable" + +Have you seen something like this: + +.. code-block:: bash + + $ ./run_html_render.py + -bash: ./run_html_render.py: Permission denied + +The problem is that the file is not "executable": + +.. code-block:: bash + + $ ls -l run_html_render.py + -rw-r--r-- 1 cewing staff 5015 Dec 10 21:18 run_html_render.py + +.. nextslide:: + +The fix for this is to add the executable bit to the permissions for the file: + +.. code-block:: bash + + $ chmod u+x run_html_render.py + $ ls -l run_html_render.py + -rwxr--r-- 1 cewing staff 5015 Dec 10 21:18 run_html_render.py + +You can also do this with a numeric file-mode designation: + +.. code-block:: bash + + $ chmod 744 run_html_render.py + $ ls -l run_html_render.py + -rwxr--r-- 1 cewing staff 5015 Dec 10 21:18 run_html_render.py + + +Testing +======= + +.. rst-class:: build left +.. container:: + + You've already seen some a very basic testing strategy. + + You've written some tests using that strategy. + + These tests were pretty basic, and a bit awkward in places (testing error + conditions in particular). + + .. rst-class:: centered + + **It gets better** + +Test Runners +------------ + +So far our tests have been limited to code in an ``if __name__ == "__main__":`` +block. + +.. rst-class:: build + +* They are run only when the file is executed +* They are always run when the file is executed +* You can't do anything else when the file is executed without running tests. + +.. rst-class:: build +.. container:: + + This is not optimal. + + Python provides testing systems to help. + + +.. nextslide:: Standard Library: ``unittest`` + +The original testing system in Python. + +You write subclasses of the ``unittest.TestCase`` class: + +.. code-block:: python + + # in test.py + import unittest + + class MyTests(unittest.TestCase): + def test_tautology(self): + self.assertEquals(1, 1) + +Then you run the tests by using the ``main`` function from the ``unittest`` +module: + +.. code-block:: python + + # in test.py + if __name__ == '__main__': + unittest.main() + +.. nextslide:: Testing Your Code + +This way, you can write your code in one file and test it from another: + +.. code-block:: python + + # in my_mod.py + def my_func(val1, val2): + return val1 * val2 + + # in test_my_mod.py + import unittest + from my_mod import my_func + + class MyFuncTestCase(unittest.TestCase): + def test_my_func(self): + test_vals = (2, 3) + expected = reduce(lambda x, y: x * y, test_vals) + actual = my_func(*test_vals) + self.assertEquals(expected, actual) + + if __name__ == '__main__': + unittest.main() + +.. nextslide:: Advantages of ``unittest`` + +The ``unittest`` module is pretty full featured + +.. rst-class:: build +.. container:: + + It comes with the standard Python distribution, no installation required. + + It provides a wide variety of assertions for testing all sorts of situations. + + It allows for a setup and tear down workflow both before and after all tests + and before and after each test. + + It's well known and well understood. + +.. nextslide:: Disadvantages: + +It's Object Oriented, and quite heavy. + +.. rst-class:: build +.. container:: + + It was modeled after Java's ``junit`` and it shows... + + It uses the framework design pattern, so knowing how to use the features + means learning what to override. + + Needing to override means you have to be cautious. + + Test discovery is both inflexible and brittle. + +.. nextslide:: Other Options + +There are several other options for running tests in Python. + +* `Nose`_ +* `pytest`_ +* ... (many frameworks supply their own test runners) + +We are going to play today with pytest + +.. _Nose: https://nose.readthedocs.org/ +.. _pytest: http://pytest.org/latest/ + + +.. nextslide:: Installing ``pytest`` + +The first step is to install the package: + +.. code-block:: bash + + (cff2py)$ pip install pytest + +.. rst-class:: build +.. container:: + + You may need to use 'sudo' to get that to work. + + Once this is complete, you should have a ``py.test`` command you can run at + the command line: + + .. code-block:: bash + + (cff2py)$ py.test + + If you have any tests in your repository, that will find and run them. + +.. rst-class:: build +.. container:: + + **Do you?** + +.. nextslide:: Pre-existing Tests + +I've added two files to the ``Examples/Session07`` folder, along with a python +source code file called ``circle.py``. + +.. rst-class:: build +.. container:: + + The results you should have seen when you ran ``py.test`` above come partly + from these files. + + Let's take a few minutes to look these files over. + + [demo] + +.. nextslide:: What's Happening Here. + +When you run the ``py.test`` command, ``pytest`` starts in your current working +directory and searches the filesystem for things that might be tests. + +It follows some simple rules: + +.. rst-class:: build + +* Any python file that starts with ``test_`` or ``_test`` is imported. +* Any functions in them that start with ``test_`` are run as tests. +* Any classes that start with ``Test`` are treated similarly, with methods that + begin with ``test_`` treated as tests. + + +.. nextslide:: + +This test running framework is simple, flexible and configurable. + +`Read the documentation`_ for more information. + +.. _Read the documentation: http://pytest.org/latest/getting-started.html#getstarted + +.. nextslide:: Test Driven Development + +What we've just done here is the first step in what is called **Test Driven +Development**. + +.. rst-class:: build +.. container:: + + A bunch of tests exist, but the code to make them pass does not yet exist. + + The red we see in the terminal when we run our tests is a goad to us to write + the code that fixes these tests. + + Let's do that next! + + [lab time!] + +More on Subclassing +=================== + +Watch This Video: + +http://pyvideo.org/video/879/the-art-of-subclassing + +| +| +| + +( I pointed you to it last week, but Seriously, well worth the time. ) + + +What's a Subclass For? +---------------------- + +The most salient points from that video are as follows: + +.. rst-class:: build +.. container:: + + **Subclassing is not for Specialization** + + **Subclassing is for Reusing Code** + + **Bear in mind that the subclass is in charge** + + Is any of this starting to make sense with the HTML builder example? + + +Multiple Inheritance +-------------------- + +Multiple inheritance: Inheriting from more than one class + +.. rst-class:: build +.. container:: + + Simply provide more than one parent. + + .. code-block:: python + + class Combined(Super1, Super2, Super3): + def __init__(self, something, something else): + # some custom initialization here. + Super1.__init__(self, ......) + Super2.__init__(self, ......) + Super3.__init__(self, ......) + # possibly more custom initialization + + (calls to the super class ``__init__`` are optional -- case dependent) + + Now you have one class with functionaility of ALL the superclasess! + + But what if the same attribute exists in more than one superclass? + +.. nextslide:: Method Resolution Order + +.. code-block:: python + + class Combined(Super1, Super2, Super3) + +.. rst-class:: build +.. container:: + + Attributes are located bottom-to-top, left-to-right + + .. rst-class:: build + + * Is it an instance attribute ? + * Is it a class attribute ? + * Is it a superclass attribute ? + + * is the it an attribute of the left-most superclass? + * is the it an attribute of the next superclass? + * and so on up the hierarchy... + + * Is it a super-superclass attribute ? + * ... also left to right ... + + (This is not **at all** simple!) + + http://python-history.blogspot.com/2010/06/method-resolution-order.html + +.. nextslide:: Mix-ins + +Why would you want multiple inheritance? -- one reason is mix-ins. + +.. rst-class:: build +.. container:: + + Provides an subset of expected functionality in a re-usable package. + + Hierarchies are not always simple: + + * Animal + + * Mammal + + * GiveBirth() + + * Bird + + * LayEggs() + + Where do you put a Platypus? + + Real World Example: `FloatCanvas`_ + + **Careful About This Pattern** + +.. _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``. + +.. rst-class:: build +.. container:: + + 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.** + + The differences are subtle, and may not appear until they jump up to bite + you. + + (which they will the rest of this class session!) + +.. nextslide:: ``super()`` + +``super()``: use it to call a superclass method, rather than explicitly calling +the unbound method on the superclass. + +.. rst-class:: build +.. container:: + + instead of: + + .. code-block:: python + + class A(B): + def __init__(self, *args, **kwargs) + B.__init__(self, *args, **kwargs) + ... + + You can do: + + .. code-block:: python + + class A(B): + def __init__(self, *args, **kwargs) + super(A, self).__init__(*args, **kwargs) + ... + +.. nextslide:: Caveats + +Caution: There are some subtle differences with multiple inheritance. + +.. rst-class:: build +.. container:: + + One difference is the syntax: need to think hard to understand all that: + + .. code-block:: python + + super(A, self).__init__(*args, **kwargs) + + This means something like: + + "create a ``super`` object for the superclass of class A, with this + instance. Then call ``__init__`` on that object." + + Important note: ``super()`` **does not** return the superclass object! + + | + + But you can use explicit calling to ensure that the 'right' method is + called. + + +.. nextslide:: Background + +Two seminal articles about ``super()``: + +.. rst-class:: build +.. container:: + + .. container:: + + "Super Considered Harmful" -- James Knight + + https://fuhm.net/super-harmful/ + + .. container:: + + "super() considered super!" -- Raymond Hettinger + + http://rhettinger.wordpress.com/2011/05/26/super-considered-super/} + + (Both worth reading....) + + While appearing to be contradictory, they both have the same final + message... + +super() issues... +----------------- + +Both articles actually say similar things: + +.. rst-class:: build + +* The method being called by super() needs to exist +* Every occurrence of the method needs to use super(): + + - Use it consistently, and document that you use it, as it is part of + the external interface for your class, like it or not. + +.. nextslide:: calling super() + +The caller and callee need to have a matching argument signature: + +.. rst-class:: build +.. container:: + + Never call super with anything but the exact arguments you received, + unless you really know what you're doing. + + .. container:: + + If you add one or more optional arguments, always accept: + + .. code-block:: python + + *args, **kwargs + + .. container:: + + and call super like: + + .. code-block:: python + + super(MyClass, self).method(args_declared, *args, **kwargs) + + +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(object): + def __init__(self): + self.x = 5 + In [6]: c = C() + In [7]: c.x + Out[7]: 5 + In [8]: c.x = 8 + In [9]: c.x + Out[9]: 8 + + +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(object): + ...: def __init__(self): + ...: self.x = 5 + ...: def get_x(self): + ...: return self.x + ...: def set_x(self, x): + ...: self.x = x + ...: + In [6]: c = C() + In [7]: c.get_x() + Out[7]: 5 + In [8]: c.set_x(8) + In [9]: c.get_x() + Out[9]: 8 + + + This is ugly and verbose -- `Java`_? + +.. _Java: http://dirtsimple.org/2004/12/python-is-not-java.html + +.. nextslide:: properties + +When (and if) you need them: + +.. code-block:: python + + class C(object): + def __init__(self, x=5): + self._x = x + def _getx(self): + return self._x + def _setx(self, value): + self._x = value + def _delx(self): + del self._x + x = property(_getx, _setx, _delx, doc="docstring") + +Now the interface is still like simple attribute access! + + +[demo: ``Examples/Session07/properties_example.py``] + + +.. nextslide:: "Read Only" Attributes + +Not all the arguments to ``property`` are required. + +You can use this to create attributes that are "read only": + +.. code-block:: ipython + + In [11]: class D(object): + ....: def __init__(self, x=5): + ....: self._x = 5 + ....: def getx(self): + ....: return self._x + ....: x = property(getx, doc="I am read only") + ....: + 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 + + +.. nextslide:: Syntactic Sugar + +This *imperative* style of adding a ``property`` to you class is clear, but +it's still a little verbose. + +It also has the effect of leaving all those defined method objects laying +around: + +.. code-block:: ipython + + In [19]: d.x + Out[19]: 5 + In [20]: d.getx + Out[20]: > + In [21]: d.getx() + Out[21]: 5 + +.. nextslide:: + +Python provides us with a way to solve both these issues at once, using a +syntactic feature called **decorators** (more about these next session): + +.. code-block:: ipython + + In [22]: class E(object): + ....: def __init__(self, x=5): + ....: self._x = x + ....: @property + ....: def x(self): + ....: return self._x + ....: @x.setter + ....: def x(self, value): + ....: self._x = value + ....: + In [23]: e = E() + In [24]: e.x + Out[24]: 5 + In [25]: e.x = 6 + In [26]: e.x + Out[26]: 6 + + +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): + ....: def add(a, b): + ....: return a + b + ....: add = staticmethod(add) + ....: + + In [37]: StaticAdder.add(3, 6) + Out[37]: 9 + + +[demo: ``Examples/Session07/static_method.py``] + + +.. nextslide:: Syntactic Sugar + +Like ``properties``, static methods can be written *declaratively* using the +``staticmethod`` built-in as a *decorator*: + +.. code-block:: python + + class StaticAdder(object): + @staticmethod + def add(a, b): + return a + b + +.. 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 + ....: def a_class_method(cls, y): + ....: print(u"in a class method: ", cls) + ....: return y ** cls.x + ....: a_class_method = classmethod(a_class_method) + ....: + In [42]: Classy.a_class_method(4) + in a class method: + Out[42]: 16 + + + +[demo: ``Examples/Session07/class_method.py``] + +.. nextslide:: Syntactic Sugar + +Once again, the ``classmethod`` built-in can be used as a *decorator* for a +more declarative style of programming: + +.. code-block:: python + + class Classy(object): + x = 2 + @classmethod + def a_class_method(cls, y): + print(u"in a class method: ", cls) + return y ** cls.x + +.. nextslide:: 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 + +.. nextslide:: 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 + + +Kicking the Tires +----------------- + +Copy the file ``Example/Session07/circle.py`` to your student folder. +(we used it for our testing try out...) + +In it, update the simple "Circle" class: + +.. code-block:: ipython + + In [13]: c = Circle(3) + In [15]: c.diameter + Out[15]: 6.0 + In [16]: c.diameter = 8 + In [17]: c.radius + Out[17]: 4.0 + In [18]: c.area + Out[18]: 50.26548245743669 + + +Use ``properties`` so you can keep the radius and diameter in sync, and the +area computed on the fly. + +Extra Credit: use a class method to make an alternate constructor that takes +the diameter instead. + + +.. nextslide:: + +Also copy the file ``test_circle1.py`` to your student folder. + +As you work, run the tests: + +.. code-block:: bash + + (cff2py)$ py.test test_circle1.py + +As each of the requirements from above are fulfilled, you'll see tests 'turn +green'. + +When all your tests are passing, you've completed the job. + +(This clear finish line is another of the advantages of TDD) + + +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__ + +.. rst-class:: build +.. container:: + + It's all in the double underscores... + + Pronounced "dunder" (or "under-under") + + try: ``dir(2)`` or ``dir(list)`` + +.. nextslide:: Protocols + +The set of special methods needed to emulate a particular type of Python object +is called a *protocol*. + +.. rst-class:: build +.. container:: + + 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. + +.. rst-class:: build +.. container:: + + 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: ``Examples/Session07/vector.py>``] + + +.. nextslide:: Generally Useful Special Methods + +You only *need* to define the special methods that will be used by your class. + +.. rst-class:: build +.. container:: + + However, even in the absence of wanting to duck-type, you should almost + always define these: + + ``object.__str__``: + Called by the str() built-in function and by the print statement 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. + + ``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``) + +.. nextslide:: Summary + +Use special methods when you want your class to act like a "standard" class in +some way. + +.. rst-class:: build +.. container:: + + 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 + * http://www.rafekettler.com/magicmethods.html + + Be a bit cautious about the code examples in that last one. It uses quite a + bit of old-style class definitions, which should not be emulated. + + +Kicking the Tires +----------------- + +Extend your "Circle" class: + +* Add ``__str__`` and ``__repr__`` methods +* Write an ``__add__`` method so you can add two circles +* Make it so you can multiply a circle by a number.... + +.. code-block:: ipython + + In [22]: c1 = Circle(3) + In [23]: c2 = Circle(4) + In [24]: c3 = c1+c2 + In [25]: c3.radius + Out[25]: 7 + In [26]: c1*3 + Out[26]: Circle(9) + +If you have time: compare them... (``c1 > c2`` , etc) + + +.. nextslide:: + +As you work, run the tests in ``test_circle2.py``: + +.. code-block:: bash + + (cff2py)$ py.test test_circle2.py + +As each of the requirements from above are fulfilled, you'll see tests 'turn +green'. + +When all your tests are passing, you've completed the job. + + +Homework +======== + +.. rst-class:: centered large + +Testing, Testing, 1 2 3 + + +Task 19: Testing, Testing, 1 2 3 +-------------------------------- + +If you are not yet done, complete the ``Circle`` class so that all tests in +``test_circle2.py`` pass. + +Go back over some of your assignments from the last weeks. + +Convert tests that are currently in the ``if __name__ == '__main__':`` blocks +into standalone pytest files. + +Name each test file so that it is clear with which source file it belongs:: + + test_series.py -> series.py + +Add unit tests for the HTML Renderer that you are currently constructing. + +Create at least 4 test files with tests that exercise the features built in +the corresponding source file. + +Create a directory called ``session07`` in your student directory. +Create a branch in your local repo called `task19` and switch to it (`git checkout -b task19`). + +Add your files +to that branch, commit frequently, and push to it as you work, +writing good commit messages. +Then create a pull request to the main class repo, +titled ``Task 19 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Task 20: Investigate Session 8 +------------------------------ + +Read through the Session 8 slides. + +http://codefellows.github.io/sea-c34-python/session08.html + +There are three sections. For each one, come up with the following +numbers of questions. + +* Decorators (2 question) +* Iterators and Generators (4 question) +* Context Managers (1 question) + +Write some +Python code to answer these questions, one function per question. + +For each function, write a good ``docstring`` describing what +question you are trying to answer. + +Put the functions in three separate modules (files) called +`decorators.py`, `iterators.py`, and +`contexts.py` in the +``session07`` subdirectory of your student directory. + +.. nextslide:: + +That is, you should have seven questions, and seven functions, total, +spread out across three files. + +You may use everything you've learned +so far as needed (including lists, tuples, slicing, iteration, functions, booleans, printing, modules, assertions, dictionaries, +sets, exceptions, file reading/writing, paths, lambdas, keyword/variable arguments, comprehensions, object-oriented programming, +and testing). + +Create a branch in your local repo called `task20` and switch to it (`git checkout task20`). + +Add your files +to that branch, commit and push, then create a pull request to +the main class repo, +titled ``Task 20 pull request from Your Name`` where you should substitute your name for ``Your Name``. + +Finally, submit your assignment in Canvas by giving the URL of the pull request. diff --git a/docs/session8.rst b/docs/session8.rst new file mode 100644 index 0000000..37a559b --- /dev/null +++ b/docs/session8.rst @@ -0,0 +1,1149 @@ +********************************************************************** +Session Eight: Generators, Iterators, Decorators, and Context Managers +********************************************************************** + +.. rst-class:: large centered + +The tools of Pythonicity + +Last Session! +================ + +* Review Circle Testing, HTML Renderer +* Complexity, Data Structures +* Decorators +* Iterators +* Generators +* Context Managers +* The Future + +Review/Questions +================ + +Review of Previous Class +------------------------ + +* Advanced OO Concepts + + * Properties + * Special Methods + +* Testing with pytest + + +Homework review +--------------- + +* Circle Class +* Writing Tests using the ``pytest`` module + + +Decorators +========== + +**A Short Digression** + +.. rst-class:: left build +.. container:: + + Functions are things that generate values based on input (arguments). + + In Python, functions are first-class objects. + + This means that you can bind symbols to them, pass them around, just like + other objects. + + Because of this fact, you can write functions that take functions as + arguments and/or return functions as values (we played with this a + bit with the lambda magic assignment): + + .. code-block:: python + + def substitute(a_function): + def new_function(*args, **kwargs): + return u"I'm not that other function" + return new_function + +A Definition +------------ + +There are many things you can do with a simple pattern like this one. + +So many, that we give it a special name: + +.. rst-class:: centered mlarge + +**Decorator** + +.. rst-class:: build +.. container:: + + "A decorator is a function that takes a function as an argument and + returns a function as a return value." + + That's nice and all, but why is that useful? + +An Example +---------- + +Imagine you are trying to debug a module with a number of functions like this +one: + +.. code-block:: python + + def add(a, b): + return a + b + +.. rst-class:: build +.. container:: + + You want to see when each function is called, with what arguments and with what + result. So you rewrite each function as follows: + + .. code-block:: python + + def add(a, b): + print(u"Function 'add' called with args: %r" % locals()) + result = a + b + print(u"\tResult --> %r" % result) + return result + +.. nextslide:: + +That's not particularly nice, especially if you have lots of functions in your +module. + +Now imagine we defined the following, more generic *decorator*: + +.. code-block:: python + + def logged_func(func): + def logged(*args, **kwargs): + print(u"Function %r called" % func.__name__) + if args: + print(u"\twith args: %r" % args) + if kwargs: + print(u"\twith kwargs: %r" % kwargs) + result = func(*args, **kwargs) + print(u"\t Result --> %r" % result) + return result + return logged + +.. nextslide:: + +We could then make logging versions of our module functions: + +.. code-block:: python + + logging_add = logged_func(add) + +Then, where we want to see the results, we can use the logged version: + +.. code-block:: ipython + + In [37]: logging_add(3, 4) + Function 'add' called + with args: (3, 4) + Result --> 7 + Out[37]: 7 + +.. rst-class:: build +.. container:: + + This is nice, but we have to call the new function wherever we originally + had the old one. + + It'd be nicer if we could just call the old function and have it log. + +.. nextslide:: + +Remembering that you can easily rebind symbols in Python using *assignment +statements* leads you to this form: + +.. code-block:: python + + def logged_func(func): + # implemented above + + def add(a, b): + return a + b + add = logged_func(add) + +.. rst-class:: build +.. container:: + + And now you can simply use the code you've already written and calls to + ``add`` will be logged: + + .. code-block:: ipython + + In [41]: add(3, 4) + Function 'add' called + with args: (3, 4) + Result --> 7 + Out[41]: 7 + +Syntax +------ + +Rebinding the name of a function to the result of calling a decorator on that +function is called **decoration**. + +Because this is so common, Python provides a special operator to perform it +more *declaratively*: the ``@`` operator: + +.. code-block:: python + + # this is the imperative version: + def add(a, b): + return a + b + add = logged_func(add) + + # and this declarative form is exactly equal: + @logged_func + def add(a, b): + return a + b + +.. rst-class:: build +.. container:: + + The declarative form (called a decorator expression) is far more common, + but both have the identical result, and can be used interchangeably. + +Callables +--------- + +Our original definition of a *decorator* was nice and simple, but a tiny bit +incomplete. + +In reality, decorators can be used with anything that is *callable*. + +In python a *callable* is a function, a method on a class, or even a class that +implements the ``__call__`` special method. + +So in fact the definition should be updated as follows: + +.. rst-class:: centered + + "A decorator is a callable that takes a callable as an argument and returns a callable as a return value."" + +An Example +---------- + +Consider a decorator that would save the results of calling an expensive +function with given arguments: + +.. code-block:: python + + class Memoize: + """Provide a decorator class that caches expensive function results + + from avinash.vora http://avinashv.net/2008/04/python-decorators-syntactic-sugar/ + """ + def __init__(self, function): # runs when memoize class is called + self.function = function + self.memoized = {} + + def __call__(self, *args): # runs when memoize instance is called + try: + return self.memoized[args] + except KeyError: + self.memoized[args] = self.function(*args) + return self.memoized[args] + +.. nextslide:: + +Let's try that out with a potentially expensive function: + +.. code-block:: ipython + + In [56]: @Memoize + ....: def sum2x(n): + ....: return sum(2 * i for i in xrange(n)) + ....: + + In [57]: sum2x(10000000) + Out[57]: 99999990000000 + + In [58]: sum2x(10000000) + Out[58]: 99999990000000 + +It's nice to see that in action, but what if we want to know *exactly* how much +difference it made? + +Nested Decorators +----------------- + +You can stack decorator expressions. The result is like calling each decorator +in order, from bottom to top: + +.. code-block:: python + + @decorator_two + @decorator_one + def func(x): + pass + + # is exactly equal to: + def func(x): + pass + func = decorator_two(decorator_one(func)) + +.. nextslide:: + +Let's define another decorator that will time how long a given call takes: + +.. code-block:: python + + import time + def timed_func(func): + def timed(*args, **kwargs): + start = time.time() + result = func(*args, **kwargs) + elapsed = time.time() - start + print(u"time expired: %s" % elapsed) + return result + return timed + +.. nextslide:: + +And now we can use this new decorator stacked along with our memoizing +decorator: + +.. code-block:: ipython + + In [71]: @timed_func + ....: @Memoize + ....: def sum2x(n): + ....: return sum(2 * i for i in xrange(n)) + In [72]: sum2x(10000000) + time expired: 0.997071027756 + Out[72]: 99999990000000 + In [73]: sum2x(10000000) + time expired: 4.05311584473e-06 + Out[73]: 99999990000000 + +Examples from the Standard Library +---------------------------------- + +It's going to be a lot more common for you to use pre-defined decorators than +for you to be writing your own. + +Let's see a few that might help you with work you've been doing recently. + +For example, we saw that ``staticmethod()`` can be implemented with a decorator +expression: + +.. code-block:: python + + class C(object): + def add(a, b): + return a + b + add = staticmethod(add) + +Can be implimented as: + +.. code-block:: python + + class C(object): + @staticmethod + def add(a, b): + return a + b + +.. nextslide:: + +And the ``classmethod()`` builtin can do the same thing: + +In imperative style... + +.. code-block:: python + + class C(object): + def from_iterable(cls, seq): + # method body + from_iterable = classmethod(from_iterable) + +and in declarative style: + +.. code-block:: python + + class C(object): + @classmethod + def from_iterable(cls, seq): + # method body + +.. nextslide:: + +Perhaps most commonly, you'll see the ``property()`` builtin used this way. + +Remember this from last week? + +.. code-block:: python + + class C(object): + def __init__(self): + self._x = None + def getx(self): + return self._x + def setx(self, value): + self._x = value + def delx(self): + del self._x + x = property(getx, setx, delx, + u"I'm the 'x' property.") + +.. nextslide:: The Decorator version + +.. code-block:: python + + class C(object): + def __init__(self): + self._x = None + @property + def x(self): + return self._x + @x.setter + def x(self, value): + self._x = value + @x.deleter + def x(self): + del self._x + +Note that in this case, the decorator object returned by the property decorator +itself implements additional decorators as attributes on the returned method +object. + +Does this make more sense now? + +Iterators and Generators +========================= + +Iterators +--------- + +Iterators are one of the main reasons Python code is so readable: + +.. code-block:: python + + for x in just_about_anything: + do_stuff(x) + +It does not have to be a "sequence": list, tuple, etc. + +Rather: you can loop through anything that satisfies the "iterator protocol" + +http://docs.python.org/library/stdtypes.html#iterator-types + +The Iterator Protocol +---------------------- + +An iterator must have the following methods: + +.. code-block:: python + + an_iterator.__iter__() + +Returns the iterator object itself. This is required to allow both containers +and iterators to be used with the ``for`` and ``in`` statements. + +.. code-block:: python + + an_iterator.next() + +Returns the next item from the container. If there are no further items, +raises the ``StopIteration`` exception. + +List as an Iterator: +-------------------- + +.. code-block:: ipython + + In [10]: a_list = [1,2,3] + + In [11]: list_iter = a_list.__iter__() + + In [12]: list_iter.next() + Out[12]: 1 + + In [13]: list_iter.next() + Out[13]: 2 + + In [14]: list_iter.next() + Out[14]: 3 + + In [15]: list_iter.next() + -------------------------------------------------- + StopIteration Traceback (most recent call last) + in () + ----> 1 list_iter.next() + StopIteration: + +Making an Iterator +------------------- + +A simple version of ``xrange()`` (whoo hoo!) + +.. code-block:: python + + class IterateMe_1(object): + def __init__(self, stop=5): + self.current = 0 + self.stop = stop + def __iter__(self): + return self + def next(self): + if self.current < self.stop: + self.current += 1 + return self.current + else: + raise StopIteration + +(demo: ``Examples/Session08/iterator_1.py``) + +``iter()`` +----------- + +How doyou get the iterator object (the thing with the next() method) from an "iterable"? + +The ``iter()`` function: + +.. code-block:: ipython + + In [20]: iter([2,3,4]) + Out[20]: + + In [21]: iter(u"a string") + Out[21]: + + In [22]: iter( (u'a', u'tuple') ) + Out[22]: + +for an arbitrary object, ``iter()`` calls the ``__iter__`` method. But it knows about some object (``str``, for instance) that don't have a ``__iter__`` method. + + +What does ``for`` do? +---------------------- + +Now that we know the iterator protocol, we can write something like a for loop: + +(``Examples/Session08/my_for.py``) + +.. code-block:: python + + def my_for(an_iterable, func): + """ + Emulation of a for loop. + func() will be called with each item in an_iterable + """ + # equiv of "for i in l:" + iterator = iter(an_iterable) + while True: + try: + i = iterator.next() + except StopIteration: + break + func(i) + + +Itertools +--------- + +``itertools`` is a collection of utilities that make it easy to +build an iterator that iterates over sequences in various common ways + +http://docs.python.org/library/itertools.html + +NOTE: + +iterators are not *only* for ``for`` + +They can be used with anything that expects an iterator: + +``sum``, ``tuple``, ``sorted``, and ``list`` + +For example. + +LAB / Homework +-------------- + +In the ``Examples/Session08`` dir, you will find: ``iterator_1.py`` + +* Extend (``iterator_1.py`` ) to be more like ``xrange()`` -- + add three input parameters: ``iterator_2(start, stop, step=1)`` + +* See what happens if you break out in the middle of the loop: + +.. code-block:: python + + it = IterateMe_2(2, 20, 2) + for i in it: + if i > 10: break + print(i) + +And then pick up again: + +.. code-block:: python + + for i in it: + print(i) + +* Does ``xrange()`` behave the same? + + - make yours match ``xrange()`` + +Generators +---------- + +Generators give you the iterator immediately: + +* no access to the underlying data ... if it even exists + + +Conceptually: + Iterators are about various ways to loop over data, generators generate + the data on the fly + +Practically: + You can use either either way (and a generator is one type of iterator) + + Generators do some of the book-keeping for you. + +yield +----- + +``yield`` is a way to make a quickie generator with a function: + +.. code-block:: python + + def a_generator_function(params): + some_stuff + yield something + +Generator functions "yield" a value, rather than returning a value. + +State is preserved in between yields. + + +.. nextslide:: + +A function with ``yield`` in it is a "factory" for a generator + +Each time you call it, you get a new generator: + +.. code-block:: python + + gen_a = a_generator() + gen_b = a_generator() + +Each instance keeps its own state. + +Really just a shorthand for an iterator class that does the book keeping for you. + +.. nextslide:: + +An example: like ``xrange()`` + +.. code-block:: python + + def y_xrange(start, stop, step=1): + i = start + while i < stop: + yield i + i += step + +Real World Example from FloatCanvas: + +https://github.com/svn2github/wxPython/blob/master/3rdParty/FloatCanvas/floatcanvas/FloatCanvas.py#L100 + + +.. nextslide:: + +Note: + +.. code-block:: ipython + + In [164]: gen = y_xrange(2,6) + In [165]: type(gen) + Out[165]: generator + In [166]: dir(gen) + Out[166]: + ... + '__iter__', + ... + 'next', + + +So the generator **is** an iterator + +.. nextslide:: + +A generator function can also be a method in a class + + +More about iterators and generators: + +http://www.learningpython.com/2009/02/23/iterators-iterables-and-generators-oh-my/ + +``Examples/Session08/yield_example.py`` + + +generator comprehension +----------------------- + +yet another way to make a generator: + +.. code-block:: python + + >>> [x * 2 for x in [1, 2, 3]] + [2, 4, 6] + >>> (x * 2 for x in [1, 2, 3]) + at 0x10911bf50> + >>> for n in (x * 2 for x in [1, 2, 3]): + ... print(n) + ... 2 4 6 + + +More interesting if [1, 2, 3] is also a generator + +Generator LAB / Homework +------------------------- + + +Write a few generators: + +* Sum of integers +* Doubler +* Fibonacci sequence +* Prime numbers + +(test code in ``Examples/Session08/test_generator.py``) + +Descriptions: + +Sum of the integers: + keep adding the next integer + + 0 + 1 + 2 + 3 + 4 + 5 + ... + + so the sequence is: + + 0, 1, 3, 6, 10, 15 ..... + +.. nextslide:: + +Doubler: + Each value is double the previous value: + + 1, 2, 4, 8, 16, 32, + +Fibonacci sequence: + The fibonacci sequence as a generator: + + f(n) = f(n-1) + f(n-2) + + 1, 1, 2, 3, 5, 8, 13, 21, 34... + +Prime numbers: + Generate the prime numbers (numbers only divisible by them self and 1): + + 2, 3, 5, 7, 11, 13, 17, 19, 23... + +Others to try: + Try x^2, x^3, counting by threes, x^e, counting by minus seven, ... + + + +Context Managers +================ + +**A Short Digression** + +.. rst-class:: left build +.. container:: + + Repetition in code stinks. + + A large source of repetition in code deals with the handling of externals + resources. + + As an example, how many times do you think you might type the following + code: + + .. code-block:: python + + file_handle = open(u'filename.txt', u'r') + file_content = file_handle.read() + file_handle.close() + # do some stuff with the contents + + What happens if you forget to call ``.close()``? + + What happens if reading the file raises an exception? + +Resource Handling +----------------- + +Leaving an open file handle laying around is bad enough. What if the resource +is a network connection, or a database cursor? + +You can write more robust code for handling your resources: + +.. code-block:: python + + try: + file_handle = open(u'filename.txt', u'r') + file_content = file_handle.read() + finally: + file_handle.close() + # do something with file_content here + +But what exceptions do you want to catch? And do you really want to have to +remember all that **every** time you open a file (or other resource)? + +.. nextslide:: It Gets Better + +Starting in version 2.5, Python provides a structure for reducing the +repetition needed to handle resources like this. + +.. rst-class:: centered + +**Context Managers** + +You can encapsulate the setup, error handling and teardown of resources in a +few simple steps. + +The key is to use the ``with`` statement. + +.. nextslide:: ``with`` a little help + +Since the introduction of the ``with`` statement in `pep343`_, the above six +lines of defensive code have been replaced with this simple form: + +.. code-block:: python + + with open(u'filename', u'r') as file_handle: + file_content = file_handle.read() + # do something with file_content + +``open`` builtin is defined as a *context manager*. + +The resource it returnes (``file_handle``) is automatically and reliably closed +when the code block ends. + +.. _pep343: http://legacy.python.org/dev/peps/pep-0343/ + +.. nextslide:: A Growing Trend + +At this point in Python history, many functions you might expect to behave this +way do: + +.. rst-class:: build + +* ``open`` and ``codecs.open`` both work as context managers +* networks connections via ``socket`` do as well. +* most implementations of database wrappers can open connections or cursors as + context managers. +* ... + +But what if you are working with a library that doesn't support this +(``urllib``)? + +.. nextslide:: Close It Automatically + +There are a couple of ways you can go. + +If the resource in questions has a ``.close()`` method, then you can simply use +the ``closing`` context manager from ``contextlib`` to handle the issue: + +.. code-block:: python + + import urllib + from contextlib import closing + + with closing(urllib.urlopen('http://google.com')) as web_connection: + # do something with the open resource + # and here, it will be closed automatically + +But what if the thing doesn't have a ``close()`` method, or you're creating the thing and it shouldn't? + +.. nextslide:: Do It Yourself + +You can also define a context manager of your own. + +The interface is simple. It must be a class that implements these two *special +methods*: + +``__enter__(self)``: + Called when the ``with`` statement is run, it should return something to work + with in the created context. + +``__exit__(self, e_type, e_val, e_traceback)``: + Clean-up that needs to happen is implemented here. + + The arguments will be the exception raised in the context. + + If the exception will be handled here, return True. If not, return False. + +Let's see this in action to get a sense of what happens. + +An Example +---------- + +Consider this code: + +.. code-block:: python + + class Context(object): + """from Doug Hellmann, PyMOTW + http://pymotw.com/2/contextlib/#module-contextlib + """ + def __init__(self, handle_error): + print(u'__init__(%s)' % handle_erro)r + self.handle_error = handle_error + def __enter__(self): + print(u'__enter__()') + return self + def __exit__(self, exc_type, exc_val, exc_tb): + print(u'__exit__(%s, %s, %s)' % (exc_type, exc_val, exc_tb)) + return self.handle_error + +.. nextslide:: + +This class doesn't do much of anything, but playing with it can help clarify +the order in which things happen: + +.. code-block:: ipython + + In [46]: with Context(True) as foo: + ....: print(u'This is in the context') + ....: raise RuntimeError(u'this is the error message') + __init__(True) + __enter__() + This is in the context + __exit__(, this is the error message, ) + +.. rst-class:: build +.. container:: + + Because the exit method returns True, the raised error is 'handled'. + +.. nextslide:: + +What if we try with ``False``? + +.. code-block:: ipython + + In [47]: with Context(False) as foo: + ....: print(u'This is in the context') + ....: raise RuntimeError(u'this is the error message') + __init__(False) + __enter__() + This is in the context + __exit__(, this is the error message, ) + --------------------------------------------------------------------------- + RuntimeError Traceback (most recent call last) + in () + 1 with Context(False) as foo: + 2 print(u'This is in the context') + ----> 3 raise RuntimeError(u'this is the error message') + 4 + RuntimeError: this is the error message + +.. nextslide:: ``contextmanager`` decorator + +``contextlib.contextmanager`` turns generator functions into context managers + +Consider this code: + +.. code-block:: python + + from contextlib import contextmanager + + @contextmanager + def context(boolean): + print(u"__init__ code here") + try: + print(u"__enter__ code goes here") + yield object() + except Exception as e: + print(u"errors handled here") + if not boolean: + raise + finally: + print(u"__exit__ cleanup goes here") + +.. nextslide:: + +The code is similar to the class defined previously. + +And using it has similar results. We can handle errors: + +.. code-block:: ipython + + In [50]: with context(True): + ....: print(u"in the context") + ....: raise RuntimeError(u"error raised") + __init__ code here + __enter__ code goes here + in the context + errors handled here + __exit__ cleanup goes here + +.. nextslide:: + +Or, we can allow them to propagate: + +.. code-block:: ipython + + In [51]: with context(False): + ....: print(u"in the context") + ....: raise RuntimeError(u"error raised") + __init__ code here + __enter__ code goes here + in the context + errors handled here + __exit__ cleanup goes here + --------------------------------------------------------------------------- + RuntimeError Traceback (most recent call last) + in () + 1 with context(False): + 2 print(u"in the context") + ----> 3 raise RuntimeError(u"error raised") + 4 + RuntimeError: error raised + +Accounting +========== + +Personal Growth Plan + +Attendance + +All homework due for final grading by Tuesday, April 14 + +The Future +========== + +Book for More In-Depth Introduction: + +`Learning Python by John Zelle `_ + +Intermediate + +newcoder.io + +Dev Accelerator +-------------------- + +June + +3 code challenge questions. + +Main study resources: + +Go over class notes again. + +Do mock interviews with each other. Talk about code! + +Read up on Django basics. + +Data Science +-------------------- + +UW CSE 140: Data Programming +https://courses.cs.washington.edu/courses/cse140/14wi/ + +Machine learning example with Theano and MNIST + +http://deeplearning.net/software/theano/ + +http://yann.lecun.com/exdb/mnist/ + +How to Keep in Touch +-------------------- + +Drinks at Bravehorse after class + +TA for me at next Python F2 + +LinkedIn (paulpham@yahoo.com) + +Hacker Hour meetups on Tuesdays + +(Optional) Homework +======== + +Python Power + +More reading, etc: +------------------ + +**Iterators, generators, and containers:** + +A nice post that clearly lays out how all these things fit together: + +http://nvie.com/posts/iterators-vs-generators/ + +| +| + +**Transforming Code into Beautiful, Idiomatic Python:** + +Raymond hettinger (again) talks about Pythonic code. + +A lot of it is about using iterators -- now you know what those really are. + +https://www.youtube.com/watch?v=OSGv2VnC0go + +Optional Homework +----------------- + +Task 21: Timing Context Manager + +Create a context manager that will print to stdout the elapsed time taken to +run all the code inside the context: + +.. code-block:: ipython + + In [3]: with Timer() as t: + ...: for i in range(100000): + ...: i = i ** 20 + ...: + this code took 0.206805 seconds + +**Extra Credit**: allow the ``Timer`` context manager to take a file-like +object as an argument (the default should be sys.stdout). The results of the +timing should be printed to the file-like object. + + +.. nextslide:: + +Task 22: ``p-wrapper`` Decorator + +Write a simple decorator you can apply to a function that returns a string. +Decorating such a function should result in the original output, wrapped by an +HTML 'p' tag: + +.. code-block:: ipython + + In [4]: @p_wrapper + ...: def return_a_string(string): + ...: return string + ...: + + In [5]: return_a_string(u"this is a string") + Out[5]: '

this is a string

' + +Note that this is a **very** simple version of the very useful decorators +provided by Web Frameworks. + +.. nextslide:: + +Task 23: Generator Homework (documented above) + +Task 24: Iterator Homework (documented above) + +The End +======== + +Thank you! diff --git a/hw/hw11/test_mailroom.py b/hw/hw11/test_mailroom.py index 63312b5..185c0d4 100644 --- a/hw/hw11/test_mailroom.py +++ b/hw/hw11/test_mailroom.py @@ -36,7 +36,7 @@ def test_name_quit(): def test_name_donate(): - output, error = process(b'T\nbill gates\n200\nquit') + output, error = process(b'T\nbill gates\n200\n\nquit') if "eoferror" in error.decode().lower(): raise AssertionError("Quit unsuccessful") @@ -51,7 +51,7 @@ def test_name_donate(): def test_name_report(): - output, error = process(b'T\nbill gates\n200\nR\nquit') + output, error = process(b'T\nbill gates\n200\nR\n\nquit') if "eoferror" in error.decode().lower(): raise AssertionError("Quit unsuccessful") diff --git a/hw/hw18/html_render.py b/hw/hw18/html_render.py new file mode 100755 index 0000000..c16b5e0 --- /dev/null +++ b/hw/hw18/html_render.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +""" +Python class example. + +""" + +# The start of it all: +# Fill it all in here. + +# 4 Spaces used to make all indents easier +INDENT = " " + + +class Element(object): + + def __init__(self, name="", indent="", **atributes): + self.name = name + self.contains = [] + self.indent = indent + self.atributes = atributes + + def append(self, content): + self.contains.append(content) + + def render(self, file_out): + file_out.write("{}<{}".format(self.indent, self.name)) + if(self.atributes): # if we have atributes to add on to the tag. + for atribute, value in self.atributes.items(): + file_out.write(" {}={}".format(atribute, value)) + file_out.write(">\n") + for values in self.contains: + # If the content is another element render it + if(isinstance(values, Element)): + values.render(file_out) + else: # Render out its contents + file_out.write("{}{}{}\n".format(self.indent, INDENT, values)) + file_out.write("{}\n".format(self.indent, self.name)) + + +class Html(Element): + + def __init__(self): + Element.__init__(self, "html") + + +class Head(Element): + + def __init__(self): + Element.__init__(self, "head", INDENT) + + +class Body(Element): + + def __init__(self): + Element.__init__(self, "body", INDENT) + + +class OneLineTag(Element): + + def render(self, file_out): + file_out.write("{s}<{n}>{c}\n".format(s=self.indent, n=self.name, + c=self.contains[0])) + + +class Title(OneLineTag): + + def __init__(self, content): + OneLineTag.__init__(self, "title", INDENT * 2) + self.contains.append(content) + + +class Hr(OneLineTag): + + def __init__(self): + OneLineTag.__init__(self, indent=INDENT * 2) + + def render(self, file_out): + file_out.write("{}
\n".format(self.indent)) + + +class Br(OneLineTag): + + def __init__(self): + OneLineTag.__init__(self, indent=INDENT * 2) + + def render(self, file_out): + file_out.write("{i}
\n".format(self.indent)) + + +class P(Element): + + def __init__(self, content, **atributes): + Element.__init__(self, "p", INDENT * 2, **atributes) + self.append(content) + + +class A(Element): + + def __init__(self, url, text): + self.url = url + self.text = text + + def render(self, file_out): + file_out.write('{}{}\n'.format(INDENT * 2, + self.url, self.text)) diff --git a/hw/hw18/run_html_render.py b/hw/hw18/run_html_render.py new file mode 100644 index 0000000..fcd0e26 --- /dev/null +++ b/hw/hw18/run_html_render.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python + +""" +a simple script can run and test your html rendering classes. + +Uncomment the steps as you add to your rendering. + +""" +import codecs +import io + + +# importing the html_rendering code with a short name for easy typing. +import html_render as hr + + +# writing the file out: +def render(page, filename): + """ + render the tree of elements + + This uses cSstringIO to renderto memory, then dump to console and + write to file -- very handy! + """ + + f = io.StringIO() + page.render(f) + + print(f.getvalue()) + + codecs.open(filename, 'w', encoding="utf-8").write(f.getvalue()) + + +## Step 1 +########## + +page = hr.Element() + +page.append("Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text") + +page.append("And here is another piece of text -- you should be able to add any number") + +render(page, "test_html_output1.html") + +# ## Step 2 +# ########## + +# page = hr.Html() + +# body = hr.Body() + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text")) + +# body.append(hr.P(u"And here is another piece of text -- you should be able to add any number")) + +# page.append(body) + +# render(page, u"test_html_output2.html") + +# # Step 3 +# ########## + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text")) +# body.append(hr.P(u"And here is another piece of text -- you should be able to add any number")) + +# page.append(body) + +# render(page, u"test_html_output3.html") + +# # Step 4 +# ########## + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text", +# style=u"text-align: center; font-style: oblique;")) + +# page.append(body) + +# render(page, u"test_html_output4.html") + +# # Step 5 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text", +# style=u"text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# page.append(body) + +# render(page, u"test_html_output5.html") + +# # Step 6 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text", +# style=u"text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# body.append(u"And this is a ") +# body.append( hr.A(u"http://google.com", "link") ) +# body.append(u"to google") + +# page.append(body) + +# render(page, u"test_html_output6.html") + +# # Step 7 +# ######### + +# page = hr.Html() + +# head = hr.Head() +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append( hr.H(2, u"PythonClass - Class 6 example") ) + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text", +# style=u"text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# list = hr.Ul(id=u"TheList", style=u"line-height:200%") + +# list.append( hr.Li(u"The first item in a list") ) +# list.append( hr.Li(u"This is the second item", style="color: red") ) + +# item = hr.Li() +# item.append(u"And this is a ") +# item.append( hr.A(u"http://google.com", u"link") ) +# item.append(u"to google") + +# list.append(item) + +# body.append(list) + +# page.append(body) + +# render(page, u"test_html_output7.html") + +# # Step 8 +# ######## + +# page = hr.Html() + + +# head = hr.Head() +# head.append( hr.Meta(charset=u"UTF-8") ) +# head.append(hr.Title(u"PythonClass = Revision 1087:")) + +# page.append(head) + +# body = hr.Body() + +# body.append( hr.H(2, u"PythonClass - Class 6 example") ) + +# body.append(hr.P(u"Here is a paragraph of text -- there could be more of them, but this is enough to show that we can do some text", +# style=u"text-align: center; font-style: oblique;")) + +# body.append(hr.Hr()) + +# list = hr.Ul(id=u"TheList", style=u"line-height:200%") + +# list.append( hr.Li(u"The first item in a list") ) +# list.append( hr.Li(u"This is the second item", style="color: red") ) + +# item = hr.Li() +# item.append(u"And this is a ") +# item.append( hr.A(u"http://google.com", "link") ) +# item.append(u"to google") + +# list.append(item) + +# body.append(list) + +# page.append(body) + +# render(page, u"test_html_output8.html") + + + + diff --git a/hw/hw18/sample_html.html b/hw/hw18/sample_html.html new file mode 100644 index 0000000..f2687e9 --- /dev/null +++ b/hw/hw18/sample_html.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 diff --git a/hw/hw18/test_html_output1.html b/hw/hw18/test_html_output1.html new file mode 100644 index 0000000..d7d8573 --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output2.html b/hw/hw18/test_html_output2.html new file mode 100644 index 0000000..25d5cdc --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output3.html b/hw/hw18/test_html_output3.html new file mode 100644 index 0000000..b5b308c --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output4.html b/hw/hw18/test_html_output4.html new file mode 100644 index 0000000..671fee7 --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output5.html b/hw/hw18/test_html_output5.html new file mode 100644 index 0000000..92d4748 --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output6.html b/hw/hw18/test_html_output6.html new file mode 100644 index 0000000..342e88c --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output7.html b/hw/hw18/test_html_output7.html new file mode 100644 index 0000000..cac89ea --- /dev/null +++ b/hw/hw18/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/hw/hw18/test_html_output8.html b/hw/hw18/test_html_output8.html new file mode 100644 index 0000000..f2687e9 --- /dev/null +++ b/hw/hw18/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 diff --git a/requirements.txt b/requirements.txt index 5e95695..fd20a97 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ PdbSublimeTextSupport==0.2 Pygments==1.6 Sphinx==1.2.2 docutils==0.11 -gnureadline==6.2.5 +#gnureadline==6.2.5 ipython==4.0.0 jsonschema==2.4.0 libsass==0.3.0