{ "metadata": { "name": "", "signature": "sha256:d3cbe07a3ab863ac3cf1fcf2ad77869e682309aa40fd2f33747dafc40f18ede2" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook was put together by [Jake Vanderplas](http://www.vanderplas.com) for UW's [Astro 599](http://www.astro.washington.edu/users/vanderplas/Astr599_2014/) course. Source and license info is on [GitHub](https://github.com/jakevdp/2014_fall_ASTR599/)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "%run talktools.py" ], "language": "python", "metadata": {}, "outputs": [ { "html": [ "" ], "metadata": {}, "output_type": "display_data", "text": [ "" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Advanced Python\n", "## Mishmash:\n", "## Classes, Exceptions, Iterators, and Generators \n", "\n", "We have spent much of our time so far taking a look at scientific tools in Python. But a big part of using Python is an in-depth knowledge of the language itself. The topics here may not have *direct* science applications, but you'd be surprised when and where they can pop up as you use and write scientific Python code.\n", "\n", "We'll dive a bit deeper into a few of these topics here." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Advanced Python: Outline\n", "\n", "Here is what we plan to cover in this section:\n", "\n", "- Classes: defining your own objects\n", "- Exceptions: handling the unexpected\n", "- Iterators: sequences on-the-fly\n", "- Generator Expressions: the sky's the limit" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Classes\n", "\n", "Python can be used as an *object-oriented* language. An object is an entity that encapsulates *data*, called attributes, and *functionality*, called methods.\n", "\n", "Everything in Python is an object. Take for example the ``complex`` object:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "z = 1 + 2j" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "# The type function allows us to inspect the object type\n", "type(z)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "complex" ] } ], "prompt_number": 4 }, { "cell_type": "code", "collapsed": false, "input": [ "# \"calling\" an object type is akin to constructing an object\n", "z = complex(1, 2)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "# z has real and imaginary attributes\n", "print(z.real)\n", "print(z.imag)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1.0\n", "2.0\n" ] } ], "prompt_number": 7 }, { "cell_type": "code", "collapsed": false, "input": [ "# z has methods to operate on these attributes\n", "z.conjugate()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 9, "text": [ "(1-2j)" ] } ], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Every data container you see in Python is an object, from integers and floats to lists to numpy arrays." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Classes: creating your own objects\n", "\n", "Here we'll show a quick example of spinning our own ``complex``-like object, using a ``class``.\n", "\n", "Class definitions look like this:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class MyClass(object):\n", " # attributes and methods are defined here\n", " pass" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "# create a MyClass \"instance\" named m\n", "m = MyClass()\n", "type(m)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 12, "text": [ "__main__.MyClass" ] } ], "prompt_number": 12 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Class Initialization\n", "\n", "Things get a bit more interesting when we define the ``__init__`` method:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class MyClass(object):\n", " def __init__(self):\n", " print(self)\n", " print(\"initialization called\")\n", " pass\n", " \n", "m = MyClass()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "<__main__.MyClass object at 0x10370fc90>\n", "initialization called\n" ] } ], "prompt_number": 14 }, { "cell_type": "code", "collapsed": false, "input": [ "m" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 15, "text": [ "<__main__.MyClass at 0x10370fc90>" ] } ], "prompt_number": 15 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The first argument of ``__init__()`` points to the object itself, and is usually called ``self`` by convention.\n", "\n", "Note above that when we print ``self`` and when we print ``m``, we see that they point to the same thing. ``self`` *is* ``m``" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### A more interesting initialization\n", "\n", "We can use the ``__init__()`` method to accept initialization keyword arguments. Here we'll allow the user to pass a value to the initialization, which is saved in the class:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class MyClass(object):\n", " def __init__(self, value):\n", " self.value = value" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 16 }, { "cell_type": "code", "collapsed": false, "input": [ "m = MyClass(5.0) # note: the self argument is always implied\n", "\n", "m.value" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 17, "text": [ "5.0" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Adding some methods\n", "\n", "Now let's add a ``squared()`` method, which returns the square of the value:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class MyClass(object):\n", " def __init__(self, value):\n", " self.value = value\n", " \n", " def squared(self):\n", " return self.value ** 2" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 18 }, { "cell_type": "code", "collapsed": false, "input": [ "m = MyClass(5)\n", "m.squared()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 19, "text": [ "25" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Methods act just like functions: they can have any number of arguments or keyword arguments, they can accept ``*args`` and ``**kwargs`` arguments, and can call other methods or functions." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Other special methods\n", "\n", "There are numerous special methods, indicated by double underscores. One important one is the ``__repr__`` method, which controls how an instance of the class is represented when it is output:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "class MyClass(object):\n", " def __init__(self, value):\n", " self.value = value\n", " \n", " def squared(self):\n", " return self.value ** 2\n", " \n", " def __repr__(self):\n", " return \"MyClass(value=\" + str(self.value) + \")\"" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": [ "m = MyClass(10)\n", "print(m)\n", "print(type(m))" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "MyClass(value=10)\n", "\n" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Other special methods\n", "\n", "Other special methods to be aware of:\n", "\n", "- String representations: ``__str__``, ``__repr__``, ``__hash__``, etc.\n", "- Arithmetic: ``__add__``, ``__sub__``, ``__mul__``, ``__div__``, etc.\n", "- Item access: ``__getitem__``, ``__setitem__``, etc.\n", "- Attribute Access: ``__getattr__``, ``__setattr__``, etc.\n", "- Comparison: ``__eq__``, ``__lt__``, ``__gt__``, etc.\n", "- Constructors/Destructors: ``__new__``, ``__init__``, ``__del__``, etc.\n", "- Type Conversion: ``__int__``, ``__long__``, ``__float__``, etc.\n", "\n", "For a nice discussion and explanation of these and many other special double-underscore methods, see [http://www.rafekettler.com/magicmethods.html](http://www.rafekettler.com/magicmethods.html)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercise: A Custom Complex Object\n", "\n", "Create a class ``MyComplex`` which behaves like the built-in complex numbers. You should be able to execute the following code and see these results:\n", "\n", " >>> z = MyComplex(2, 3)\n", " >>> print z\n", " (2, 3j)\n", " >>> print z.real\n", " 2\n", " >>> print z.imag\n", " 3\n", " >>> print z.conjugate()\n", " (2, -3j)\n", " >>> print type(z.conjugate())\n", " \n", " \n", "Note that the ``conjugate()`` method should return *a new object of type MyComplex*." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If you finish this quickly, search online for help on defining the ``__add__`` method such that you can compute:\n", "\n", " >>> z + z.conjugate()\n", " (4, 0j)" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Exceptions\n", "### Handling the Unexpected\n", "\n", "Sometimes things go wrong in your code, and this is where exceptions come in. For example, you may have illegal inputs to an operation:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "0/0" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m0\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Or you may call a function with the wrong number of arguments:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from math import sqrt\n", "sqrt(2, 3)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "TypeError", "evalue": "sqrt() takes exactly one argument (2 given)", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmath\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0msqrt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0msqrt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: sqrt() takes exactly one argument (2 given)" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Or you may choose an index that is out of range:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "L = [4, 5, 6]\n", "L[100]" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "IndexError", "evalue": "list index out of range", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mL\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m6\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: list index out of range" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Or a dictionary key that doesn't exist:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "D = {'a':2, 'b':300}\n", "print D['Q']" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "SyntaxError", "evalue": "invalid syntax (, line 2)", "output_type": "pyerr", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m print D['Q']\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" ] } ], "prompt_number": 25 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Or the wrong value for a conversion function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = int('ABC')" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "ValueError", "evalue": "invalid literal for int() with base 10: 'ABC'", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'ABC'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: invalid literal for int() with base 10: 'ABC'" ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "These are known as ``Exceptions``, and handling them appropriately is a big part of writing usable code." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Handling exceptions: ``try...except``\n", "\n", "Exceptions can be handled using the ``try`` and ``except`` statements:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def try_division(value):\n", " try:\n", " x = value / value\n", " return x\n", " except ZeroDivisionError:\n", " return 'Not A Number'\n", " \n", "print(try_division(1))\n", "print(try_division(0))" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1.0\n", "Not A Number\n" ] } ], "prompt_number": 28 }, { "cell_type": "code", "collapsed": false, "input": [ "def get_an_int():\n", " while True:\n", " try:\n", " # change to raw_input for Python 2\n", " x = int(input(\"Enter an integer: \"))\n", " print(\" >> Thank you!\")\n", " break\n", " except ValueError:\n", " print(\" >> Boo. That's not an integer.\")\n", " return x\n", "\n", "get_an_int()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "stream": "stdout", "text": [ "Enter an integer: e\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " >> Boo. That's not an integer.\n" ] }, { "name": "stdout", "output_type": "stream", "stream": "stdout", "text": [ "Enter an integer: rew\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " >> Boo. That's not an integer.\n" ] }, { "name": "stdout", "output_type": "stream", "stream": "stdout", "text": [ "Enter an integer: 3.4\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " >> Boo. That's not an integer.\n" ] }, { "name": "stdout", "output_type": "stream", "stream": "stdout", "text": [ "Enter an integer: 4\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " >> Thank you!\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 31, "text": [ "4" ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Advanced Exception Handling\n", "\n", "Other things to be aware of:\n", "\n", "- you may use multiple ``except`` statements for different exception types\n", "- ``else`` and ``finally`` statements can fine-tune the exception handling\n", "\n", "More information is available in the [Python documentation](http://docs.python.org/2/tutorial/errors.html) and in the [scipy lectures](http://scipy-lectures.github.io/intro/language/exceptions.html)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Raising your own exceptions\n", "\n", "In addition to handling exceptions, you can also raise your own exceptions using the ``raise`` keyword:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def laugh(N):\n", " if N < 0:\n", " raise ValueError(\"N must be positive\")\n", " return N * \"ha! \"" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 32 }, { "cell_type": "code", "collapsed": false, "input": [ "laugh(10)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 33, "text": [ "'ha! ha! ha! ha! ha! ha! ha! ha! ha! ha! '" ] } ], "prompt_number": 33 }, { "cell_type": "code", "collapsed": false, "input": [ "laugh(-4)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "ValueError", "evalue": "N must be positive", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlaugh\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m4\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mlaugh\u001b[0;34m(N)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mlaugh\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mN\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mN\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"N must be positive\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mN\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0;34m\"ha! \"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: N must be positive" ] } ], "prompt_number": 34 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Custom Exceptions\n", "\n", "For your own projects, you may desire to define custom exception types, which is done through **class inheritance**.\n", "\n", "The important point to note here is that *exceptions themselves are classes*:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "v = ValueError(\"message\")\n", "type(v)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 36, "text": [ "ValueError" ] } ], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "When you ``raise`` an exception, you are creating an **instance** of the exception type, and passing it to the ``raise`` keyword:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "raise ValueError(\"error message\")" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "ValueError", "evalue": "error message", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"error message\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: error message" ] } ], "prompt_number": 37 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In the seminar later this quarter we'll dive into **object-oriented programming**, but here's a quick preview of the principle of **inheritance**: new objects derived from existing objects:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# define a custom exception, inheriting from the base class Exception\n", "class CustomException(Exception):\n", " # can define custom behavior here\n", " pass\n", "\n", "raise CustomException(\"error message\")" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "CustomException", "evalue": "error message", "output_type": "pyerr", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mCustomException\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mCustomException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"error message\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mCustomException\u001b[0m: error message" ] } ], "prompt_number": 38 }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Iterators\n", "Iterators are a high-level concept in Python that allow a sequence of objects to be examined in sequence.\n", "\n", "We've seen a basic example of this in the for-loop:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for i in range(10):\n", " print(i)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0\n", "1\n", "2\n", "3\n", "4\n", "5\n", "6\n", "7\n", "8\n", "9\n" ] } ], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "In Python 3.x, ``range`` does not actually construct a list of numbers, but just an object which acts like a list (in Python 2.x, ``range`` does actually create a list)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "range(10)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "range(0, 10)" ] } ], "prompt_number": 41 }, { "cell_type": "code", "collapsed": false, "input": [ "list(range(10))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 42, "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]" ] } ], "prompt_number": 42 }, { "cell_type": "code", "collapsed": false, "input": [ "import sys\n", "print(sys.getsizeof(list(range(1000))))\n", "print(sys.getsizeof(range(1000)))" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "9120\n", "48\n" ] } ], "prompt_number": 44 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Handy Iterators to know about" ] }, { "cell_type": "code", "collapsed": false, "input": [ "D = {'a':0, 'b':1, 'c':2}\n", "\n", "D.keys()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 47, "text": [ "dict_keys(['a', 'b', 'c'])" ] } ], "prompt_number": 47 }, { "cell_type": "code", "collapsed": false, "input": [ "D.values()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 48, "text": [ "dict_values([0, 1, 2])" ] } ], "prompt_number": 48 }, { "cell_type": "code", "collapsed": false, "input": [ "D.items()" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 49, "text": [ "dict_items([('a', 0), ('b', 1), ('c', 2)])" ] } ], "prompt_number": 49 }, { "cell_type": "code", "collapsed": false, "input": [ "for key in D.keys():\n", " print(key)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "a\n", "b\n", "c\n" ] } ], "prompt_number": 51 }, { "cell_type": "code", "collapsed": false, "input": [ "for key in D:\n", " print(key)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "a\n", "b\n", "c\n" ] } ], "prompt_number": 52 }, { "cell_type": "code", "collapsed": false, "input": [ "for item in D.items():\n", " print(item)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "('a', 0)\n", "('b', 1)\n", "('c', 2)\n" ] } ], "prompt_number": 55 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### ``itertools``: more sophisticated iterations" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import itertools\n", "dir(itertools)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 56, "text": [ "['__doc__',\n", " '__loader__',\n", " '__name__',\n", " '__package__',\n", " '_grouper',\n", " '_tee',\n", " '_tee_dataobject',\n", " 'accumulate',\n", " 'chain',\n", " 'combinations',\n", " 'combinations_with_replacement',\n", " 'compress',\n", " 'count',\n", " 'cycle',\n", " 'dropwhile',\n", " 'filterfalse',\n", " 'groupby',\n", " 'islice',\n", " 'permutations',\n", " 'product',\n", " 'repeat',\n", " 'starmap',\n", " 'takewhile',\n", " 'tee',\n", " 'zip_longest']" ] } ], "prompt_number": 56 }, { "cell_type": "code", "collapsed": false, "input": [ "for c in itertools.combinations([1, 2, 3, 4], 2):\n", " print(c)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "(1, 2)\n", "(1, 3)\n", "(1, 4)\n", "(2, 3)\n", "(2, 4)\n", "(3, 4)\n" ] } ], "prompt_number": 57 }, { "cell_type": "code", "collapsed": false, "input": [ "for p in itertools.permutations([1, 2, 3]):\n", " print(p)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "(1, 2, 3)\n", "(1, 3, 2)\n", "(2, 1, 3)\n", "(2, 3, 1)\n", "(3, 1, 2)\n", "(3, 2, 1)\n" ] } ], "prompt_number": 58 }, { "cell_type": "code", "collapsed": false, "input": [ "for val in itertools.chain(range(0, 4), range(-4, 0)):\n", " print(val, end=' ')" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0 1 2 3 -4 -3 -2 -1 " ] } ], "prompt_number": 59 }, { "cell_type": "code", "collapsed": false, "input": [ "# zip: itertools.izip is an iterator equivalent\n", "for val in zip([1, 2, 3], ['a', 'b', 'c']):\n", " print(val)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "(1, 'a')\n", "(2, 'b')\n", "(3, 'c')\n" ] } ], "prompt_number": 60 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Quick Exercise:\n", "\n", "Write a function ``count_pairs(N, m)`` which returns the number of pairs of numbers in the sequence $0 ... N-1$ whose sum is divisible by ``m``.\n", "\n", "For example, if ``N = 3`` and ``m = 2``, the pairs are\n", "\n", " [(0, 1), (0, 2), (1, 2)]\n", " \n", "The sum of each pair respectively is ``[1, 2, 3]``, and there is a single pair whose sum is divisible by 2, so the result is ``1``.\n", "\n", "1. What is the result for $(N,m) = (10, 2)$?\n", "2. What is the result for $(N,m) = (1000, 5)$?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### From iterators to generators: the ``yield`` statement\n", "\n", "Python provides a ``yield`` statement that allows you to make your own iterators. Technically the result is called a \"generator object\".\n", "\n", "For example, here's one way you can create an generator that returns all even numbers in a sequence:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def select_evens(L):\n", " for value in L:\n", " if value % 2 == 0:\n", " yield value" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "prompt_number": 61 }, { "cell_type": "code", "collapsed": false, "input": [ "for val in select_evens([1,2,5,3,6,4]):\n", " print(val)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "2\n", "6\n", "4\n" ] } ], "prompt_number": 63 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The ``yield`` statement is like a ``return`` statement, but the iterator remembers where it is in the execution, and comes back to that point on the next pass." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Breakout: Generator Practice" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fibonacci Numbers\n", "\n", "Here is a loop which prints the first 10 Fibonacci numbers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a, b = 0, 1\n", "for i in range(10):\n", " print(b)\n", " a, b = b, a + b" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\n", "1\n", "2\n", "3\n", "5\n", "8\n", "13\n", "21\n", "34\n", "55\n" ] } ], "prompt_number": 72 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using a similar strategy, write a generator expression which generates the first $N$ Fibonacci numbers" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def fib(N):\n", " # your code here" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "for num in fib(N):\n", " print(num)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example: Primes via the Sieve of Eratosthenes\n", "\n", "Here is a function which uses the Sieve of Eratosthenes to generate the first $N$ prime numbers. Rewrite this using the ``yield`` statement as a generator over the first $N$ primes:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def list_primes(Nprimes):\n", " N = 2\n", " found_primes = []\n", " while True:\n", " if all([N % p != 0 for p in found_primes]):\n", " found_primes.append(N)\n", " if len(found_primes) >= Nprimes:\n", " break\n", " N += 1\n", " return found_primes\n", "\n", "top_primes(10)" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 73, "text": [ "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]" ] } ], "prompt_number": 73 }, { "cell_type": "code", "collapsed": false, "input": [ "def iter_primes(Nprimes):\n", " # your code here" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Find the first twenty primes\n", "for N in iter_primes(20):\n", " print(N, end=' ')" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 " ] } ], "prompt_number": 66 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Bonus: Generator Expressions\n", "\n", "If you finish the above examples, try wrapping your head around *generator expressions*.\n", "\n", "We previously saw examples of **list comprehensions** which can create lists succinctly in one line:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "L = []\n", "for i in range(20):\n", " if i % 3 > 0:\n", " L.append(i)\n", "L" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 67, "text": [ "[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]" ] } ], "prompt_number": 67 }, { "cell_type": "code", "collapsed": false, "input": [ "# or, as a list comprehension\n", "[i for i in range(20) if i % 3 > 0]" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 68, "text": [ "[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]" ] } ], "prompt_number": 68 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The corresponding construction of an iterator is known as a \"generator expression\":" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def genfunc():\n", " for i in range(20):\n", " if i % 3 > 0:\n", " yield i\n", "print(genfunc())\n", "print(list(genfunc())) # convert iterator to list" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]\n" ] } ], "prompt_number": 70 }, { "cell_type": "code", "collapsed": false, "input": [ "# or, equivalently, as a \"generator expression\"\n", "g = (i for i in range(20) if i % 3 > 0)\n", "print(g)\n", "print(list(g)) # convert generator expression to list" ], "language": "python", "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " at 0x104537a00>\n", "[1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19]\n" ] } ], "prompt_number": 71 }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The syntax is identical to that of list comprehensions, except we surround the expression with ``()`` rather than with ``[]``. Again, this may seem a bit specialized, but it allows some extremely powerful constructions in Python, and it's one of the features of Python that some people get very excited about." ] } ], "metadata": {} } ] }