{ "metadata": { "name": "", "signature": "sha256:5c326f01565998050fafd5ee72c052f8d7aee5db09c020ecf7d345b357d15310" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "code", "collapsed": false, "input": [ "from IPython.core.display import Image" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 146 }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Die Python Programmiersprache\n", "W\u1d07\u026a\u029f \u0274\u026a\u1d04\u029c\u1d1b \u1d0a\u1d07\u1d05\u1d07 S\u1d18\u0280\u1d00\u1d04\u029c\u1d07 \u0299\u1d07\u0274\u1d1c\u1d1b\u1d22\u1d07\u0280\u0493\u0280\u1d07\u1d1c\u0274\u1d05\u029f\u026a\u1d04\u029c \u026as\u1d1b\n", "\n", "Eine flotte 40-Minuten Python Einf\u00fchrung.
\n", "Bitte unterbrecht mich sofort bei Fragen, Korrekturen oder Anregungen!\n", "\n", "[lukas-prokop.at/talks/glt14-python](http://lukas-prokop.at/talks/glt14-python/)\n", "\n", "\n", " Vortrag von Lukas Prokop
\n", " Grazer Linuxtage // 05.04.2014\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> \"It was nice to learn Python; a nice afternoon\" (Don Knuth, SAT 2012 in Trento, Italy)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Installation\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
python 2.xLinux python 2.6 or 2.7 is installed. Run python in the terminal.
Windows Manual installation via python.org
py3kLinux Install py3k via package manager
Windows Manual installation via python.org
\n", "\n", "Gentoo and Arch Linux: ``/usr/bin/python`` points to py3k [per default](http://distrowatch.com/search.php?pkg=Python&pkgver=3#pkgsearch)
\n", "In this presentation I am using ipython3 notebook (a different REPL)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Py3k?\n", "\n", "We are in the progress of migrating from python version 2 to 3. See Gerald's talk last year for [migration tips](http://www.youtube.com/watch?v=02NTKVtN5EU&list=PLWHx0EvwLLUFBeI_t8RDyyCxZirzoK4ua&index=7).\n", "\n", "Python 2.7 is the newest python 2.x version, but development goes on in Python 3 (currently Python 3.4). We use Python 3.4 in the following slides.\n", "\n", "Python 2 and 3 are incompatible. Do not write Python code for both versions simultaneously!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Python Tutorial" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(\"Hello World\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello World\n" ] } ], "prompt_number": 75 }, { "cell_type": "code", "collapsed": false, "input": [ "variable = 1\n", "print(variable)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\n" ] } ], "prompt_number": 76 }, { "cell_type": "code", "collapsed": false, "input": [ "variable = 42 - 5 * 3\n", "print(variable)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "27\n" ] } ], "prompt_number": 77 }, { "cell_type": "code", "collapsed": false, "input": [ "variable = 2\n", "variable = 3\n", "print(variable)\n", "print(type(variable))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "3\n", "\n" ] } ], "prompt_number": 78 }, { "cell_type": "code", "collapsed": false, "input": [ "# everything is an object (see OOP later) (\"dir\" lists all methods)\n", "print(dir(variable))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']\n" ] } ], "prompt_number": 79 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with simple datatypes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# scalars\n", "a = None # {None}\n", "b = True # {True, False}\n", "c = 1 # optional sign, sequence of digits\n", "d = 1.5\n", "e = \"glt14\" # \" or '\n", "f = b\"glt14\" # b literal for bytes\n", "g = 3 + 1j # complex\n", "print(type(a), type(b), type(c), type(d), type(e), type(f), type(g))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " \n" ] } ], "prompt_number": 128 }, { "cell_type": "code", "collapsed": false, "input": [ "float('inf')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 81, "text": [ "inf" ] } ], "prompt_number": 81 }, { "cell_type": "code", "collapsed": false, "input": [ "# arbitrary precision integers\n", "print(2**64)\n", "other_approach = (2**63 - 1 << 1) | 0b1\n", "print(bin(other_approach))\n", "print(other_approach + 1)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "18446744073709551616\n", "0b1111111111111111111111111111111111111111111111111111111111111111\n", "18446744073709551616\n" ] } ], "prompt_number": 82 }, { "cell_type": "code", "collapsed": false, "input": [ "# these datatypes are all immutable\n", "print(id(a) == id(True))\n", "print(id(b) == id(1))\n", "print(id(c) == id(1.5))\n", "print(id(d) == id(\"glt14\"))\n", "print(id(e) == id(3 + 1j))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "False\n", "False\n", "False\n", "False\n", "False\n" ] } ], "prompt_number": 129 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with strings (aka. unicode)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# The difference of repr(var) and str(var)\n", "print(repr('Und er meinte: \"Komm\\' doch zu den Linuxtagen!\"')) # repr" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "'Und er meinte: \"Komm\\' doch zu den Linuxtagen!\"'\n" ] } ], "prompt_number": 132 }, { "cell_type": "code", "collapsed": false, "input": [ "print(str('Und er meinte: \"Komm\\' doch zu den Linuxtagen!\"')) # str" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Und er meinte: \"Komm' doch zu den Linuxtagen!\"\n" ] } ], "prompt_number": 133 }, { "cell_type": "code", "collapsed": false, "input": [ "multiline = \"\"\"This is a rather\n", "lengthy \"{}\" and therefore\n", "break it into \\\n", "several {}\n", "\"\"\"\n", "\n", "print(multiline.format(\"message\", \"lines\")) # format is a *method* of the object *multiline*\n", "print(multiline[0:13] + \".\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "This is a rather\n", "lengthy \"message\" and therefore\n", "break it into several lines\n", "\n", "This is a rat.\n" ] } ], "prompt_number": 86 }, { "cell_type": "code", "collapsed": false, "input": [ "# You want unicode data internally. You get bytes via I/O.\n", "# bytes = sequence of 1s and 0s\n", "filecontent = b'[user]\\n\\tname = \"m\\xCE\\xB5isterluk\"\\n\\temail = \"admin@lukas-prokop.at\"'\n", "\n", "# convert to unicode data (you *have* to know the encoding)\n", "# str = sequence of unicode codepoints\n", "content = filecontent.decode('utf-8')\n", "\n", "print(len(filecontent))\n", "print(len(content))\n", "\n", "# now string operations on `content` work great and are well defined :)\n", "# we remove 1 unicode point (which was *2* bytes) and add 1 new unicode point\n", "content = content[0:17] + 'e' + content[18:]\n", "\n", "# writing data somewhere = converting to output encoding\n", "\n", "#with open('file.out', 'w') as fp:\n", "# fp.write(content.encode('utf-8'))\n", "\n", "# or fp.write shall take care of it (-:\n", "\n", "#with open('file.out', 'w', encoding='utf-8') as fp:\n", "# fp.write(content)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "61\n", "60\n" ] } ], "prompt_number": 87 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Complex datatypes" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# native data structures\n", "demo_list = [1, 2, 3, 4] # [heterogenous] sequence, mutable\n", "demo_set = {1, 2, 3, 4} # [unordered] set of unique objects, mutable\n", "demo_tuple = (1, 2, 3, 4) # semantical unit, immutable\n", "demo_dict = {1: \"one\", 3: \"three\"} # {key: value}, mutable, keys must be immutable\n", "\n", "print(len(demo_list), len(demo_set), len(demo_tuple))\n", "\n", "# Question: Which data structure is not syntactically supported by other dynamic languages?" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "4 4 4\n" ] } ], "prompt_number": 88 }, { "cell_type": "code", "collapsed": false, "input": [ "demo_set = {1, 2, 3, 4, 3}\n", "print(len(demo_set))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "4\n" ] } ], "prompt_number": 89 }, { "cell_type": "code", "collapsed": false, "input": [ "nested_list = [\"defn\", \"method\", [\"this\", \"m\"], [\"str\", '\"Your arg is: \"', '\" m']]\n", "print(nested_list[0]) # zero-based indexing" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "defn\n" ] } ], "prompt_number": 90 }, { "cell_type": "code", "collapsed": false, "input": [ "print([i**2 for i in range(20)])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]\n" ] } ], "prompt_number": 91 }, { "cell_type": "code", "collapsed": false, "input": [ "# pitfall #1: inclusive, exclusive\n", "\n", "import random\n", "# [0, 3)\n", "print(list(range(0, 3)))\n", "# [0, 3]\n", "print({random.randint(0, 3) for n in range(20)})" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[0, 1, 2]\n", "{0, 1, 2, 3}\n" ] } ], "prompt_number": 92 }, { "cell_type": "code", "collapsed": false, "input": [ "# operators on objects\n", "a = 1\n", "b = 1\n", "c = \"1\"\n", "\n", "# testing equivalence\n", "print(a == b)\n", "print(a == c) # python dislikes coersion" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "True\n", "False\n" ] } ], "prompt_number": 93 }, { "cell_type": "code", "collapsed": false, "input": [ "forty_two = 42\n", "answer = 42\n", "Image(filename='python_var_names.png')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "png": "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\n99NjpKbLSUyW7LJovZnjJBExhNCNmJ/+DskbEUWlBbvtvbDXHKM4H4YWjGJjp2alGguWjuXia+9o\nvxFzmbL9RmzQGE5uzQVL+/ddeoUFVlZeem19NdbSSey8mxyhJLOImYm5tOxd9ssMcufkzLi5M+XH\ncHJjb/vxQ3URFhY2depUbm5ubm7uSZMmzZgxw9zc/MSJE0+fPqXhdxzOfH19VVVVP3/+LDpO8NGN\nixYG2vSuCAwFWgZWeGwwQmjqxOndrAaPN9G2RAjlvHrc7WdXLrUdzcFFfsvHI0DlSpmZmLGRr0UF\nkBsbm7/HPrhpoGHGzDxYR7XFJ82YMH5KJ7HzfnoM1pKRk/Kt5usao42q85cghCLjQ7D22vrq3Pyn\naoo65M8O5IfCfPr0qaioqKampqampqSk5PXr11evXt22bZucnNycOXP8/Pyamppo9EWHnebm5tWr\nV69bt661tXWxomxWVNDcmdPoXRQYIrQMrLfv8xFC/DyC3S6dNU0aIdTW3vq9qeHXpcvUVvR7vSuX\n2iKEij+8eZybirXcTY5oam4cvP1BjJbyMoRQwqMo7O2d+9c52ccsVtBermmOELqTeL2jswMh9PBp\nIpFIVFP4L7AG8kNh1NXVo6Ojs7Kynj59mpCQEBYW5ujoqKysTCAQnj9/bmdnJyYmFhgYSHn48s9Q\nWlqqpKQUHByMEHJebxl36dRYrjH0LgoMHVoGVk1dFUKoo6O926VMBGaEEA6HY2Fm+XUp9ZtUv5ok\nIjZfaiFC6FqUP9YSHhskPmnGbInu71BBK9heYXJmXHtHe0dnR0xKpO4ioxHMLBpKeuwjOapqK1My\n4xFCKZnxQvzC2GwdZiA/FGbWrFk6OjrS0tIyMjJqamomJiZHjhx58OBBSUmJq6srLy9vWVmZtbW1\nrKxsUVERbb81HSUlJcnKyubk5IxkY7367wGYtPoL0TKwuMeMRQg9L8jqdumXyk8IIfGJMwZjNw2b\neo9KutHQWP+54mN6ToqxtiXN19KFjKTCWC6+hsb6x7kP07KSqmu/GWisRAixsrBpqyxH/98rTHkc\nT7l5hQbzhxIWFj5w4MD79+8PHTrEwcGRk5MjJSUVERHR+yeHvcOHD2tqalZWVk4SGZceEWC2VJPe\nFQE6oGVgzZkuhxDKePbgZeGzX5di+0Gmumv6NziR1NPejY6q4WgOruaWplsJ1yLir+AQzkjLoof+\nNFkpHo9XV9JDCCU8ir5z/7oA7zhsQw8hhO0Vxqfezn6ZWV5Ztljxp8Aa1B8KITRy5EhnZ+cnT55M\nnz79+/fvJiYmZ8+e7fdodNfU1GRubu7i4tLR0aGxQP7p7aDZElPpXRSgD1oGloG6GR6PJxKJu3wc\nOomdlItaWpsj4q7wjxUy07Pp67AEAgEhVFVT2UMflhGshlqrEEJX71wMjw1WkdccyD4mlStFCGkt\n1EcIxafexub4ydcqKcos4uMRaG5pcj26kWUEq5L0IspPDdIP1cW0adMeP35sbGxMJBIdHBwOHz48\nwAHpoqSkRFFR8erVqzgcbu+WtTGB/3KP4aR3UYBuaBlYk0TErAwdEELZLzPW/2Pa0FiPtTc2f9/o\nvqqyqvyk22VO9tF9HZaXmx8hVFRakJv/FCFUU1d19c7FzftXd+mG7RU+z896+z7fRMdqYF+F2pUq\ny6qNYmP/8Lmktr4a26rCEPAE7DBCXkG2orRql6vEB+mH+hU7O3toaKi1tTVCyMXFxdvbe+BjDqWE\nhARZWdnc3NzRHOy3fI+4b7UjEODy1b9afy5+LiotwF48zUufLCLOysJGXrR305FvNV/vJIbdTY5I\ny05WllNva2vNzH3Y2dkRfPTuAlm1LkN9Ki/FXpR+fifIN77b1S2QVWMiMHV0dujYzOMazVNTVzVT\nTMrHxbdLN4nJknNnzMt59Xg0B9fAT7+icqUsI1hV5DXvJkdITJacMfWnu4Mt1zT3CzuBENJUXvbr\n+H39ofoNj8f7+fnhcDh/f/9du3ZNnjzZ0NCw94/RG4lE8vb2dnV17ezslJgy8eYFH/FJovQuCtBf\n3/5/VVtfrW0tp2E5F3u7+4iDuNpoTStp8lwMM/OI8x6hF70iVOZp4vH42JTI10XPVy61TbvxduE8\nDcqhPpWXLrGW1V+/AHu7fMNClZUzAsNP/7rScfwiJ/cGCfELc4zilJ4hH3DoZlxgFnbsvwtsI0tf\nfcWI3x9foxL1K9VWWS49c/6pvcFd2mdLyMyRkLUx2bxCp5vZKOp/qIHD4/EXLlxYunQpkUhcvXp1\ndnY2bcenucbGRjMzMxcXl87OTgNNlce3LkFaAUzftrDGcHLHBDzptZu2ynLsMFkPxguIxgZQe1q2\nvrqZvrpZr91aWpsRQgPfH+zTSg00VmIHB3/V629FzQ9FEwQC4dq1a6qqqk+ePDE1Nc3JyeHkHKYz\nQcXFxcuXL8/Ly8Pj8Qed7HeuWz00t+EGDOHPmRHo6OzwCz0xa5o05dXIgGzkyJFRUVGCgoLFxcVr\n1vT/EOSgun379ty5c/Py8sZwckRdPOa83hLSClAa6INUh4/A8NPvy4oveF7/XYeD53ZhZwz8Dg7h\n9m09Liw4gfbFDQ98fHwXL17U1dWNjIwMDg62sKDBmR+0QiQSd+/e7e3tTSKRZopNvunrM0VUmN5F\ngWHnDwmsvILsQ+dd5WYr6S0y7rZDR2fH5Yiz5ONx3RrBzHJiz6VBqW/Y0NbW3rp16/Hjx7du3aqm\npiYo2P3lQUOsrq5u1apV0dHRCCFjbbUAnz3sI+Hu26AbDBxY8Q9vP8l7JCk+t7j0jV/YvywjWH+d\n+SZjIjC9SawbyvKGLU9Pz+jo6Ldv365fv/727dv0Lge9fv3awMCgsLCQQMB77oBJK9ATRg2sjGcP\n7FyN2/9/OR7PGN4rx2L+4L05GmJjYwsODlZUVLxz505kZOTy5fR89mJkZKSVlVVDQwMfD3fY6YMq\n8t0chwWAjFEn3cvKP4iOmzyCmYVnDO/KpbaJwbmDfanzn2TevHmWlpYIod27d7e3d38N9mAjEoku\nLi5GRkYNDQ0ykhJZUUGQVqBXjLqFZbTEoss9kUGfeHl5RUREvHnz5tSpU9u3bx/itdfU1Jibm8fG\nxiKErIx0zx2Ah8gDqjDqFhYYID4+PmdnZ4SQh4fHt2/feu1PW2ZmZrGxsQQC/qCTQ4C3G6QVoBIE\n1t9r27ZtoqKitbW1Hh4evfemqdevXyOETu/bucveCqbYAfUgsP5erKys+/fvRwidP3+eLvf5Ex03\nLE6qAAwEAuuvtmrVKmlp6ba2tr1799K7FgB6B4H1V8Pj8dg9Z65du/b8+XN6lwNALyCw/naLFi1S\nUVEhkUhDP5MFQF9BYAGERVVkZCRsZIFhDgILICUlJVVVVRKJtG/fPnrXAkBPILAAQv/fyLp161ZW\nVvfP8gFgOOj+TPe8/Kydh9cNcSmAGll56YMxrKKiooqKSkpKire39/Xrv71FDwD01TWwmJiYEELv\ny4rflxXTox5AFWZmZpqP6ebmlpKSEhERkZ+fLyEhQfPxARi4roFla2tbUVHR0dFBl2oANQgEwvr1\n62k+rKqq6oIFC1JTU/fv33/t2jWajw/AwHUNLCEhoTNnztClFEB3rq6uWlpaN27ccHd3FxcXp3c5\nAHQFk+7gP5qamrKysp2dnYcOHaJ3LQB0AwIL/GTXrl0IoZCQkJKSEnrXAkBXEFjgJ8uWLRMXF29v\nbz969Ci9awGgKwgs8BM8Hr97926E0MWLFz99+kTvcqjV2tZmttmVY8ZCtVX25ZVV9C5ncD3Ofck0\nZR5uouz3piZq+je3tMampJ+7Eu519pLvtZtPnr8iEomDXeQgYdQ7joLBY25ufujQofz8fA8PjwsX\nLtC7HKqcunw9NOoeQuh+2tNd3qcDff7Ym080t7RaOrp3dlKVOF+rqt1P+AVHxnSJtnECvK4O1uvN\nDRnuZmSwhQW6IhAI2N1mAgMDGWUm69OXCorXX+lYyWDb5X36zbtSanqmZGZLqBmfuxL+64ZYWXml\n/Z7DerbbqdxGGz4gsEA3TExMZs+e3d7e7uXlRe9aqLJ6uQ52n2UcDmdjuoze5QyWlMzsk5fCqOmZ\n+7pQe82W6tp6JgJhtsTURQqyc2dOYxnx062o7yY9Mt24m8qNtWECAgt0A4fDubm5IYQuXbrEEBtZ\nc2dOexEf6nfINetO0Ao9jUFdV87LgkEd/3caGpvWOO3jZB/Va8/OTqLFNreW1rZtNivLn8bnxly9\nH3I2Oyq45nnShYO7ebhGk3vGJKddDLs1mFXTGAQW6J6+vr6kpGR7e/vBgwfpXQtVpogK25rqz505\nbVDXkvOywGoHfe5psd3jeF1Do9tm2157Bty487Kw+Kjr1mP/bKOMJzZWFjszg6e3g8YJ8JIbPU8H\nDEq5gwMCC3QPj8cfOHAAIRQYGJibm0vvcoaF+u+Npht3t9HjSY7YptApdychft6ee5JIpCO+wdqq\nittsVnbbYaKw0NV/PclvP36pyCt4S8taBxMEFvitpUuXLlmypLOz097enkQi0bscOiORSFY73ItK\nPw7G4B8+l3+tqv7d0uraeluXAwaaKub6Wr0OlZHz4t2HsmP/bOuhj7KclLKcFPlt0fufzl/JfpE/\nbP91Q2CBnhw7doyZmTkjIyMsjKq53j/YnmPnb8an0HzYnJcFK7f8M1lZv7Dkw+/6OLgdbm/vOO+5\ni5oBEx49NtFVF58k2nM3NSU58uvG5uafV+ctoWbse+1mS2sbNWscShBYoCfTpk2zt7dHCDk5OTUx\nyCHwL19p/1zYA6f9aT7XE5uSvtjcXlrP4tqd+I7Ozt91C4+9Hxp17+wBZz4ebmqGNdFRO+hk32s3\nYUEB8ms+Hq4uS9+8K123+6Coop7HqYtVNXXUrHdoQGCBXuzZs4eLi+vTp0/Hjx+ndy09qamrPx10\nXUrH3NjBpdsOzS2tEbFJxvYuJg4/NlU6Ojv9Qm8pGtmMmaXKMWOhkrHt9buJXT7V0Nika7Ntz9Hz\n5JY370pxE2XJ/zzK6tsEX1t7e+CNqJmaptprtiSlP8UaR41kG8PJ8Wvnim/VG1wPmeqqG2urUTm+\nxJSJ1DzwsZMiIudM/+nOHOMF+bAXX6uq3Y5dEFHUdXA7XFw6LC57gDPdQS94eHjc3d23bNni4eGh\np6c3a9Yself0k7qG79H3H0XE3Y9NScd2YRRlZlN2qKqpi05KvZ3w4F7q48amZoSQpvJ8hNC7D2XG\nDi6U5yikZT1Py3qeV/D2gOMGcmNbW7u+xkJ9jYUHTgWUln1BCAnw8ng4/nc/sqkTRKgstba+4XxI\nxMlLYZTbgPxjuTdZmtpbGHGN5vz1I3a7PAkE/Jn9zlSugnqfv1ZiL2RnTecf+9O2W/jZw/dSM30u\nBCemPUEINTW3nA0OPx8SaaCpsmPtKnkpSZoXQz0ILNA7BweH0NDQjIyMVatWPXnyhJWVld4V/VBe\nWWVs78LKMiL3deHvJlxSnz6r/97Y0dGJpRUmLev5MjtHQd6x7lvtxnKNyX1deDkiur2jAyF08Eyg\n1sL5SjJzsJ48XKNtTfURQudDIrHAGs3BjrVQr7Tsy4mAaxdDb1OeWT5t8gTHteYWBtpdzuckuxQe\nfSfx4S3fI5SnJtBKVl4+9qLb82w1FshrLJDPfV3o4xt8PTqho7OTSCRGxCZFxCYpyczZYbdqqZoy\nXS7rgcACvSMQCJcuXZKSknrx4sXmzZt9fX3pXdEPArw8qTf8EEKv35bM0DDpto++hgpCaL25Ic+c\nxQ2NTQihl4VFWpab92y22bF2FR7/Y1ZEW1Vx+XonhBCJRPo3IJQcWAOU/SL/iN+V8Jj7lLNUSjJz\nnNZZ6C1e0MPf/McvFVv2HbEw0F6mvpAmlVBqbmnFtp7GCfBaGur+rtuc6WIhJzy8djoc979KTttH\nWbmPsnLFJopstzW3NNTFLjAYMjCHBagiJiZ27NgxhJCfn5+fn99AhsrKyvry5QuN6vph+tSJ3e5S\nkTEzMZFndr5V14X867Fz3WpyWiGEDDRV5kwXw14nZw700UEkEikmOW3Ryg0yS1eHRt3D0gqPxy/X\nUs2IDEi94dfzFgqJRLJ22s/BPvKk+44BVtKtsOiEpuYWhNBh5829Jo6IkMDxPds/ZkQfdHIQ5BuL\nNRaWfFjv6iWioLv/5MVvNbWDUWS3ILAAtdatW2djY4MQ2rhxY2xsbP8GuXz58oIFC7CHBtD2ZJ9e\nr1lh+f9f5oTxgkvVlH/tMG/OTOxFVU1dbX1D/8ogz6nrWG9NzvgRfGysLOvNDd8khUec86ZmDuhs\ncHhi2hM/r3+6nYkfICKReOxiCEJId9ECak7swozh5Nhlb/X+0R3/w3umT52INVZW1+w9fkFEQdd+\nz+FBOkOtCwgs0AdnzpxRVlZua2szMjJ68OBBnz5bV1dnZ2dnZWXV0tIyYsSPC5UHp8x+4h7z3zYa\ntvPYJzV19V5nL01QWmq9c//rtz8uwBzLNWbvlrUf0qPPHXCZIipMzThFpR93HjppY7psiYpCX2ug\nxqXw6BdvisYL8Pl7/9PXz45gZrY2WfoyPizK/xj51NPmltZzV8LFFxkZbtiZkfOC1vX+BAIL9AEL\nC0tcXNzixYubmprU1NQOHz5M5QeDgoLExcWxfUlbW1s+Pr7BLLOfKPcQ+3oPg4NnAkUU9Hb7nCEf\nAZwsOv7MfucP6dHYvD6V4xCJREtH97FcY3o+Vb3fKr5V7zx0ko2VJfKCD5Undv0Kh8PpLlrwIMz3\n8a1LRksWY78bkUiMjEtWMLRWNLL5SHG3H9qCwAJ9w8bGdvPmTW1t7Y6ODhcXFxMTk3fv3vXQ/8GD\nB0uWLLG0tKyoqODl5b18+bKfn99w27YauDuJD8lHAOVmz7hx9lBhUoS9hREbK0ufxvHxDc7IeRHg\n7UbNXRn6ikQiWe/cX1vfEHbaS3bW9IEPiH3Tt8mRDquNR7L9OHacnp2HHU4dDBBYoM84ODiioqJc\nXFxwONyNGzckJCTMzMyioqIqKyvJfV68eOHj4yMvL6+iohIXF4fD4dasWZOfn7969Wo6Vj40+h3H\nLwuL9x73XW9uuFhRlrYlYTxO+cempPsf3qO3eMFgjD8E4LQG0B94PN7Ly0tPT8/BwSE3Nzc0NDQ0\nNBQhNGrUKGZm5tra/w4bMTExmZqaOjs7S0rS84TDwaa3eMGrwnfYRtbj3JfG9i6TRcdvtzG3MtIl\nb3r06ta9lNa2tnNXws9dCaemP8eM/8548Nrp4LLBqofOV2/HuZ/wPeXu1MN5DH315PmrI75XIuOT\nKPeg58+VpOZU+/6BLSzQfwoKCllZWTdv3jQxMWFjY0MINTY2YmlFIBAUFBQ8PT0LCwuvXLnyZ6cV\nQsh1o/WH9CjPHfYCvDxYS3HpJwe3wyKKunuPX6isrqFveQmPHq9x2u+108FhtfHARyORSNFJqQtN\n7ebpW92IScTSCo/HG2iqpEcEpEcECAvyD3wt3YItLDAgBAJBX19fX1+fSCR+/PixtLQUIcTDwyMk\nJMTF1fWS2j8b12jO3Q5rdtitunIz9ujFK9iBwqqauv0nL3pfCLIy0ttuaz51Qk8HCscL8JNPrfid\n2voG8j3dZWdNJx8oGCfw2+MYGTkvDNY5Oa2zcF5v2bev9IvWtrYrN2OPXgzJL/rvPrRsrCyWhrqO\na82pPAw6EBBYgDbweLyoqKioaC93NfnjYQf+1xjrxSSnHfG7kpKZjRBqaW07HxLhe+3mMnXlHWst\nFKS7vx7TykjXyqiX/bXopFQ9m+3Y66Rr59hHjuy5f17BWx3rrTYmyygvkPwViURqbG7uYbSauvpz\nVyJOXQ6jfIoaLzeXw2pjh9XG1B8GHSAILACogv//VDp2yWHPcDicziIlnUVK2S/yfXyDw2Pvd3YS\niUTizfiUm/EpCtKznOwslqopU55IMRiKSj9qrt6kr7HwhNv2nnv+GxjKxsqybuXyXxeVln057n/1\nYthtyosxxSaKbLNZaWWkN8SX5nQNrPLy8iNHjjQ09PM0XzAERo0atX379vHjx9O7kL/LqJFs2Ivy\nyioikUhl1khLSoSeOoj9zfuH3cFm5dOz8wzWOQ325Xhl5ZXqqxyU5aT8vP7p+cBl6tNnHqcuvn8U\n1aUduxCSPEuFUZSZvWPtqiFI2251DSx/f394Rvnwx8bG5unp2Xu/v0lzSyv24ne3bejobcuIPALq\n7rKhCeN/HPlqam55+OSZirw09vbAaf8N5kY931BBdJzgCTdH96122F4VdnIpdjme27ELGy2N7VcZ\n0/aWDN9qatUtHNo7Ooy0F0fdf/i7bvXfG7NfFFwMu2W+TItj1H/7g7Ep6T6+weRLixBCeDxeX2Oh\nk53F8Lq9TGtrK0Joiug0TeU/9uFuDC0pPSa/+AX2rwmQNTY1k4/ElX7q/qzFsvIfp4n97jpBynvU\nVdXWTRQWoly6SEH2Ung09tpss6v7VjsertEXQ2+P5R5NZdZgl+M5rjW/cjP2mH/Iq8J36P83yTt0\n7nLmzUBJ8SnUjNOrhsYmLcvN2Lw4+W6FPbOj2Bk0WOd0614K+e1INlZLQ9nKIxUAABEkSURBVN3t\ntiuHYE69V93PYU2fOtvV/tAQlwKo8a3ma37x4F6uxYguXI0kbxN9q6m9FB7dZfb6Rkwi+aYCFd+q\nb91LwW47Q5b7ujD+YQb5rd+1m1LTxQmE//Z6THXVPU8HYEfoyiur1rt6IYQMNFV8D7r2qVTyrHxs\nSvoRvyvYVkxTc0tdw/c+jfM7La1tejbbsl/kU/+RuTOnyUhKkN+Sry7i5ebaaGniYEHjrb+BgPOw\nAAPLfPZisbm9qKKeo+cJyvY1TvuE5+vo2+1ACN2ISZyjvbLLhobBOqfp6ib7/vVDCL0qfKdgaC2z\n1IJyX9L32s3x87WdDv5LbhnBzHwv+PRyLVVO9lGjOdgVpGcFHdsXcc6b+vNCKeFwOG1VxaSr57Kj\nglfoaTARCP0YpFtb9x998DinTx+xMzPo0iI2UeS8564P6dFum22HT1ohOEoIGJq8lOT9kLM99zHW\nVuv5hugzxCalR1D1gAkRIYGIc959qI8Kc2dOu3bS85DzRlYWqq461F20gFTytIcO5z13Ufl8nd85\ns3/n3JnThuf1nhBYANDf4F3L0g/SFLuHww3sEgIAGAYEFgCAYUBgAQAYBgQWAIBhQGABABgGBBYA\ngGFAYAEAGAYEFgCAYUBgAQAYBgQWGGqFhYWNjY30rgIwJAgsMKRqamr09PSqq6vpXQhgSBBYYOi0\ntLQYGhoWFhbS5WaV4A8AFz//sTo6OkpKSqqqqr5//3GjJQEBAVFRUQ4ODrrU09raunz58uTkZAKB\nwM3NTfnUVQCoBIH1RyESicnJybGxsQkJCQUFBW1t3dwsmIuLS1RUdNKkSdLS0nJycjIyMmPGDPoj\nT2pqaszMzOLj4wkEQmBgoKtr3256BwAGAusPUVdXd+LECX9//48fP5Ib8Xg8FxfX6NGjEUJEIvHL\nly+tra01NTU1NTW5ubmRkZEIIRwOJyYmpqioqKqqqqqqOm7cOJrX9uzZM2Nj4+LiYmZm5uDgYFNT\nUwgs0D8QWAyPRCJduHDBzc0N28kaMWLE4sWLtbW15eXlp0+fPpLiSXMkEunz588lJSUlJSUFBQXZ\n2dnZ2dnfvn178+bNmzdvAgICEEJTp05VUVHBwktAQGCAtdXW1rq7u585c6ajo0NAQCAsLExZWXmA\nY4K/2ZAGVv33uut3L928d/VNySsmAvOsadJ7NnpLis8ld2hvb8t+lRkaFfClsizsZALW+PZ9/qkg\nr3upUcxMzIsVtPds8uEZw0s5LIlEirp/3Tf0+Ku3zwkEwsTxUxfILrZcbi86bpKPn9vZKz6tbS0I\nIa7RPDKSCoGHb5FnfF2Pbsp6kR5/KZs81MnLB/2vn6ysrkAIzZg6J+xkAveYseSlbe2tIbf9IuKu\nvC7KG83BNX3KrPUrHRfI/nc3y5bW5szchzdigiqrK66fSkQIJabddT+5/XPFx2mTZu62P6Qks4i2\nP2l1dbWVlVVUVBRCiIeHZ8eOHdbW1nx83T8EGIfDjRs3bty4cUpKSuTG0tLSJ0+epKSkJCcn5+fn\nv3379u3bt35+fgghCQkJVVVVRUVFRUXFvj4htbKy8vTp0ydPnsSeXK+hoXHp0iVBwWF0mzrAiIYu\nsCLiruw+4jBtsqSynPrcmfJR968/yrq/YovGg2uvx3LxIYTKKj6Yb1tSWPIaIcTJ/mNW5VzIkUPn\nd3cSO1lZ2Oq/116Pufz568frp+5Tjux2YmvAjVPmy9bamGwuKi0IuHH6/NWjj7KS7l3OcVq7f+li\n00WrJEkk0pVjMVLT5cifIhKJd5PDv1aVF5UWTBGdhjVuttxta7JFTI1TS1n/olcE5Vo+fnlv4ajz\nueKjpvIyGUmFgncvkzPjkjPjPLb9a2OyGSH0ueLj2t1GLwuftXe0s4/kQAjdiA3a6mGFPRwhN/9p\nzqtM2gZWUVGRhoZGSUkJDofbtGmTu7t7P54Ojz2u2djYGCFUXl6enJx8//79xMTE0tLS/Pz8/Pz8\ns2fPIoSEhIQUFBRkZGSmTJkyefLkyZMn/zp5X1dX9+LFi+zs7JiYmKSkJOyxWvz8/D4+PhYWFrT4\nxuBvN0SBte+k44Vrx0y0LU/suYS1bLRwnm80paau6lHWfX11M4TQOH6RlKuvopPC7VyNm5q/I4Tc\n/91+5bbvno0+ZkttRrGxH/Hbeyxg/6OspJKPbycKT8XGeZqX5n/9pNESC2/nC1jLCl3rpXaK2io/\nHlskPmnGrGnSz/OzMnJSKAMrM/fh16pyhFBkfMhOOw9ye35xHpFI1FE1pKy/vLJM11ZeRGhialgB\n/9gfT3+KjA/Z6L5q30lHFXnNySLiQvzCd/0fxz+8vcZZv7H5e8G7l05edpstd1sZOhzz3xd864LE\nlO4fUN4/ubm5WlpaFRUVvLy8QUFBWlpaAx9TQEDAzMzMzMwMIfT27dvExMSkpKSMjIyysrLPnz+H\nh4eHh4eTO/Pz848aNYr8tq6urqqqinI0MTGxrVu3WllZsbGxDbw2ANDQnIeVlp184dqxcfwi1sab\nyI38Y4XEJk5HCJVXfqbsPHfGPIRQR2eH//WT4XHBty88sjXdMoqNHSFkYbAe6/Ot5iu5/8vCZwgh\nAuG/5BUWnJAQ9Gyd2X/P5tZS1kcIJaRFU64o6v517MXNe1cp21My4wl4gqr8T3//jgdtm1uaLnpF\nktMKIbRc03y2hExHZ0fIbT9yI5ZKJBJpx0FbUx0r53UH+McK/rPRm5N9tMJclV5/KyrV19djaSUs\nLPzo0SOapFUXU6dO3bBhw40bNz59+vThw4fr169v27ZNS0tLTEyMhYUFIVRRUfGOApZW7OzsysrK\nu3btys7OfvPmzYYNGyCtAA0NxRbWbAmZ1LCCScJiXZ7DwTKCFf3yiF1yn4Pndt04nTRj6hzyIl5u\nfuwF5UfG8YsghMKiA3m5+R1t9mJjYvuYZFrK+ocv/JOVl15bXz2Gkxsh1EnsvJscoSSz6HFuamnZ\nu+yXGdIz52OdkzPj5s6Ux7phXrzJSc6M01ZZzsbKVv+9lnJkQd7xz/Ozsl9mkluYmZixF0WlBSHH\nY7HXHKM4H4YWYLFLE7du3aqsrBw/fnxqampfZ5f6QVhYWFhYGNttRAgRicSysrL379/X1dU1Nzcj\nhHA4nKCgoKioqKCgIIF2T6wCoIuhCCz2kRzsIuKULQ2N9UWlBbX1PV2f4bR2P7a1Rdbt6dHqSnpa\nyvpxD2+dDjoUGR9ib+60St9uBPNPT0wSnzRjwvgp7z8V3U+PMdRahRDKyEn5VvN1jdHGkWzs91Lv\nRMaHYIFVW1+dm//Ued0Byo/fSriGEIpJiYxJify1ACYCk8o8zV/bVy61Hc3x34wSH89Aj7hRqqys\nZGdnj4qKGoK0+hUej8cibOhXDf5yQzfpTiQSUx7H302OyHj24MPnd8KCE6prv/XQf5maKTXD4nA4\nP6/wEwEeZ654f674+M+xzedCjng6ntJYsJSym5bysvNXjyY8isIC687965zsYxYraLd3tN9LvXMn\n8fq+rSeYCEwPnyYSiUQ1BR3Kz759n48QWmO0scvEFmbCuMlC/N386S5TW0FN/X318ct77EVQUNCc\nOXN66grAH2eILun68LlEx3aehaNOY1PD/q0nXsdXZYQXT5ssSZPBCXiCo617RnixrekWVha2sooP\nVjuXnQ3xoeyDTWMlZ8a1d7R3dHbEpETqLjIawcyioaTHPpKjqrYyJTMeIZSSGS/EL9xldvxb9VeE\nEPtIDoW5Kr/+021aIVpvUpEVl75BCElKShoYdH1aLwB/vKEIrIbG+hWb1Z/nZx3Z5Xf+QJiaog75\nrAUa4h8ruH/ridSwgoXzNBBCB8+6FH94Q14qI6kwlouvobH+ce7DtKyk6tpvBhorEUKsLGzY8cTI\n+BCEUMrj+C6bVwih0ZxcCKGcV5loGFg4Tx0hpKqqSu9CAKCDoQisfy95vi8rni0hY6ZnQ/PBiz+8\n+fL1E/ntOH6RoCPRs6ZJE4nEF29yyO14PF5dSQ8hlPAo+s796wK84+ZLLcQWLdc0RwjFp97OfplZ\nXlm2WLFrYM0Sl0YIZTx78L6smOb19xUOh0cIMTMz07sQAOhgKALraV4aQkhUaBJl44fPJZ/KSwc+\n+K2E0MuR5yhbmJmYsbPnuTh5KNu1FuojhOJTb8c+uGmgYUaewleUWcTHI9Dc0uR6dCPLCFYl6a7n\nduprmOFwOCKR6Hx4fUdnR5elodEBJwIPIADA4BuKwMKO9GfmPqz49hkhVNdQc/aKt97a+e3tbQih\nhsY6hFD997r+DV5dW3kuxCcx7S65paGxPiUzXpBvvNxsJcqeyrJqo9jYP3wuqa2vxraqMAQ8AZsg\nzyvIVpRWZWMdiX4mMVnSRMcKIZT6NHHVdm1sDh7rb+Goc/Kyl5medf+KBwD0yVAEFvbX/rWqfL7R\nlPlGkyWX8AXfuhB0JFpJZjFC6FzIERl9kdDoAKzzh88l2IvSz++6jFPy8e2vi1hGsLZ3tK/eoWu8\ncdHBc7s8z7qoWcyura/+d8+lLtHDMoJVRV4TISQxWZLy9C70/71ChJCm8rJuv8JBx9PKcuoIoYdP\nEhaaTZfQ4BZbzKm1RqaqpvLW+VTKs0nJm42/1g8AGKAhCSxtS0/H0yJCExFCozm49mzyeXDt9WwJ\nGVMdK0720aryWpd97tit2PapvFR9tZTBhh9X8xvaqyyxlsX+/ptbmnRt5ZVMf5zMtWW/pZKpeG7+\nU4SQy3rP464BqvJar4vyzoccibp/XWWeZkLQMywNu9BWWS49c/6pvcFd2mdLyMyRkLUx2bxCZ023\nX4GNdeTV43E+Lr5S0+XYWEe2d7SNFxB123Tktm8a+Wjgp/LSJday+usXYG+Xb1iosnJGYPjpAf56\nf5uW1rbtB47zSWtwz1m8YtPu8sqq3j8D/hq4Lieau7m5eXh4LFUzPe8RSq+aQA+2eVqHRQc6Ojoe\nOXKE3rX0n4iIyMePH2MC/12iotBl0XpXLz4e7tkSUx9l5Z66HDZnunhGZAAzE9wH6a8wUkKpuaX1\nyZMnsrKy3XaA/w7AMPL6bclEYSHn9ZYIIcMli8Zwcrif8H2S+0pRZja9SwPDAjwLAAwjdQ3fN1n+\nd4WD3uIFCKGq2n4ekAF/HtjCAsPI/Lk/XfzQ3NJKIOBlJCXoVQ8YbmALCwxf91IzTXU1hPh5e+8K\n/g6whQWGqdr6htsJD+Iun6J3IWAYgS0sMEw5Hzrl6+UqwMvTe1fw14DAAsPRsYshhksWyc2eQe9C\nwPACgQWGnaDIuxPGC2kskMfefqupra6tp29JYJiAOSwwvNxOeBCVmGqqpx4eex8h1N7eEZuSfvmo\nO73rAsMCBBYYRjJyXqzYtLultQ1LK4znDvsuTwMAfy0ILDCMzJ8r2VyQRu8qwPAFc1gAAIYBgQUA\nYBgQWAAAhgGBBQBgGDDpDujm1OWwW/dS6F0FGEba27s+M6ELCCxABywsLAih2JR0ehcChiNWVtbf\nLYLAAnRw6tSplJQUelcBhiM+Pr6ZM2f+bikEFqADLS0tLS0telcBGE/3gZWXn7Xz8LohLgVQIysP\ndqPA36trYGGPFH5fVjwcnnIMfmfEiBH0LgEAOugaWDY2NtXV1U1NTXSpBlCDlZV1w4YN9K4CADro\n+pgvAAAYtuDEUQAAw4DAAgAwDAgsAADDgMACADAMCCwAAMOAwAIAMAwILAAAw4DAAgAwDAgsAADD\ngMACADAMCCwAAMOAwAIAMAwILAAAw4DAAgAwDAgsAADDgMACADAMCCwAAMOAwAIAMAwILAAAw4DA\nAgAwDAgsAADDgMACADAMCCwAAMOAwAIAMAwILAAAw4DAAgAwDAgsAADDgMACADCM/wGHVEmE/D5I\nBAAAAABJRU5ErkJggg==\n", "prompt_number": 113, "text": [ "" ] } ], "prompt_number": 113 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Control flow statements" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# conditional statements\n", "if True:\n", " print(\"Hello\")\n", "else:\n", " print(\"World\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello\n" ] } ], "prompt_number": 95 }, { "cell_type": "code", "collapsed": false, "input": [ "if False:\n", " print(\"Hello\")\n", "else:\n", " print(\"World\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "World\n" ] } ], "prompt_number": 96 }, { "cell_type": "code", "collapsed": false, "input": [ "cond1, cond2, cond3 = False, True, False\n", "\n", "# No switch statement. Just elif!\n", "\n", "if cond1:\n", " print(\"The world\")\n", "elif cond2:\n", " print(\"is\")\n", "elif cond3:\n", " print(\"not\")\n", "else:\n", " print(\"enough\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "is\n" ] } ], "prompt_number": 97 }, { "cell_type": "code", "collapsed": false, "input": [ "import math\n", "\n", "if 3 <= math.pi < 4:\n", " print('constant PI: {}'.format(math.pi))\n", " print('constant e: {}'.format(math.e))\n", "\n", "# chained comparisons supported by Python, Perl 6 and Mathematica" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "constant PI: 3.141592653589793\n", "constant e: 2.718281828459045\n" ] } ], "prompt_number": 98 }, { "cell_type": "code", "collapsed": false, "input": [ "# for loop over list elements\n", "for i in [42, 3.14159, 666, 256]:\n", " print(i)\n", "\n", "# unary \"break\" and \"continue\" statements are available\n", "# even \"else\" for for-loops ^^" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "42\n", "3.14159\n", "666\n", "256\n" ] } ], "prompt_number": 99 }, { "cell_type": "code", "collapsed": false, "input": [ "# iteration over 0..20\n", "for i in range(20):\n", " print(i)" ], "language": "python", "metadata": {}, "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", "10\n", "11\n", "12\n", "13\n", "14\n", "15\n", "16\n", "17\n", "18\n", "19\n" ] } ], "prompt_number": 100 }, { "cell_type": "code", "collapsed": false, "input": [ "# while loop\n", "transitions = {1: 2, 2: 4, 3: 3, 4: 3}\n", "\n", "visited = set()\n", "current = 1\n", "while current not in visited: # poetic \u00e0 la Shakespeare ;)\n", " visited.add(current)\n", " current = transitions[current]\n", "\n", "print(visited)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{1, 2, 3, 4}\n" ] } ], "prompt_number": 101 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with functions" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# functions = small processing units for repetitive tasks\n", "def my_first_function(first_arg, second_arg):\n", " print(first_arg)\n", " print(second_arg)\n", "\n", "# \"Off-side rule\": code block\n", "# = successive lines of code with same level of indentation\n", "# (please don't mix tabs and spaces; and prefer 4 spaces!)\n", "\n", "# call the custom function\n", "my_first_function(\"Hello\", 0x007)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello\n", "7\n" ] } ], "prompt_number": 102 }, { "cell_type": "code", "collapsed": false, "input": [ "def function_with_return_value(first_arg=3): # default value = 3\n", " return first_arg * 2\n", "\n", "print(function_with_return_value(21))\n", "print(function_with_return_value(1 + 2 + 3 + 4 + 5 + 6))\n", "print(function_with_return_value())" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "42\n", "42\n", "6\n" ] } ], "prompt_number": 103 }, { "cell_type": "code", "collapsed": false, "input": [ "def compute_speed(distance_in_m: float, time_in_sec: int) -> float:\n", " \"\"\"Computing the speed.\n", "\n", " :param distance_in_m: How many meters are we talking about?\n", " :param time_in_sec: In which time span were these meters passed?\n", " :return: Return the speed in km/k\n", " \"\"\"\n", " # \"/\" is float division\n", " # \"//\" is integer division\n", " return (distance_in_m / time_in_sec) * 3.6\n", "\n", "print('{} m/s are {} km/h'.format(42, compute_speed(42, 1)))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "42 m/s are 151.20000000000002 km/h\n" ] } ], "prompt_number": 104 }, { "cell_type": "code", "collapsed": false, "input": [ "def affine(a, b, c):\n", " return a + b * c\n", "\n", "print(affine(1, 2, 3))\n", "print(affine(b=2, a=1, c=3)) # passing by keyword\n", "print(affine(1, 2, c=3))\n", "\n", "print(affine(*[1, 2, 3]))\n", "print(affine(**{'a': 1, 'b': 2, 'c':3}))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "7\n", "7\n", "7\n", "7\n", "7\n" ] } ], "prompt_number": 105 }, { "cell_type": "code", "collapsed": false, "input": [ "# pitfall #2: CPython implementation error: mutable default parameters\n", "def push_one(lst=[]):\n", " lst.append(1)\n", " return lst\n", "\n", "val = push_one()\n", "print(val)\n", "val = push_one()\n", "print(val) # WTH?\n", "\n", "def push_one_fixed(lst=None):\n", " if lst is None:\n", " lst = []\n", " lst.append(1)\n", " return lst\n", "\n", "val = push_one_fixed()\n", "print(val)\n", "val = push_one_fixed()\n", "print(val) # :)\n" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[1]\n", "[1, 1]\n", "[1]\n", "[1]\n" ] } ], "prompt_number": 106 }, { "cell_type": "code", "collapsed": false, "input": [ "# lambdas are anonymous functions (only expressions, use for tiny functions)\n", "add_one = lambda x: x + 1\n", "print(add_one(3))\n", "\n", "values = [('127.0.0.1', 80), ('83.246.69.174', 22), (\"216.239.32.27\", 80)]\n", "print(list(map(lambda x: x[0], values)))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "4\n", "['127.0.0.1', '83.246.69.174', '216.239.32.27']\n" ] } ], "prompt_number": 107 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generator functions" ] }, { "cell_type": "code", "collapsed": false, "input": [ "def generator_function():\n", " yield 0\n", " yield 1\n", " yield 1\n", " yield 2\n", " yield 3\n", " yield 5\n", "\n", "iteration = generator_function()\n", "print(next(iteration))\n", "print(next(iteration))\n", "print(next(iteration))\n", "print(next(iteration))\n", "print(next(iteration))\n", "print(next(iteration))\n", "# print(next(iteration)) # raises StopIteration" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0\n", "1\n", "1\n", "2\n", "3\n", "5\n" ] } ], "prompt_number": 108 }, { "cell_type": "code", "collapsed": false, "input": [ "def infinity(start=0):\n", " current = start\n", " while True:\n", " yield current\n", " current += 1\n", "\n", "for element in infinity():\n", " print(element)\n", " if element >= 20:\n", " break" ], "language": "python", "metadata": {}, "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", "10\n", "11\n", "12\n", "13\n", "14\n", "15\n", "16\n", "17\n", "18\n", "19\n", "20\n" ] } ], "prompt_number": 109 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Classes and OOP" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Class-based object orientation\n", "# - polymorphism\n", "# - inheritance\n", "# - encapsulation\n", "#\n", "# NO protected, private and public!\n", "# Python idiom: \"We are all adults here!\"\n", "# name mangling for members starting with \"__\"\n", "\n", "class Car:\n", " def __init__(self):\n", " self.seats = 4\n", " self.radio = False\n", " def __repr__(self):\n", " return \"A car\"\n", " def speed_up(self, acce):\n", " self.radio += acce\n", "\n", "class Glt14Transporter(Car):\n", " def __init__(self):\n", " super().__init__() # super() refers to all parent objects\n", " self.seats = 7\n", "\n", "vehicle = Glt14Transporter() # call constructor \"__init__\"\n", "print(vehicle.seats) # access public member \"seats\"\n", "print(vehicle.radio) # access public member \"radio\" inherited from \"Car\"\n", "\n", "# Methods with \"__\" at beginning and end are special.\n", "# \"__init__\" is the constructor. \"Car.__repr__\" is called when repr(obj) is called with obj as instance of Car\n", "# \"__len__\" represents the length of an object representing a sequence. Returned by len(obj).\n", "# \"__eq__\" is triggered for comparison with other objects.\n", "#\n", "# And so on and so on\u2026\n", "# See https://docs.python.org/2/reference/datamodel.html" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "7\n", "False\n" ] } ], "prompt_number": 138 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exceptions, I/O, importing, best practices" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Python has exceptions\n", "# SyntaxError, ValueError, TypeError, OSError, \u2026\n", "a = \"this is not an integer\"\n", "int(a)" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "invalid literal for int() with base 10: 'this is not an integer'", "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[1;32m 2\u001b[0m \u001b[0;31m# SyntaxError, ValueError, TypeError, OSError, \u2026\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"this is not an integer\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\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: 'this is not an integer'" ] } ], "prompt_number": 112 }, { "cell_type": "code", "collapsed": false, "input": [ "class ParsingError(Exception):\n", " def __str__(self):\n", " return 'My personal parsing error: ' + self.args[0]\n", "\n", "raise ParsingError(\"Unexpected token '.'\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# Python idiom:\n", "# \"Easier to ask for forgiveness than permission\"\n", "# Do it and if it fails, do it with another approach\n", "\n", "# DO\n", "\n", "try:\n", " # I/O example code: Use with statement to manage file handling in a context\n", " with open(\"/etc/passwd\", \"a\") as fp:\n", " fp.write(\"meisterluk:x:1000:1000:meisterluk,,,:/home/meisterluk:/bin/zsh\\n\")\n", "except IOError:\n", " print(\"Sorry, writing /etc/passwd went wrong\")\n", " raise # reraise\n", "\n", "# DON'T\n", "\n", "import os\n", "\n", "if os.access('/etc/passwd', os.W_OK):\n", " with open(\"/etc/passwd\", \"a\") as fp:\n", " fp.write(\"meisterluk:x:1000:1000:meisterluk,,,:/home/meisterluk:/bin/zsh\\n\")\n", "else:\n", " print(\"Sorry, writing /etc/passwd went wrong\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "try:\n", " 1/0\n", "except (ZeroDivisionError, TypeError) as e:\n", " print(\"ZeroDivisionError or TypeError occured\")\n", " print(e) # e is error object\n", "else:\n", " print(\"No exception was raised. Nice!\")\n", "finally:\n", " print(\"Thanks for all the fish!\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# a simple breadth-first search algorithm as real-world example\n", "import collections\n", "\n", "def bfs(root, get_children, data):\n", " active = collections.deque([root])\n", " collected_data = []\n", " while active:\n", " current = active.popleft()\n", " for child in get_children(current):\n", " active.append(child)\n", " collected_data.append(data(current))\n", " return collected_data\n", "\n", "# tree:\n", "# \u2197 2\n", "# 0 \u2192 1 \u2192 3 \u2192 5 \u2192 6\n", "# \u2198 4 \u2192 \u2197\n", "\n", "tree = [[1], [2,3,4], [], [5], [6], [6], []]\n", "\n", "print(bfs(0, lambda n: tree[n], lambda n: n))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "[0, 1, 2, 3, 4, 5, 6, 6]\n" ] } ], "prompt_number": 111 }, { "cell_type": "code", "collapsed": false, "input": [ "# python uses duck typing\n", "# \"When I see a bird that walks like a duck and swims like\n", "# a duck and quacks like a duck, I call that bird a duck\"\n", "\n", "class ModularArithmetic:\n", " def __init__(self, value, base=42):\n", " self.base = base\n", " self.value = value % base\n", " def __repr__(self):\n", " return str(self.value)\n", " def __eq__(self, other):\n", " return self.value == other\n", " def __add__(self, other):\n", " try:\n", " summed = (self.value + other)\n", " except TypeError:\n", " summed = (self.value + other.value)\n", " return ModularArithmetic(summed, self.base)\n", "\n", "# DO\n", "\n", "def total_sum(elements):\n", " total = ModularArithmetic(0)\n", " for elem in elements:\n", " total = total + elem\n", " return total\n", "\n", "print(total_sum([1, 5, 9, ModularArithmetic(55), ModularArithmetic(155)]))\n", "\n", "# DON'T\n", "\n", "def total_sum(elements):\n", " total = ModularArithmetic(0)\n", " for elem in elements:\n", " if isinstance(elem, ModularArithmetic):\n", " # apply ModularArithmetics-specific addition\n", " summed = total.value + elem.value\n", " total += summed\n", " elif isinstance(elem, int):\n", " # apply int-specific addition\n", " total += elem\n", " return total\n", "\n", "print(total_sum([1, 5, 9, ModularArithmetic(55), ModularArithmetic(155)]))\n", "\n", "# Thus, do not check the type and apply specific operations.\n", "# Just use the object and it should provide the operations itself." ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "15\n", "31\n" ] } ], "prompt_number": 127 }, { "cell_type": "code", "collapsed": false, "input": [ "# Python's import mechanism is pretty complex just like any other.\n", "# Let's consider specific usecases.\n", "\n", "# Import \"module.py\" from working directory\n", "import module\n", "# module.local_variable # access local_variable in module\n", "\n", "# Import \"module\" package by importing folder \"module/\" containing a \"__init__.py\" file\n", "import module\n", "# module.local_variable # access local_variable in module/__init__.py\n", "\n", "# Import \"hashlib\" from the stdlib\n", "# If no file or package is found in working directory, import from any sys.path (stdlib)\n", "import hashlib\n", "\n", "# Packages might be nested.\n", "# \"import os\" does not give you access to \"os.path\"\n", "import os.path # functions of \"os\" and \"os.path\" are available now\n", "\n", "# You can skip the namespace, but using \"from module import class\"\n", "from module import local_variable\n", "\n", "# \"from import\" and \"import\" has semantical differences\n", "# for details see e.g. http://lukas-prokop.at/proj/snippets/py013.html\n", "# Long story, short: I recommend \"import\" syntax\n", "\n", "# Python's stdlib is huge!\n", "# Python idiom: \"Batteries included!\"" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "# summary of idioms / design principles of Python\n", "import this" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 145 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "### Handy tools\n", "\n", "* ``python3 -m http.server``\n", "* ``python3 -m cProfile ``\n", "\n", "### What is python good at\n", "\n", "* Readability of source code (clean and precise).\n", "* Need for a large set of libraries? Use python.\n", "* Fast prototyping / Rapid application development.\n", "* Web development (Django, Flask, \u2026)\n", "* Scientific community (SciPy, NumPy, matplotlib, Scikit-learn, \u2026)\n", "\n", "### What sucks about python?\n", "\n", "* You have to remember a small set of functions, you should use instead of methods.\n", "* Mutable and immutable datatypes are mixed!\n", "* Migration python 2.x to py3k is slow and tedious.\n", "* Python might be slow in some cases. Faster than ruby, slower than javascript. How to cope with that:\n", " * Improve data model / data structures\n", " * Profile your functions and methods. Interface important parts with C?!\n", " * pypy (faster, alternative interpreter) (no py3k)\n", " * Multiprocessing is useless due to Global Interpreter Lock\n", "\n", "### Where to go from here?\n", "\n", "* [Dive into Python3](http://getpython3.com/diveintopython3/) is the nicest python tutorial I know of\n", "* The [official documentation](https://docs.python.org/3.4/) as tutorial and library reference\n", "* [Stackoverflow](http://stackoverflow.com/questions/tagged/python-3.x) for any questions\n", "* IRC channels for informal questions (freenode #python, #python.de)\n", "* [PyCon](https://us.pycon.org/2014/), [SciPy](http://www.scipy.org/), [EuroPython](https://ep2014.europython.eu/en/) conferences to listen to talks\n", "* Read PEPs (Python Enhancement Proposals) to follow the development progress\n", "* [PEP8](http://legacy.python.org/dev/peps/pep-0008/) must be read by any python programmer!\n", "* [getpython3.com](http://getpython3.com/) as tutorial for experienced programmers and py3k migrations\n", "\n", "Here in Graz?\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "Image(filename='pygraz.png')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "png": "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\nAAAAAElFTkSuQmCC\n", "prompt_number": 114, "text": [ "" ] } ], "prompt_number": 114 }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Thanks fellows!

\n", "\n", "

\ud83d\ude01 Keep on hacking! \u2615

" ] } ], "metadata": {} } ] }