From 4797e5d32790dff54f0418f77d5fa0bace5f8df8 Mon Sep 17 00:00:00 2001 From: rasbt Date: Sun, 10 Apr 2016 18:50:40 -0400 Subject: [PATCH] How to Make Mistakes in Python --- other/python_book_reviews.md | 104 +- ...y_differences_between_python_2_and_3.ipynb | 4300 ++++---- tutorials/not_so_obvious_python_stuff.ipynb | 8702 ++++++++--------- tutorials/scope_resolution_legb_rule.ipynb | 2 +- 4 files changed, 6516 insertions(+), 6592 deletions(-) diff --git a/other/python_book_reviews.md b/other/python_book_reviews.md index bc1105f..4b762ea 100644 --- a/other/python_book_reviews.md +++ b/other/python_book_reviews.md @@ -3,14 +3,13 @@ # Python Book Reviews -- [Matplotlib Plotting Cookbook](#Matplotlib-Plotting-Cookbook) -- [Python High Performance Programming](#Python-High-Performance-Programming) -- [Learning Ipython for Interactive Computing and Data Visualization](#Learning-Ipython-for-Interactive-Computing-and-Data-Visualization) -- [The Practice of Computing Using Python (2nd Edition)](#The-Practice-of-Computing-Using-Python-(2nd-Edition)) +- [Matplotlib Plotting Cookbook](#matplotlib-plotting-cookbook) +- [Python High Performance Programming](#python-high-performance-programming) +- [Learning IPython for Interactive Computing and Data Visualization](#learning-ipython-for-interactive-computing-and-data-visualization) +- [The Practice of Computing Using Python (2nd Edition)](#the-practice-of-computing-using-python-(2nd-Edition)) +- [How to Make Mistakes in Python](#how-to-make-mistakes-in-python) -
- **Where are the links?** I decided to **not** post any links to any online shop here - I don't want to advertise anything but merely want to leave my brief thoughts in hope that it might be helpful to one or the other. @@ -18,25 +17,20 @@ I decided to **not** post any links to any online shop here - I don't want to ad **About the rating scale/review scores** -Most popular review sites provide some sort of rating, e.g., 7/10, 90/100, 3 starts out of 5 etc. +Most popular review sites provide some sort of rating, e.g., 7/10, 90/100, 3 stars out of 5 etc. I have to admit that I am not a big fan of those review scores - and you won't find them here. Based on my experience, review scores are just kindling all sorts of arguments, destructive debates, and hate-mails. Let's be honest, every opinion is subjective, and I think that boiling it down to a final score is just an annoyance for everyone. -
+--- +### Matplotlib Plotting Cookbook -
- - - -### Matplotlib Plotting Cookbook -[[back to top](#table-of-contents)] ***by Alexandre Devert*** - -Paperback: 222 pages -Release Date: March 2014 -ISBN: 1849513260 -ISBN 13: 9781849513265 +- Paperback: 222 pages +- Release Date: March 2014 +- ISBN: 1849513260 +- ISBN 13: 9781849513265 +- Publisher: Packt **A good alternative to the official matplotlib documentation** @@ -56,42 +50,41 @@ But to it's defense, my hard copy of the "Gnuplot in Action" is also presented i Not a real point of criticism but more like a suggestion for future editions: as big fan of it, I was actually looking for this section that mentions how to use it in IPython notebooks (%pylab inline vs. matplotlib inline), and maybe also plotly for additional value :) -
+--- - ### Python High Performance Programming -[[back to top](#table-of-contents)] ***by Gabriele Lanaro*** - -Paperback: 108 pages -Release Date: December 2013 -ISBN: 1783288450 -ISBN 13: 9781783288458 + +- Paperback: 108 pages +- Release Date: December 2013 +- ISBN: 1783288450 +- ISBN 13: 9781783288458 +- Publisher: Packt **Really recommended book for Python beginners** A really nice read! It covered 4 important topics: how to profile & benchmark Python code, NumPy, C-extensions via Cython, and parallel programming. However, I found it a little bit too brief on all of the topics, a little bit more depth would have been nice. -Also, I missed a few parts, like general Python tricks for better performance (e.g., in-place operators for mutable types and many many others that I started to create benchmarks for here: https://github.com/rasbt/One-Python-benchmark-per-day) +Also, I missed a few parts, like general Python tricks for better performance (e.g., in-place operators for mutable types and many many others that I started to create benchmarks for here: https://github.com/rasbt/One-Python-benchmark-per-day) And another thing that I think would be worth adding in a future addition would be the JIT (just-in-time) compilers, such as parakeet or Numba, especially since Numexpr was briefly mentioned in the NumPy section. But overall I think it is a very recommended read for Python beginners! -
+--- + +### Learning Ipython for Interactive Computing and Data Visualization + - -###Learning Ipython for Interactive Computing and Data Visualization -[[back to top](#table-of-contents)] - ***by Cyrille Rossant*** - -Paperback: 138 pages -Release Date: April 2013 -ISBN: 1782169938 -ISBN 13: 9781782169932 + +- Paperback: 138 pages +- Release Date: April 2013 +- ISBN: 1782169938 +- ISBN 13: 9781782169932 +- Publisher: Packt @@ -100,23 +93,38 @@ ISBN 13: 9781782169932 It's a brief but good book that provides a good introduction to the IPython environment. I think the high-performance chapter that explained the usage of NumPy among others was a little bit redundant, since it is a general Python topic and is not necessarily specific to IPython. And on the other hand, the chapters on customizing IPython and especially writing own IPython magic extensions were way too brief - when I wrote my own extensions, I needed to look more closely at the IPython extension source code to be able to handle this task. But still, this is a nice book that I would recommend to people who are fairly new to Python and people who want to get a taste of IPython! -
+--- + +### The Practice of Computing Using Python (2nd Edition) - -###The Practice of Computing Using Python (2nd Edition) -[[back to top](#table-of-contents)] ***by William F. Punch and Richard Enbody*** - -Paperback: 792 pages -Release Date: February 25, 2012 -ISBN-10: 013280557X -ISBN-13: 978-0132805575 +- Paperback: 792 pages +- Release Date: February 25, 2012 +- ISBN-10: 013280557X +- ISBN-13: 978-0132805575 +- Publisher: Pearson **A great first Python book** This was actually my first Python book. It is not meant to be a thorough coverage of all the greatest Python features and capabilities, but it provides a great introduction to computing and programming in general by using the Python language. It is maybe a little bit to trivial for programmers who just want to pick up the syntax Python language, but I would really recommend this book as a first introduction to people who have never programmed before - I think that Python is a very nice language to pick up this valuable skill. -I am a big fan of books that contains self-assessments: from short exercises up to bigger project assignments, and this book comes with a huge abundance of valuable material, which is a big bonus point. \ No newline at end of file +I am a big fan of books that contains self-assessments: from short exercises up to bigger project assignments, and this book comes with a huge abundance of valuable material, which is a big bonus point. + + +--- + +### How to Make Mistakes in Python + + +***by Mike Pirnat*** + + +- e-Book: 154 pages +- Release Date: October, 2015 +- Publisher: O'Reilly + + +Although I already have many years of experience with coding in Python, I thought that it couldn't hurt to read through this book -- I got the free copy via O'Reilly, and it's relatively short. Sure, many topics throughout this book are trivial for an experienced Python programmer, but I believe that it's a great summary for someone who just got started with this programming language. Although the author doesn't go into technical depths regarding e.g., pylint, unit testing, etc., I think that his descriptions are sufficient, and a reader can always look at the online documentation of the respective tools. What's more important is that the author gives good reasons WHY we should use/do certain things, and I really like the use of paraphrased examples from real-world use cases. It's a solid book overall! diff --git a/tutorials/key_differences_between_python_2_and_3.ipynb b/tutorials/key_differences_between_python_2_and_3.ipynb index f3e2067..0f74195 100644 --- a/tutorials/key_differences_between_python_2_and_3.ipynb +++ b/tutorials/key_differences_between_python_2_and_3.ipynb @@ -1,2194 +1,2118 @@ { - "metadata": { - "name": "", - "signature": "sha256:1a71ccc70829239143d02cebcb97bec031b45e676ebad340fc04c9bd4a5760bf" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Sebastian Raschka](http://sebastianraschka.com) \n", - "\n", - "last updated 05/27/2014\n", - "\n", - "- [Open in IPython nbviewer](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb?create=1) \n", - "\n", - "- [Link to this IPython notebook on Github](https://github.com/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb) \n", - "\n", - "- [Link to the GitHub repository python_reference](https://github.com/rasbt/python_reference)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "I would be happy to hear your comments and suggestions. \n", - "Please feel free to drop me a note via\n", - "[twitter](https://twitter.com/rasbt), [email](mailto:bluewoodtree@gmail.com), or [google+](https://plus.google.com/118404394130788869227).\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Key differences between Python 2.7.x and Python 3.x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "Many beginning Python users are wondering with which version of Python they should start. My answer to this question is usually something along the lines \"just go with the version your favorite tutorial was written in, and check out the differences later on.\"\n", - "\n", - "But what if you are starting a new project and have the choice to pick? I would say there is currently no \"right\" or \"wrong\" as long as both Python 2.7.x and Python 3.x support the libraries that you are planning to use. However, it is worthwhile to have a look at the major differences between those two most popular versions of Python to avoid common pitfalls when writing the code for either one of them, or if you are planning to port your project." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Sections" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "- [Using the `__future__` module](#future_module)\n", - "\n", - "- [The print function](#The-print-function)\n", - "\n", - "- [Integer division](#Integer-division)\n", - "\n", - "- [Unicode](#Unicode)\n", - "\n", - "- [xrange](#xrange)\n", - "\n", - "- [Raising exceptions](#Raising-exceptions)\n", - "\n", - "- [Handling exceptions](#Handling-exceptions)\n", - "\n", - "- [The next() function and .next() method](#The-next-function-and-next-method)\n", - "\n", - "- [For-loop variables and the global namespace leak](#For-loop-variables-and-the-global-namespace-leak)\n", - "\n", - "- [Comparing unorderable types](#Comparing-unorderable-types)\n", - "\n", - "- [Parsing user inputs via input()](#Parsing-user-inputs-via-input)\n", - "\n", - "- [Returning iterable objects instead of lists](#Returning-iterable-objects-instead-of-lists)\n", - "\n", - "- [More articles about Python 2 and Python 3](#More-articles-about-Python-2-and-Python-3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `__future__` module" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python 3.x introduced some Python 2-incompatible keywords and features that can be imported via the in-built `__future__` module in Python 2. It is recommended to use `__future__` imports it if you are planning Python 3.x support for your code. For example, if we want Python 3.x's integer division behavior in Python 2, we can import it via\n", - "\n", - " from __future__ import division\n", - " \n", - "More features that can be imported from the `__future__` module are listed in the table below:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
featureoptional inmandatory ineffect
nested_scopes2.1.0b12.2PEP 227:\n", - "Statically Nested Scopes
generators2.2.0a12.3PEP 255:\n", - "Simple Generators
division2.2.0a23.0PEP 238:\n", - "Changing the Division Operator
absolute_import2.5.0a13.0PEP 328:\n", - "Imports: Multi-Line and Absolute/Relative
with_statement2.5.0a12.6PEP 343:\n", - "The “with” Statement
print_function2.6.0a23.0PEP 3105:\n", - "Make print a function
unicode_literals2.6.0a23.0PEP 3112:\n", - "Bytes literals in Python 3000
\n", - "
\n", - "
(Source: [https://docs.python.org/2/library/__future__.html](https://docs.python.org/2/library/__future__.html#module-__future__))
" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from platform import python_version" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "The print function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Very trivial, and the change in the print-syntax is probably the most widely known change, but still it is worth mentioning: Python 2's print statement has been replaced by the `print()` function, meaning that we have to wrap the object that we want to print in parantheses. \n", - "\n", - "Python 2 doesn't have a problem with additional parantheses, but in contrast, Python 3 would raise a `SyntaxError` if we called the print function the Python 2-way without the parentheses. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "print 'Hello, World!'\n", - "print('Hello, World!')\n", - "print \"text\", ; print 'print more text on the same line'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "Hello, World!\n", - "Hello, World!\n", - "text print more text on the same line\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "print('Hello, World!')\n", - "\n", - "print(\"some text,\", end=\"\") \n", - "print(' print more text on the same line')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "Hello, World!\n", - "some text, print more text on the same line\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Hello, World!'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print 'Hello, World!'\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note:**\n", - "\n", - "Printing \"Hello, World\" above via Python 2 looked quite \"normal\". However, if we have multiple objects inside the parantheses, we will create a tuple, since `print` is a \"statement\" in Python 2, not a function call." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "print('a', 'b')\n", - "print 'a', 'b'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.7\n", - "('a', 'b')\n", - "a b\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Integer division" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This change is particularly dangerous if you are porting code, or if you are executing Python 3 code in Python 2, since the change in integer-division behavior can often go unnoticed (it doesn't raise a `SyntaxError`). \n", - "So, I still tend to use a `float(3)/2` or `3/2.0` instead of a `3/2` in my Python 3 scripts to save the Python 2 guys some trouble (and vice versa, I recommend a `from __future__ import division` in your Python 2 scripts)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "print '3 / 2 =', 3 / 2\n", - "print '3 // 2 =', 3 // 2\n", - "print '3 / 2.0 =', 3 / 2.0\n", - "print '3 // 2.0 =', 3 // 2.0" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "3 / 2 = 1\n", - "3 // 2 = 1\n", - "3 / 2.0 = 1.5\n", - "3 // 2.0 = 1.0\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "print('3 / 2 =', 3 / 2)\n", - "print('3 // 2 =', 3 // 2)\n", - "print('3 / 2.0 =', 3 / 2.0)\n", - "print('3 // 2.0 =', 3 // 2.0)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "3 / 2 = 1.5\n", - "3 // 2 = 1\n", - "3 / 2.0 = 1.5\n", - "3 // 2.0 = 1.0\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Unicode" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python 2 has ASCII `str()` types, separate `unicode()`, but no `byte` type. \n", - "\n", - "Now, in Python 3, we finally have Unicode (utf-8) `str`ings, and 2 byte classes: `byte` and `bytearray`s." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print type(unicode('this is like a python3 str type'))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print type(b'byte type does not exist')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'they are really' + b' the same'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "they are really the same\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print type(bytearray(b'bytearray oddly does exist though'))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "print('strings are now utf-8 \\u03BCnico\\u0394\u00e9!')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "strings are now utf-8 \u03bcnico\u0394\u00e9!\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version(), end=\"\")\n", - "print(' has', type(b' bytes for storing data'))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1 has \n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('and Python', python_version(), end=\"\")\n", - "print(' also has', type(bytearray(b'bytearrays')))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "and Python 3.4.1 also has \n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "'note that we cannot add a string' + b'bytes for data'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "Can't convert 'bytes' object to str implicitly", - "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[0;32m----> 1\u001b[0;31m \u001b[0;34m'note that we cannot add a string'\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34mb'bytes for data'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: Can't convert 'bytes' object to str implicitly" - ] - } - ], - "prompt_number": 13 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "xrange" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " \n", - "The usage of `xrange()` is very popular in Python 2.x for creating an iterable object, e.g., in a for-loop or list/set-dictionary-comprehension. \n", - "The behavior was quite similar to a generator (i.e., \"lazy evaluation\"), but here the xrange-iterable is not exhaustible - meaning, you could iterate over it infinitely. \n", - "\n", - "\n", - "Thanks to its \"lazy-evaluation\", the advantage of the regular `range()` is that `xrange()` is generally faster if you have to iterate over it only once (e.g., in a for-loop). However, in contrast to 1-time iterations, it is not recommended if you repeat the iteration multiple times, since the generation happens every time from scratch! \n", - "\n", - "In Python 3, the `range()` was implemented like the `xrange()` function so that a dedicated `xrange()` function does not exist anymore (`xrange()` raises a `NameError` in Python 3)." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import timeit\n", - "\n", - "n = 10000\n", - "def test_range(n):\n", - " return for i in range(n):\n", - " pass\n", - " \n", - "def test_xrange(n):\n", - " for i in xrange(n):\n", - " pass " - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "\n", - "print '\\ntiming range()'\n", - "%timeit test_range(n)\n", - "\n", - "print '\\n\\ntiming xrange()'\n", - "%timeit test_xrange(n)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "\n", - "timing range()\n", - "1000 loops, best of 3: 433 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "\n", - "\n", - "timing xrange()\n", - "1000 loops, best of 3: 350 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "\n", - "print('\\ntiming range()')\n", - "%timeit test_range(n)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "\n", - "timing range()\n", - "1000 loops, best of 3: 520 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print(xrange(10))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'xrange' is not defined", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mNameError\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mNameError\u001b[0m: name 'xrange' is not defined" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
\n" - ] - }, - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "The `__contains__` method for `range` objects in Python 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another thing worth mentioning is that `range` got a \"new\" `__contains__` method in Python 3.x (thanks to [Yuchen Ying](https://github.com/yegle), who pointed this out). The `__contains__` method can speedup \"look-ups\" in Python 3.x `range` significantly for integer and Boolean types.\n" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "x = 10000000" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def val_in_range(x, val):\n", - " return val in range(x)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def val_in_xrange(x, val):\n", - " return val in xrange(x)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "assert(val_in_range(x, x/2) == True)\n", - "assert(val_in_range(x, x//2) == True)\n", - "%timeit val_in_range(x, x/2)\n", - "%timeit val_in_range(x, x//2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "1 loops, best of 3: 742 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000000 loops, best of 3: 1.19 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Based on the `timeit` results above, you see that the execution for the \"look up\" was about 60,000 faster when it was of an integer type rather than a float. However, since Python 2.x's `range` or `xrange` doesn't have a `__contains__` method, the \"look-up speed\" wouldn't be that much different for integers or floats:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "assert(val_in_xrange(x, x/2.0) == True)\n", - "assert(val_in_xrange(x, x/2) == True)\n", - "assert(val_in_range(x, x/2) == True)\n", - "assert(val_in_range(x, x//2) == True)\n", - "%timeit val_in_xrange(x, x/2.0)\n", - "%timeit val_in_xrange(x, x/2)\n", - "%timeit val_in_range(x, x/2.0)\n", - "%timeit val_in_range(x, x/2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.7\n", - "1 loops, best of 3: 285 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1 loops, best of 3: 179 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1 loops, best of 3: 658 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1 loops, best of 3: 556 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Below the \"proofs\" that the `__contain__` method wasn't added to Python 2.x yet:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "range.__contains__" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n" - ] - }, - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 8, - "text": [ - "" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "range.__contains__" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.7\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "'builtin_function_or_method' object has no attribute '__contains__'", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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;32mprint\u001b[0m \u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mrange\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__contains__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: 'builtin_function_or_method' object has no attribute '__contains__'" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "xrange.__contains__" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.7\n" - ] - }, - { - "ename": "AttributeError", - "evalue": "type object 'xrange' has no attribute '__contains__'", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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;32mprint\u001b[0m \u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mxrange\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__contains__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: type object 'xrange' has no attribute '__contains__'" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 4, - "metadata": {}, - "source": [ - "Note about the speed differences in Python 2 and 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Some people pointed out the speed difference between Python 3's `range()` and Python2's `xrange()`. Since they are implemented the same way one would expect the same speed. However the difference here just comes from the fact that Python 3 generally tends to run slower than Python 2. " - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def test_while():\n", - " i = 0\n", - " while i < 20000:\n", - " i += 1\n", - " return" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "%timeit test_while()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "100 loops, best of 3: 2.68 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "%timeit test_while()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "1000 loops, best of 3: 1.72 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Raising exceptions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "Where Python 2 accepts both notations, the 'old' and the 'new' syntax, Python 3 chokes (and raises a `SyntaxError` in turn) if we don't enclose the exception argument in parentheses:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "raise IOError, \"file error\"" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "IOError", - "evalue": "file error", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIOError\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[0mIOError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mIOError\u001b[0m: file error" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "raise IOError(\"file error\")" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "IOError", - "evalue": "file error", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIOError\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[0mIOError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mIOError\u001b[0m: file error" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "raise IOError, \"file error\"" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "SyntaxError", - "evalue": "invalid syntax (, line 1)", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m raise IOError, \"file error\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The proper way to raise an exception in Python 3:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "raise IOError(\"file error\")" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n" - ] - }, - { - "ename": "OSError", - "evalue": "file error", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mOSError\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIOError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mOSError\u001b[0m: file error" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Handling exceptions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Also the handling of exceptions has slightly changed in Python 3. In Python 3 we have to use the \"`as`\" keyword now" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "try:\n", - " let_us_cause_a_NameError\n", - "except NameError, err:\n", - " print err, '--> our error message'" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "name 'let_us_cause_a_NameError' is not defined --> our error message\n" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Sebastian Raschka](http://sebastianraschka.com) \n", + "\n", + "last updated 05/27/2014\n", + "\n", + "- [Open in IPython nbviewer](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb?create=1) \n", + "\n", + "- [Link to this IPython notebook on Github](https://github.com/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.ipynb) \n", + "\n", + "- [Link to the GitHub repository python_reference](https://github.com/rasbt/python_reference)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "I would be happy to hear your comments and suggestions. \n", + "Please feel free to drop me a note via\n", + "[twitter](https://twitter.com/rasbt), [email](mailto:bluewoodtree@gmail.com), or [google+](https://plus.google.com/118404394130788869227).\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Key differences between Python 2.7.x and Python 3.x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "Many beginning Python users are wondering with which version of Python they should start. My answer to this question is usually something along the lines \"just go with the version your favorite tutorial was written in, and check out the differences later on.\"\n", + "\n", + "But what if you are starting a new project and have the choice to pick? I would say there is currently no \"right\" or \"wrong\" as long as both Python 2.7.x and Python 3.x support the libraries that you are planning to use. However, it is worthwhile to have a look at the major differences between those two most popular versions of Python to avoid common pitfalls when writing the code for either one of them, or if you are planning to port your project." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sections" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "- [Using the `__future__` module](#future_module)\n", + "\n", + "- [The print function](#The-print-function)\n", + "\n", + "- [Integer division](#Integer-division)\n", + "\n", + "- [Unicode](#Unicode)\n", + "\n", + "- [xrange](#xrange)\n", + "\n", + "- [Raising exceptions](#Raising-exceptions)\n", + "\n", + "- [Handling exceptions](#Handling-exceptions)\n", + "\n", + "- [The next() function and .next() method](#The-next-function-and-next-method)\n", + "\n", + "- [For-loop variables and the global namespace leak](#For-loop-variables-and-the-global-namespace-leak)\n", + "\n", + "- [Comparing unorderable types](#Comparing-unorderable-types)\n", + "\n", + "- [Parsing user inputs via input()](#Parsing-user-inputs-via-input)\n", + "\n", + "- [Returning iterable objects instead of lists](#Returning-iterable-objects-instead-of-lists)\n", + "\n", + "- [More articles about Python 2 and Python 3](#More-articles-about-Python-2-and-Python-3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `__future__` module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python 3.x introduced some Python 2-incompatible keywords and features that can be imported via the in-built `__future__` module in Python 2. It is recommended to use `__future__` imports it if you are planning Python 3.x support for your code. For example, if we want Python 3.x's integer division behavior in Python 2, we can import it via\n", + "\n", + " from __future__ import division\n", + " \n", + "More features that can be imported from the `__future__` module are listed in the table below:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
featureoptional inmandatory ineffect
nested_scopes2.1.0b12.2PEP 227:\n", + "Statically Nested Scopes
generators2.2.0a12.3PEP 255:\n", + "Simple Generators
division2.2.0a23.0PEP 238:\n", + "Changing the Division Operator
absolute_import2.5.0a13.0PEP 328:\n", + "Imports: Multi-Line and Absolute/Relative
with_statement2.5.0a12.6PEP 343:\n", + "The “with” Statement
print_function2.6.0a23.0PEP 3105:\n", + "Make print a function
unicode_literals2.6.0a23.0PEP 3112:\n", + "Bytes literals in Python 3000
\n", + "
\n", + "
(Source: [https://docs.python.org/2/library/__future__.html](https://docs.python.org/2/library/__future__.html#module-__future__))
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from platform import python_version" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The print function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Very trivial, and the change in the print-syntax is probably the most widely known change, but still it is worth mentioning: Python 2's print statement has been replaced by the `print()` function, meaning that we have to wrap the object that we want to print in parantheses. \n", + "\n", + "Python 2 doesn't have a problem with additional parantheses, but in contrast, Python 3 would raise a `SyntaxError` if we called the print function the Python 2-way without the parentheses. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "Hello, World!\n", + "Hello, World!\n", + "text print more text on the same line\n" ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "try:\n", - " let_us_cause_a_NameError\n", - "except NameError as err:\n", - " print(err, '--> our error message')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "name 'let_us_cause_a_NameError' is not defined --> our error message\n" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "The next() function and .next() method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since `next()` (`.next()`) is such a commonly used function (method), this is another syntax change (or rather change in implementation) that is worth mentioning: where you can use both the function and method syntax in Python 2.7.5, the `next()` function is all that remains in Python 3 (calling the `.next()` method raises an `AttributeError`)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "\n", - "my_generator = (letter for letter in 'abcdefg')\n", - "\n", - "next(my_generator)\n", - "my_generator.next()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n" - ] - }, - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 11, - "text": [ - "'b'" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "\n", - "my_generator = (letter for letter in 'abcdefg')\n", - "\n", - "next(my_generator)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n" - ] - }, - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 13, - "text": [ - "'a'" - ] - } - ], - "prompt_number": 13 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_generator.next()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'generator' object has no attribute 'next'", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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[0mmy_generator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m: 'generator' object has no attribute 'next'" - ] - } - ], - "prompt_number": 14 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "For-loop variables and the global namespace leak" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Good news is: In Python 3.x for-loop variables don't leak into the global namespace anymore!\n", - "\n", - "This goes back to a change that was made in Python 3.x and is described in [What\u2019s New In Python 3.0](https://docs.python.org/3/whatsnew/3.0.html) as follows:\n", - "\n", - "\"List comprehensions no longer support the syntactic form `[... for var in item1, item2, ...]`. Use `[... for var in (item1, item2, ...)]` instead. Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a `list()` constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "\n", - "i = 1\n", - "print 'before: i =', i\n", - "\n", - "print 'comprehension: ', [i for i in range(5)]\n", - "\n", - "print 'after: i =', i" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "before: i = 1\n", - "comprehension: [0, 1, 2, 3, 4]\n", - "after: i = 4\n" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "\n", - "i = 1\n", - "print('before: i =', i)\n", - "\n", - "print('comprehension:', [i for i in range(5)])\n", - "\n", - "print('after: i =', i)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "before: i = 1\n", - "comprehension: [0, 1, 2, 3, 4]\n", - "after: i = 1\n" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Comparing unorderable types" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another nice change in Python 3 is that a `TypeError` is raised as warning if we try to compare unorderable types." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version()\n", - "print \"[1, 2] > 'foo' = \", [1, 2] > 'foo'\n", - "print \"(1, 2) > 'foo' = \", (1, 2) > 'foo'\n", - "print \"[1, 2] > (1, 2) = \", [1, 2] > (1, 2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "[1, 2] > 'foo' = False\n", - "(1, 2) > 'foo' = True\n", - "[1, 2] > (1, 2) = False\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "print(\"[1, 2] > 'foo' = \", [1, 2] > 'foo')\n", - "print(\"(1, 2) > 'foo' = \", (1, 2) > 'foo')\n", - "print(\"[1, 2] > (1, 2) = \", [1, 2] > (1, 2))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n" - ] - }, - { - "ename": "TypeError", - "evalue": "unorderable types: list() > str()", - "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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"[1, 2] > 'foo' = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"(1, 2) > 'foo' = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"[1, 2] > (1, 2) = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: unorderable types: list() > str()" - ] - } - ], - "prompt_number": 16 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Parsing user inputs via input()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Fortunately, the `input()` function was fixed in Python 3 so that it always stores the user inputs as `str` objects. In order to avoid the dangerous behavior in Python 2 to read in other types than `strings`, we have to use `raw_input()` instead." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
Python 2.7.6 \n",
-      "[GCC 4.0.1 (Apple Inc. build 5493)] on darwin\n",
-      "Type "help", "copyright", "credits" or "license" for more information.\n",
-      "\n",
-      ">>> my_input = input('enter a number: ')\n",
-      "\n",
-      "enter a number: 123\n",
-      "\n",
-      ">>> type(my_input)\n",
-      "<type 'int'>\n",
-      "\n",
-      ">>> my_input = raw_input('enter a number: ')\n",
-      "\n",
-      "enter a number: 123\n",
-      "\n",
-      ">>> type(my_input)\n",
-      "<type 'str'>\n",
-      "
\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
Python 3.4.1 \n",
-      "[GCC 4.2.1 (Apple Inc. build 5577)] on darwin\n",
-      "Type "help", "copyright", "credits" or "license" for more information.\n",
-      "\n",
-      ">>> my_input = input('enter a number: ')\n",
-      "\n",
-      "enter a number: 123\n",
-      "\n",
-      ">>> type(my_input)\n",
-      "<class 'str'>\n",
-      "
\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Returning iterable objects instead of lists" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we have already seen in the [`xrange`](#xrange) section, some functions and methods return iterable objects in Python 3 now - instead of lists in Python 2. \n", - "\n", - "Since we usually iterate over those only once anyway, I think this change makes a lot of sense to save memory. However, it is also possible - in contrast to generators - to iterate over those multiple times if needed, it is aonly not so efficient.\n", - "\n", - "And for those cases where we really need the `list`-objects, we can simply convert the iterable object into a `list` via the `list()` function." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 2" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print 'Python', python_version() \n", - "\n", - "print range(3) \n", - "print type(range(3))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 2.7.6\n", - "[0, 1, 2]\n", - "\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('Python', python_version())\n", - "\n", - "print(range(3))\n", - "print(type(range(3)))\n", - "print(list(range(3)))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python 3.4.1\n", - "range(0, 3)\n", - "\n", - "[0, 1, 2]\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Some more commonly used functions and methods that don't return lists anymore in Python 3:**\n", - "\n", - "- `zip()`\n", - "\n", - "- `map()`\n", - "\n", - "- `filter()`\n", - "\n", - "- dictionary's `.keys()` method\n", - "\n", - "- dictionary's `.values()` method\n", - "\n", - "- dictionary's `.items()` method\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "More articles about Python 2 and Python 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to the section-overview](#Sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a list of some good articles concerning Python 2 and 3 that I would recommend as a follow-up.\n", - "\n", - "\n", - "**// Porting to Python 3** \n", - "\n", - "- [Should I use Python 2 or Python 3 for my development activity?](https://wiki.python.org/moin/Python2orPython3)\n", - "\n", - "- [What\u2019s New In Python 3.0](https://docs.python.org/3.0/whatsnew/3.0.html)\n", - "\n", - "- [Porting to Python 3](http://python3porting.com/differences.html)\n", - "\n", - "- [Porting Python 2 Code to Python 3](https://docs.python.org/3/howto/pyporting.html) \n", - "\n", - "- [How keep Python 3 moving forward](http://nothingbutsnark.svbtle.com/my-view-on-the-current-state-of-python-3)\n", - "\n", - "**// Pro and anti Python 3**\n", - "\n", - "- [10 awesome features of Python that you can't use because you refuse to upgrade to Python 3](http://asmeurer.github.io/python3-presentation/slides.html#1)\n", - "\n", - "- [Everything you did not want to know about Unicode in Python 3](http://lucumr.pocoo.org/2014/5/12/everything-about-unicode/)\n", - "\n", - "- [Python 3 is killing Python](https://medium.com/@deliciousrobots/5d2ad703365d/)\n", - "\n", - "- [Python 3 can revive Python](https://medium.com/p/2a7af4788b10)\n", - "\n", - "- [Python 3 is fine](http://sealedabstract.com/rants/python-3-is-fine/)\n" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] } ], - "metadata": {} + "source": [ + "print 'Python', python_version()\n", + "print 'Hello, World!'\n", + "print('Hello, World!')\n", + "print \"text\", ; print 'print more text on the same line'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "Hello, World!\n", + "some text, print more text on the same line\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "print('Hello, World!')\n", + "\n", + "print(\"some text,\", end=\"\") \n", + "print(' print more text on the same line')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m print 'Hello, World!'\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "print 'Hello, World!'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:**\n", + "\n", + "Printing \"Hello, World\" above via Python 2 looked quite \"normal\". However, if we have multiple objects inside the parantheses, we will create a tuple, since `print` is a \"statement\" in Python 2, not a function call." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.7\n", + "('a', 'b')\n", + "a b\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "print('a', 'b')\n", + "print 'a', 'b'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Integer division" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This change is particularly dangerous if you are porting code, or if you are executing Python 3 code in Python 2, since the change in integer-division behavior can often go unnoticed (it doesn't raise a `SyntaxError`). \n", + "So, I still tend to use a `float(3)/2` or `3/2.0` instead of a `3/2` in my Python 3 scripts to save the Python 2 guys some trouble (and vice versa, I recommend a `from __future__ import division` in your Python 2 scripts)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "3 / 2 = 1\n", + "3 // 2 = 1\n", + "3 / 2.0 = 1.5\n", + "3 // 2.0 = 1.0\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "print '3 / 2 =', 3 / 2\n", + "print '3 // 2 =', 3 // 2\n", + "print '3 / 2.0 =', 3 / 2.0\n", + "print '3 // 2.0 =', 3 // 2.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "3 / 2 = 1.5\n", + "3 // 2 = 1\n", + "3 / 2.0 = 1.5\n", + "3 // 2.0 = 1.0\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "print('3 / 2 =', 3 / 2)\n", + "print('3 // 2 =', 3 // 2)\n", + "print('3 / 2.0 =', 3 / 2.0)\n", + "print('3 // 2.0 =', 3 // 2.0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unicode" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python 2 has ASCII `str()` types, separate `unicode()`, but no `byte` type. \n", + "\n", + "Now, in Python 3, we finally have Unicode (utf-8) `str`ings, and 2 byte classes: `byte` and `bytearray`s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n" + ] + } + ], + "source": [ + "print 'Python', python_version()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print type(unicode('this is like a python3 str type'))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print type(b'byte type does not exist')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "they are really the same\n" + ] + } + ], + "source": [ + "print 'they are really' + b' the same'" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print type(bytearray(b'bytearray oddly does exist though'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "strings are now utf-8 μnicoΔé!\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "print('strings are now utf-8 \\u03BCnico\\u0394é!')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1 has \n" + ] + } + ], + "source": [ + "print('Python', python_version(), end=\"\")\n", + "print(' has', type(b' bytes for storing data'))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "and Python 3.4.1 also has \n" + ] + } + ], + "source": [ + "print('and Python', python_version(), end=\"\")\n", + "print(' also has', type(bytearray(b'bytearrays')))" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Can't convert 'bytes' object to str implicitly", + "output_type": "error", + "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[0;32m----> 1\u001b[0;31m \u001b[0;34m'note that we cannot add a string'\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34mb'bytes for data'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: Can't convert 'bytes' object to str implicitly" + ] + } + ], + "source": [ + "'note that we cannot add a string' + b'bytes for data'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## xrange" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " \n", + "The usage of `xrange()` is very popular in Python 2.x for creating an iterable object, e.g., in a for-loop or list/set-dictionary-comprehension. \n", + "The behavior was quite similar to a generator (i.e., \"lazy evaluation\"), but here the xrange-iterable is not exhaustible - meaning, you could iterate over it infinitely. \n", + "\n", + "\n", + "Thanks to its \"lazy-evaluation\", the advantage of the regular `range()` is that `xrange()` is generally faster if you have to iterate over it only once (e.g., in a for-loop). However, in contrast to 1-time iterations, it is not recommended if you repeat the iteration multiple times, since the generation happens every time from scratch! \n", + "\n", + "In Python 3, the `range()` was implemented like the `xrange()` function so that a dedicated `xrange()` function does not exist anymore (`xrange()` raises a `NameError` in Python 3)." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import timeit\n", + "\n", + "n = 10000\n", + "def test_range(n):\n", + " return for i in range(n):\n", + " pass\n", + " \n", + "def test_xrange(n):\n", + " for i in xrange(n):\n", + " pass " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "\n", + "timing range()\n", + "1000 loops, best of 3: 433 µs per loop\n", + "\n", + "\n", + "timing xrange()\n", + "1000 loops, best of 3: 350 µs per loop\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "\n", + "print '\\ntiming range()'\n", + "%timeit test_range(n)\n", + "\n", + "print '\\n\\ntiming xrange()'\n", + "%timeit test_xrange(n)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "\n", + "timing range()\n", + "1000 loops, best of 3: 520 µs per loop\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "\n", + "print('\\ntiming range()')\n", + "%timeit test_range(n)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'xrange' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mNameError\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'xrange' is not defined" + ] + } + ], + "source": [ + "print(xrange(10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `__contains__` method for `range` objects in Python 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another thing worth mentioning is that `range` got a \"new\" `__contains__` method in Python 3.x (thanks to [Yuchen Ying](https://github.com/yegle), who pointed this out). The `__contains__` method can speedup \"look-ups\" in Python 3.x `range` significantly for integer and Boolean types.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "x = 10000000" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def val_in_range(x, val):\n", + " return val in range(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def val_in_xrange(x, val):\n", + " return val in xrange(x)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "1 loops, best of 3: 742 ms per loop\n", + "1000000 loops, best of 3: 1.19 µs per loop\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "assert(val_in_range(x, x/2) == True)\n", + "assert(val_in_range(x, x//2) == True)\n", + "%timeit val_in_range(x, x/2)\n", + "%timeit val_in_range(x, x//2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Based on the `timeit` results above, you see that the execution for the \"look up\" was about 60,000 faster when it was of an integer type rather than a float. However, since Python 2.x's `range` or `xrange` doesn't have a `__contains__` method, the \"look-up speed\" wouldn't be that much different for integers or floats:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.7\n", + "1 loops, best of 3: 285 ms per loop\n", + "1 loops, best of 3: 179 ms per loop\n", + "1 loops, best of 3: 658 ms per loop\n", + "1 loops, best of 3: 556 ms per loop\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "assert(val_in_xrange(x, x/2.0) == True)\n", + "assert(val_in_xrange(x, x/2) == True)\n", + "assert(val_in_range(x, x/2) == True)\n", + "assert(val_in_range(x, x//2) == True)\n", + "%timeit val_in_xrange(x, x/2.0)\n", + "%timeit val_in_xrange(x, x/2)\n", + "%timeit val_in_range(x, x/2.0)\n", + "%timeit val_in_range(x, x/2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below the \"proofs\" that the `__contain__` method wasn't added to Python 2.x yet:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print('Python', python_version())\n", + "range.__contains__" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.7\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "'builtin_function_or_method' object has no attribute '__contains__'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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;32mprint\u001b[0m \u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mrange\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__contains__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'builtin_function_or_method' object has no attribute '__contains__'" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "range.__contains__" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.7\n" + ] + }, + { + "ename": "AttributeError", + "evalue": "type object 'xrange' has no attribute '__contains__'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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;32mprint\u001b[0m \u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mxrange\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__contains__\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: type object 'xrange' has no attribute '__contains__'" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "xrange.__contains__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Note about the speed differences in Python 2 and 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some people pointed out the speed difference between Python 3's `range()` and Python2's `xrange()`. Since they are implemented the same way one would expect the same speed. However the difference here just comes from the fact that Python 3 generally tends to run slower than Python 2. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def test_while():\n", + " i = 0\n", + " while i < 20000:\n", + " i += 1\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "100 loops, best of 3: 2.68 ms per loop\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "%timeit test_while()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "1000 loops, best of 3: 1.72 ms per loop\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "%timeit test_while()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Raising exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Where Python 2 accepts both notations, the 'old' and the 'new' syntax, Python 3 chokes (and raises a `SyntaxError` in turn) if we don't enclose the exception argument in parentheses:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n" + ] + } + ], + "source": [ + "print 'Python', python_version()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "IOError", + "evalue": "file error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIOError\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[0mIOError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIOError\u001b[0m: file error" + ] + } + ], + "source": [ + "raise IOError, \"file error\"" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "IOError", + "evalue": "file error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mIOError\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[0mIOError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mIOError\u001b[0m: file error" + ] + } + ], + "source": [ + "raise IOError(\"file error\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n" + ] + } + ], + "source": [ + "print('Python', python_version())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m raise IOError, \"file error\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], + "source": [ + "raise IOError, \"file error\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The proper way to raise an exception in Python 3:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n" + ] + }, + { + "ename": "OSError", + "evalue": "file error", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mOSError\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mIOError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"file error\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mOSError\u001b[0m: file error" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "raise IOError(\"file error\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Handling exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Also the handling of exceptions has slightly changed in Python 3. In Python 3 we have to use the \"`as`\" keyword now" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "name 'let_us_cause_a_NameError' is not defined --> our error message\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "try:\n", + " let_us_cause_a_NameError\n", + "except NameError, err:\n", + " print err, '--> our error message'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "name 'let_us_cause_a_NameError' is not defined --> our error message\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "try:\n", + " let_us_cause_a_NameError\n", + "except NameError as err:\n", + " print(err, '--> our error message')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The next() function and .next() method" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since `next()` (`.next()`) is such a commonly used function (method), this is another syntax change (or rather change in implementation) that is worth mentioning: where you can use both the function and method syntax in Python 2.7.5, the `next()` function is all that remains in Python 3 (calling the `.next()` method raises an `AttributeError`)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n" + ] + }, + { + "data": { + "text/plain": [ + "'b'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print 'Python', python_version()\n", + "\n", + "my_generator = (letter for letter in 'abcdefg')\n", + "\n", + "next(my_generator)\n", + "my_generator.next()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n" + ] + }, + { + "data": { + "text/plain": [ + "'a'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print('Python', python_version())\n", + "\n", + "my_generator = (letter for letter in 'abcdefg')\n", + "\n", + "next(my_generator)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'generator' object has no attribute 'next'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mAttributeError\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[0mmy_generator\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m: 'generator' object has no attribute 'next'" + ] + } + ], + "source": [ + "my_generator.next()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## For-loop variables and the global namespace leak" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Good news is: In Python 3.x for-loop variables don't leak into the global namespace anymore!\n", + "\n", + "This goes back to a change that was made in Python 3.x and is described in [What’s New In Python 3.0](https://docs.python.org/3/whatsnew/3.0.html) as follows:\n", + "\n", + "\"List comprehensions no longer support the syntactic form `[... for var in item1, item2, ...]`. Use `[... for var in (item1, item2, ...)]` instead. Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a `list()` constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "before: i = 1\n", + "comprehension: [0, 1, 2, 3, 4]\n", + "after: i = 4\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "\n", + "i = 1\n", + "print 'before: i =', i\n", + "\n", + "print 'comprehension: ', [i for i in range(5)]\n", + "\n", + "print 'after: i =', i" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "before: i = 1\n", + "comprehension: [0, 1, 2, 3, 4]\n", + "after: i = 1\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "\n", + "i = 1\n", + "print('before: i =', i)\n", + "\n", + "print('comprehension:', [i for i in range(5)])\n", + "\n", + "print('after: i =', i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparing unorderable types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another nice change in Python 3 is that a `TypeError` is raised as warning if we try to compare unorderable types." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "[1, 2] > 'foo' = False\n", + "(1, 2) > 'foo' = True\n", + "[1, 2] > (1, 2) = False\n" + ] + } + ], + "source": [ + "print 'Python', python_version()\n", + "print \"[1, 2] > 'foo' = \", [1, 2] > 'foo'\n", + "print \"(1, 2) > 'foo' = \", (1, 2) > 'foo'\n", + "print \"[1, 2] > (1, 2) = \", [1, 2] > (1, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n" + ] + }, + { + "ename": "TypeError", + "evalue": "unorderable types: list() > str()", + "output_type": "error", + "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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"[1, 2] > 'foo' = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"(1, 2) > 'foo' = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"[1, 2] > (1, 2) = \"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: unorderable types: list() > str()" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "print(\"[1, 2] > 'foo' = \", [1, 2] > 'foo')\n", + "print(\"(1, 2) > 'foo' = \", (1, 2) > 'foo')\n", + "print(\"[1, 2] > (1, 2) = \", [1, 2] > (1, 2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parsing user inputs via input()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fortunately, the `input()` function was fixed in Python 3 so that it always stores the user inputs as `str` objects. In order to avoid the dangerous behavior in Python 2 to read in other types than `strings`, we have to use `raw_input()` instead." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Python 2.7.6 \n",
+    "[GCC 4.0.1 (Apple Inc. build 5493)] on darwin\n",
+    "Type "help", "copyright", "credits" or "license" for more information.\n",
+    "\n",
+    ">>> my_input = input('enter a number: ')\n",
+    "\n",
+    "enter a number: 123\n",
+    "\n",
+    ">>> type(my_input)\n",
+    "<type 'int'>\n",
+    "\n",
+    ">>> my_input = raw_input('enter a number: ')\n",
+    "\n",
+    "enter a number: 123\n",
+    "\n",
+    ">>> type(my_input)\n",
+    "<type 'str'>\n",
+    "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
Python 3.4.1 \n",
+    "[GCC 4.2.1 (Apple Inc. build 5577)] on darwin\n",
+    "Type "help", "copyright", "credits" or "license" for more information.\n",
+    "\n",
+    ">>> my_input = input('enter a number: ')\n",
+    "\n",
+    "enter a number: 123\n",
+    "\n",
+    ">>> type(my_input)\n",
+    "<class 'str'>\n",
+    "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Returning iterable objects instead of lists" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we have already seen in the [`xrange`](#xrange) section, some functions and methods return iterable objects in Python 3 now - instead of lists in Python 2. \n", + "\n", + "Since we usually iterate over those only once anyway, I think this change makes a lot of sense to save memory. However, it is also possible - in contrast to generators - to iterate over those multiple times if needed, it is aonly not so efficient.\n", + "\n", + "And for those cases where we really need the `list`-objects, we can simply convert the iterable object into a `list` via the `list()` function." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 2.7.6\n", + "[0, 1, 2]\n", + "\n" + ] + } + ], + "source": [ + "print 'Python', python_version() \n", + "\n", + "print range(3) \n", + "print type(range(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python 3.4.1\n", + "range(0, 3)\n", + "\n", + "[0, 1, 2]\n" + ] + } + ], + "source": [ + "print('Python', python_version())\n", + "\n", + "print(range(3))\n", + "print(type(range(3)))\n", + "print(list(range(3)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Some more commonly used functions and methods that don't return lists anymore in Python 3:**\n", + "\n", + "- `zip()`\n", + "\n", + "- `map()`\n", + "\n", + "- `filter()`\n", + "\n", + "- dictionary's `.keys()` method\n", + "\n", + "- dictionary's `.values()` method\n", + "\n", + "- dictionary's `.items()` method\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## More articles about Python 2 and Python 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to the section-overview](#Sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is a list of some good articles concerning Python 2 and 3 that I would recommend as a follow-up.\n", + "\n", + "\n", + "**// Porting to Python 3** \n", + "\n", + "- [Should I use Python 2 or Python 3 for my development activity?](https://wiki.python.org/moin/Python2orPython3)\n", + "\n", + "- [What’s New In Python 3.0](https://docs.python.org/3.0/whatsnew/3.0.html)\n", + "\n", + "- [Porting to Python 3](http://python3porting.com/differences.html)\n", + "\n", + "- [Porting Python 2 Code to Python 3](https://docs.python.org/3/howto/pyporting.html) \n", + "\n", + "- [How keep Python 3 moving forward](http://nothingbutsnark.svbtle.com/my-view-on-the-current-state-of-python-3)\n", + "\n", + "**// Pro and anti Python 3**\n", + "\n", + "- [10 awesome features of Python that you can't use because you refuse to upgrade to Python 3](http://asmeurer.github.io/python3-presentation/slides.html#1)\n", + "\n", + "- [Everything you did not want to know about Unicode in Python 3](http://lucumr.pocoo.org/2014/5/12/everything-about-unicode/)\n", + "\n", + "- [Python 3 is killing Python](https://medium.com/@deliciousrobots/5d2ad703365d/)\n", + "\n", + "- [Python 3 can revive Python](https://medium.com/p/2a7af4788b10)\n", + "\n", + "- [Python 3 is fine](http://sealedabstract.com/rants/python-3-is-fine/)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/tutorials/not_so_obvious_python_stuff.ipynb b/tutorials/not_so_obvious_python_stuff.ipynb index a3188b0..15569ba 100644 --- a/tutorials/not_so_obvious_python_stuff.ipynb +++ b/tutorials/not_so_obvious_python_stuff.ipynb @@ -1,4361 +1,4353 @@ { - "metadata": { - "name": "", - "signature": "sha256:5dd675ee714d0dbd00f7be378f1379f4dceaa728c56476124c1bf493d70c569e" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[Sebastian Raschka](http://sebastianraschka.com) \n", - "\n", - "- [Link to this IPython Notebook on GitHub](https://github.com/rasbt/python_reference/blob/master/tutorials/not_so_obvious_python_stuff.ipynb) \n", - "- [Link to the GitHub repository](https://github.com/rasbt/python_reference) \n", - "\n" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext watermark" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%watermark -d -u -v" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Last updated: 16/07/2014 \n", - "\n", - "CPython 3.4.1\n", - "IPython 2.0.0\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[More information](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/ipython_magic/watermark.ipynb) about the `watermark` magic command extension.\n", - "\n", - "([Changelog](#changelog))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# A collection of not-so-obvious Python stuff you should know!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "I am really looking forward to your comments and suggestions to improve and \n", - "extend this little collection! Just send me a quick note \n", - "via Twitter: [@rasbt](https://twitter.com/rasbt) \n", - "or Email: [bluewoodtree@gmail.com](mailto:bluewoodtree@gmail.com)\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Sections\n", - "- [The C3 class resolution algorithm for multiple class inheritance](#c3_class_res)\n", - "\n", - "- [Assignment operators and lists - simple-add vs. add-AND operators](#pm_in_lists)\n", - "\n", - "- [`True` and `False` in the datetime module](#datetime_module)\n", - "\n", - "- [Python reuses objects for small integers - always use \"==\" for equality, \"is\" for identity](#python_small_int)\n", - "\n", - "- [Shallow vs. deep copies if list contains other structures and objects](#shallow_vs_deep)\n", - "\n", - "- [Picking `True` values from logical `and`s and `or`s](#false_true_expressions)\n", - "\n", - "- [Don't use mutable objects as default arguments for functions!](#def_mutable_func)\n", - "\n", - "- [Be aware of the consuming generator](#consuming_generator)\n", - "\n", - "- [`bool` is a subclass of `int`](#bool_int)\n", - "\n", - "- [About lambda-in-closures and-a-loop pitfall](#lambda_closure)\n", - "\n", - "- [Python's LEGB scope resolution and the keywords `global` and `nonlocal`](#python_legb)\n", - "\n", - "- [When mutable contents of immutable tuples aren't so mutable](#immutable_tuple)\n", - "\n", - "- [List comprehensions are fast, but generators are faster!?](#list_generator)\n", - "\n", - "- [Public vs. private class methods and name mangling](#private_class)\n", - "\n", - "- [The consequences of modifying a list when looping through it](#looping_pitfall)\n", - "\n", - "- [Dynamic binding and typos in variable names](#dynamic_binding)\n", - "\n", - "- [List slicing using indexes that are \"out of range](#out_of_range_slicing)\n", - "\n", - "- [Reusing global variable names and UnboundLocalErrors](#unboundlocalerror)\n", - "\n", - "- [Creating copies of mutable objects](#copy_mutable)\n", - "\n", - "- [Key differences between Python 2 and 3](#python_differences)\n", - "\n", - "- [Function annotations - What are those `->`'s in my Python code?](#function_annotation)\n", - "\n", - "- [Abortive statements in `finally` blocks](#finally_blocks)\n", - "\n", - "- [Assigning types to variables as values](#variable_types)\n", - "\n", - "- [Only the first clause of generators is evaluated immediately](#generator_rhs)\n", - "\n", - "- [Keyword argument unpacking syntax - `*args` and `**kwargs`](#splat_op)\n", - "\n", - "- [Metaclasses - What creates a new instance of a class?](#new_instance)\n", - "\n", - "- [Else-clauses: \"conditional else\" and \"completion else\"](#else_clauses)\n", - "\n", - "- [Interning of compile-time constants vs. run-time expressions](#string_interning)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The C3 class resolution algorithm for multiple class inheritance" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we are dealing with multiple inheritance, according to the newer C3 class resolution algorithm, the following applies: \n", - "Assuming that child class C inherits from two parent classes A and B, \"class A should be checked before class B\".\n", - "\n", - "If you want to learn more, please read the [original blog](http://python-history.blogspot.ru/2010/06/method-resolution-order.html) post by Guido van Rossum.\n", - "\n", - "(Original source: [http://gistroll.com/rolls/21/horizontal_assessments/new](http://gistroll.com/rolls/21/horizontal_assessments/new))" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "class A(object):\n", - " def foo(self):\n", - " print(\"class A\")\n", - "\n", - "class B(object):\n", - " def foo(self):\n", - " print(\"class B\")\n", - "\n", - "class C(A, B):\n", - " pass\n", - "\n", - "C().foo()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "class A\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So what actually happened above was that class `C` looked in the scope of the parent class `A` for the method `.foo()` first (and found it)!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I received an email containing a suggestion which uses a more nested example to illustrate Guido van Rossum's point a little bit better:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "class A(object):\n", - " def foo(self):\n", - " print(\"class A\")\n", - "\n", - "class B(A):\n", - " pass\n", - "\n", - "class C(A):\n", - " def foo(self):\n", - " print(\"class C\")\n", - "\n", - "class D(B,C):\n", - " pass\n", - "\n", - "D().foo()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "class C\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here, class `D` searches in `B` first, which in turn inherits from `A` (note that class `C` also inherits from `A`, but has its own `.foo()` method) so that we come up with the search order: `D, B, C, A`. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Assignment operators and lists - simple-add vs. add-AND operators" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python `list`s are mutable objects as we all know. So, if we are using the `+=` operator on `list`s, we extend the `list` by directly modifying the object directly. \n", - "\n", - "However, if we use the assigment via `my_list = my_list + ...`, we create a new list object, which can be demonstrated by the following code:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a_list = []\n", - "print('ID:', id(a_list))\n", - "\n", - "a_list += [1]\n", - "print('ID (+=):', id(a_list))\n", - "\n", - "a_list = a_list + [2]\n", - "print('ID (list = list + ...):', id(a_list))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "ID: 4366496544\n", - "ID (+=): 4366496544\n", - "ID (list = list + ...): 4366495472\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Just for reference, the `.append()` and `.extends()` methods are modifying the `list` object in place, just as expected." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a_list = []\n", - "print(a_list, '\\nID (initial):',id(a_list), '\\n')\n", - "\n", - "a_list.append(1)\n", - "print(a_list, '\\nID (append):',id(a_list), '\\n')\n", - "\n", - "a_list.extend([2])\n", - "print(a_list, '\\nID (extend):',id(a_list))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "[] \n", - "ID (initial): 140704077653128 \n", - "\n", - "[1] \n", - "ID (append): 140704077653128 \n", - "\n", - "[1, 2] \n", - "ID (extend): 140704077653128\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## `True` and `False` in the datetime module\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"It often comes as a big surprise for programmers to find (sometimes by way of a hard-to-reproduce bug) that, unlike any other time value, midnight (i.e. `datetime.time(0,0,0)`) is False. A long discussion on the python-ideas mailing list shows that, while surprising, that behavior is desirable\u2014at least in some quarters.\" \n", - "\n", - "(Original source: [http://lwn.net/SubscriberLink/590299/bf73fe823974acea/](http://lwn.net/SubscriberLink/590299/bf73fe823974acea/))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import datetime\n", - "\n", - "print('\"datetime.time(0,0,0)\" (Midnight) ->', bool(datetime.time(0,0,0)))\n", - "\n", - "print('\"datetime.time(1,0,0)\" (1 am) ->', bool(datetime.time(1,0,0)))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\"datetime.time(0,0,0)\" (Midnight) -> False\n", - "\"datetime.time(1,0,0)\" (1 am) -> True\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Python reuses objects for small integers - use \"==\" for equality, \"is\" for identity\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This oddity occurs, because Python keeps an array of small integer objects (i.e., integers between -5 and 256, [see the doc](https://docs.python.org/2/c-api/int.html#PyInt_FromLong))." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = 1\n", - "b = 1\n", - "print('a is b', bool(a is b))\n", - "True\n", - "\n", - "c = 999\n", - "d = 999\n", - "print('c is d', bool(c is d))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "a is b True\n", - "c is d False\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "(*I received a comment that this is in fact a CPython artefact and **must not necessarily be true** in all implementations of Python!*)\n", - "\n", - "So the take home message is: always use \"==\" for equality, \"is\" for identity!\n", - "\n", - "Here is a [nice article](http://python.net/%7Egoodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables) explaining it by comparing \"boxes\" (C language) with \"name tags\" (Python)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This example demonstrates that this applies indeed for integers in the range in -5 to 256:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('256 is 257-1', 256 is 257-1)\n", - "print('257 is 258-1', 257 is 258 - 1)\n", - "print('-5 is -6+1', -5 is -6+1)\n", - "print('-7 is -6-1', -7 is -6-1)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "256 is 257-1 True\n", - "257 is 258-1 False\n", - "-5 is -6+1 True\n", - "-7 is -6-1 False\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### And to illustrate the test for equality (`==`) vs. identity (`is`):" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = 'hello world!'\n", - "b = 'hello world!'\n", - "print('a is b,', a is b)\n", - "print('a == b,', a == b)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "a is b, False\n", - "a == b, True\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We would think that identity would always imply equality, but this is not always true, as we can see in the next example:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = float('nan')\n", - "print('a is a,', a is a)\n", - "print('a == a,', a == a)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "a is a, True\n", - "a == a, False\n" - ] - } - ], - "prompt_number": 12 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Shallow vs. deep copies if list contains other structures and objects\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Shallow copy**: \n", - "If we use the assignment operator to assign one list to another list, we just create a new name reference to the original list. If we want to create a new list object, we have to make a copy of the original list. This can be done via `a_list[:]` or `a_list.copy()`." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "list1 = [1,2]\n", - "list2 = list1 # reference\n", - "list3 = list1[:] # shallow copy\n", - "list4 = list1.copy() # shallow copy\n", - "\n", - "print('IDs:\\nlist1: {}\\nlist2: {}\\nlist3: {}\\nlist4: {}\\n'\n", - " .format(id(list1), id(list2), id(list3), id(list4)))\n", - "\n", - "list2[0] = 3\n", - "print('list1:', list1)\n", - "\n", - "list3[0] = 4\n", - "list4[1] = 4\n", - "print('list1:', list1)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "IDs:\n", - "list1: 4346366472\n", - "list2: 4346366472\n", - "list3: 4346366408\n", - "list4: 4346366536\n", - "\n", - "list1: [3, 2]\n", - "list1: [3, 2]\n" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Deep copy** \n", - "As we have seen above, a shallow copy works fine if we want to create a new list with contents of the original list which we want to modify independently. \n", - "\n", - "However, if we are dealing with compound objects (e.g., lists that contain other lists, [read here](https://docs.python.org/2/library/copy.html) for more information) it becomes a little trickier.\n", - "\n", - "In the case of compound objects, a shallow copy would create a new compound object, but it would just insert the references to the contained objects into the new compound object. In contrast, a deep copy would go \"deeper\" and create also new objects \n", - "for the objects found in the original compound object. \n", - "If you follow the code, the concept should become more clear:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from copy import deepcopy\n", - "\n", - "list1 = [[1],[2]]\n", - "list2 = list1.copy() # shallow copy\n", - "list3 = deepcopy(list1) # deep copy\n", - "\n", - "print('IDs:\\nlist1: {}\\nlist2: {}\\nlist3: {}\\n'\n", - " .format(id(list1), id(list2), id(list3)))\n", - "\n", - "list2[0][0] = 3\n", - "print('list1:', list1)\n", - "\n", - "list3[0][0] = 5\n", - "print('list1:', list1)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "IDs:\n", - "list1: 4377956296\n", - "list2: 4377961752\n", - "list3: 4377954928\n", - "\n", - "list1: [[3], [2]]\n", - "list1: [[3], [2]]\n" - ] - } - ], - "prompt_number": 25 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Picking `True` values from logical `and`s and `or`s" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Logical `or`:** \n", - "\n", - "`a or b == a if a else b` \n", - "- If both values in `or` expressions are `True`, Python will select the first value (e.g., select `\"a\"` in `\"a\" or \"b\"`), and the second one in `and` expressions. \n", - "This is also called **short-circuiting** - we already know that the logical `or` must be `True` if the first value is `True` and therefore can omit the evaluation of the second value.\n", - "\n", - "**Logical `and`:** \n", - "\n", - "`a and b == b if a else a` \n", - "- If both values in `and` expressions are `True`, Python will select the second value, since for a logical `and`, both values must be true.\n" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "result = (2 or 3) * (5 and 7)\n", - "print('2 * 7 =', result)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "2 * 7 = 14\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Don't use mutable objects as default arguments for functions!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Don't use mutable objects (e.g., dictionaries, lists, sets, etc.) as default arguments for functions! You might expect that a new list is created every time when we call the function without providing an argument for the default parameter, but this is not the case: **Python will create the mutable object (default parameter) the first time the function is defined - not when it is called**, see the following code:\n", - "\n", - "(Original source: [http://docs.python-guide.org/en/latest/writing/gotchas/](http://docs.python-guide.org/en/latest/writing/gotchas/)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def append_to_list(value, def_list=[]):\n", - " def_list.append(value)\n", - " return def_list\n", - "\n", - "my_list = append_to_list(1)\n", - "print(my_list)\n", - "\n", - "my_other_list = append_to_list(2)\n", - "print(my_other_list)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "[1]\n", - "[1, 2]\n" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another good example showing that demonstrates that default arguments are created when the function is created (**and not when it is called!**):" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import time\n", - "def report_arg(my_default=time.time()):\n", - " print(my_default)\n", - "\n", - "report_arg()\n", - "\n", - "time.sleep(5)\n", - "\n", - "report_arg()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1397764090.456688\n", - "1397764090.456688" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Be aware of the consuming generator" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Be aware of what is happening when combining \"`in`\" checks with generators, since they won't evaluate from the beginning once a position is \"consumed\"." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gen = (i for i in range(5))\n", - "print('2 in gen,', 2 in gen)\n", - "print('3 in gen,', 3 in gen)\n", - "print('1 in gen,', 1 in gen) " - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "2 in gen, True\n", - "3 in gen, True\n", - "1 in gen, False\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although this defeats the purpose of an generator (in most cases), we can convert a generator into a list to circumvent the problem. " - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gen = (i for i in range(5))\n", - "a_list = list(gen)\n", - "print('2 in l,', 2 in a_list)\n", - "print('3 in l,', 3 in a_list)\n", - "print('1 in l,', 1 in a_list) " - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "2 in l, True\n", - "3 in l, True\n", - "1 in l, True\n" - ] - } - ], - "prompt_number": 27 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## `bool` is a subclass of `int`\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Chicken or egg? In the history of Python (Python 2.2 to be specific) truth values were implemented via 1 and 0 (similar to the old C). In order to avoid syntax errors in old (but perfectly working) Python code, `bool` was added as a subclass of `int` in Python 2.3.\n", - "\n", - "Original source: [http://www.peterbe.com/plog/bool-is-int](http://www.peterbe.com/plog/bool-is-int)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('isinstance(True, int):', isinstance(True, int))\n", - "print('True + True:', True + True)\n", - "print('3*True + True:', 3*True + True)\n", - "print('3*True - False:', 3*True - False)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "isinstance(True, int): True\n", - "True + True: 2\n", - "3*True + True: 4\n", - "3*True - False: 3\n" - ] - } - ], - "prompt_number": 28 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## About lambda-in-closures-and-a-loop pitfall" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remember the section about the [\"consuming generators\"](consuming_generators)? This example is somewhat related, but the result might still come unexpected. \n", - "\n", - "(Original source: [http://openhome.cc/eGossip/Blog/UnderstandingLambdaClosure3.html](http://openhome.cc/eGossip/Blog/UnderstandingLambdaClosure3.html))\n", - "\n", - "In the first example below, we call a `lambda` function in a list comprehension, and the value `i` will be dereferenced every time we call `lambda` within the scope of the list comprehension. Since the list comprehension has already been constructed and evaluated when we for-loop through the list, the closure-variable will be set to the last value 4." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list = [lambda: i for i in range(5)]\n", - "for l in my_list:\n", - " print(l())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "4\n", - "4\n", - "4\n", - "4\n", - "4\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, by using a generator expression, we can make use of its stepwise evaluation (note that the returned variable still stems from the same closure, but the value changes as we iterate over the generator)." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_gen = (lambda: n for n in range(5))\n", - "for l in my_gen:\n", - " print(l())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n", - "1\n", - "2\n", - "3\n", - "4\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And if you are really keen on using lists, there is a nifty trick that circumvents this problem as a reader nicely pointed out in the comments: We can simply pass the loop variable `i` as a default argument to the lambdas." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list = [lambda x=i: x for i in range(5)]\n", - "for l in my_list:\n", - " print(l())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n", - "1\n", - "2\n", - "3\n", - "4\n" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Python's LEGB scope resolution and the keywords `global` and `nonlocal`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There is nothing particularly surprising about Python's LEGB scope resolution (Local -> Enclosed -> Global -> Built-in), but it is still useful to take a look at some examples!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `global` vs. `local`\n", - "\n", - "According to the LEGB rule, Python will first look for a variable in the local scope. So if we set the variable `x = 1` `local`ly in the function's scope, it won't have an effect on the `global` `x`." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "x = 0\n", - "def in_func():\n", - " x = 1\n", - " print('in_func:', x)\n", - " \n", - "in_func()\n", - "print('global:', x)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "in_func: 1\n", - "global: 0\n" - ] - } - ], - "prompt_number": 31 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want to modify the `global` x via a function, we can simply use the `global` keyword to import the variable into the function's scope:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "x = 0\n", - "def in_func():\n", - " global x\n", - " x = 1\n", - " print('in_func:', x)\n", - " \n", - "in_func()\n", - "print('global:', x)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "in_func: 1\n", - "global: 1\n" - ] - } - ], - "prompt_number": 34 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `local` vs. `enclosed`\n", - "\n", - "Now, let us take a look at `local` vs. `enclosed`. Here, we set the variable `x = 1` in the `outer` function and set `x = 1` in the enclosed function `inner`. Since `inner` looks in the local scope first, it won't modify `outer`'s `x`." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def outer():\n", - " x = 1\n", - " print('outer before:', x)\n", - " def inner():\n", - " x = 2\n", - " print(\"inner:\", x)\n", - " inner()\n", - " print(\"outer after:\", x)\n", - "outer()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "outer before: 1\n", - "inner: 2\n", - "outer after: 1\n" - ] - } - ], - "prompt_number": 36 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is where the `nonlocal` keyword comes in handy - it allows us to modify the `x` variable in the `enclosed` scope:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def outer():\n", - " x = 1\n", - " print('outer before:', x)\n", - " def inner():\n", - " nonlocal x\n", - " x = 2\n", - " print(\"inner:\", x)\n", - " inner()\n", - " print(\"outer after:\", x)\n", - "outer()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "outer before: 1\n", - "inner: 2\n", - "outer after: 2\n" - ] - } - ], - "prompt_number": 35 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## When mutable contents of immutable tuples aren't so mutable" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we all know, tuples are immutable objects in Python, right!? But what happens if they contain mutable objects? \n", - "\n", - "First, let us have a look at the expected behavior: a `TypeError` is raised if we try to modify immutable types in a tuple: " - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "tup = (1,)\n", - "tup[0] += 1" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "'tuple' object does not support item assignment", - "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[0mtup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtup\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" - ] - } - ], - "prompt_number": 41 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### But what if we put a mutable object into the immutable tuple? Well, modification works, but we **also** get a `TypeError` at the same time." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "tup = ([],)\n", - "print('tup before: ', tup)\n", - "tup[0] += [1]" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "tup before: ([],)\n" - ] - }, - { - "ename": "TypeError", - "evalue": "'tuple' object does not support item assignment", - "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[0mtup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'tup before: '\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtup\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mtup\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" - ] - } - ], - "prompt_number": 42 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('tup after: ', tup)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "tup after: ([1],)\n" - ] - } - ], - "prompt_number": 43 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "However, **there are ways** to modify the mutable contents of the tuple without raising the `TypeError`, the solution is the `.extend()` method, or alternatively `.append()` (for lists):" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "tup = ([],)\n", - "print('tup before: ', tup)\n", - "tup[0].extend([1])\n", - "print('tup after: ', tup)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "tup before: ([],)\n", - "tup after: ([1],)\n" - ] - } - ], - "prompt_number": 44 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "tup = ([],)\n", - "print('tup before: ', tup)\n", - "tup[0].append(1)\n", - "print('tup after: ', tup)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "tup before: ([],)\n", - "tup after: ([1],)\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Explanation\n", - "\n", - "**A. Jesse Jiryu Davis** has a nice explanation for this phenomenon (Original source: [http://emptysqua.re/blog/python-increment-is-weird-part-ii/](http://emptysqua.re/blog/python-increment-is-weird-part-ii/))\n", - "\n", - "If we try to extend the list via `+=` *\"then the statement executes `STORE_SUBSCR`, which calls the C function `PyObject_SetItem`, which checks if the object supports item assignment. In our case the object is a tuple, so `PyObject_SetItem` throws the `TypeError`. Mystery solved.\"*" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### One more note about the `immutable` status of tuples. Tuples are famous for being immutable. However, how comes that this code works?" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_tup = (1,)\n", - "my_tup += (4,)\n", - "my_tup = my_tup + (5,)\n", - "print(my_tup)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "(1, 4, 5)\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What happens \"behind\" the curtains is that the tuple is not modified, but every time a new object is generated, which will inherit the old \"name tag\":" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_tup = (1,)\n", - "print(id(my_tup))\n", - "my_tup += (4,)\n", - "print(id(my_tup))\n", - "my_tup = my_tup + (5,)\n", - "print(id(my_tup))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "4337381840\n", - "4357415496\n", - "4357289952\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## List comprehensions are fast, but generators are faster!?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\"List comprehensions are fast, but generators are faster!?\" - No, not really (or significantly, see the benchmarks below). So what's the reason to prefer one over the other?\n", - "- use lists if you want to use the plethora of list methods \n", - "- use generators when you are dealing with huge collections to avoid memory issues" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import timeit\n", - "\n", - "def plainlist(n=100000):\n", - " my_list = []\n", - " for i in range(n):\n", - " if i % 5 == 0:\n", - " my_list.append(i)\n", - " return my_list\n", - "\n", - "def listcompr(n=100000):\n", - " my_list = [i for i in range(n) if i % 5 == 0]\n", - " return my_list\n", - "\n", - "def generator(n=100000):\n", - " my_gen = (i for i in range(n) if i % 5 == 0)\n", - " return my_gen\n", - "\n", - "def generator_yield(n=100000):\n", - " for i in range(n):\n", - " if i % 5 == 0:\n", - " yield i" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### To be fair to the list, let us exhaust the generators:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def test_plainlist(plain_list):\n", - " for i in plain_list():\n", - " pass\n", - "\n", - "def test_listcompr(listcompr):\n", - " for i in listcompr():\n", - " pass\n", - "\n", - "def test_generator(generator):\n", - " for i in generator():\n", - " pass\n", - "\n", - "def test_generator_yield(generator_yield):\n", - " for i in generator_yield():\n", - " pass\n", - "\n", - "print('plain_list: ', end = '')\n", - "%timeit test_plainlist(plainlist)\n", - "print('\\nlistcompr: ', end = '')\n", - "%timeit test_listcompr(listcompr)\n", - "print('\\ngenerator: ', end = '')\n", - "%timeit test_generator(generator)\n", - "print('\\ngenerator_yield: ', end = '')\n", - "%timeit test_generator_yield(generator_yield)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "plain_list: 10 loops, best of 3: 22.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "\n", - "listcompr: 10 loops, best of 3: 20.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "\n", - "generator: 10 loops, best of 3: 22 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "\n", - "generator_yield: 10 loops, best of 3: 21.9 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 13 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Public vs. private class methods and name mangling\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Who has not stumbled across this quote \"we are all consenting adults here\" in the Python community, yet? Unlike in other languages like C++ (sorry, there are many more, but that's one I am most familiar with), we can't really protect class methods from being used outside the class (i.e., by the API user). \n", - "All we can do is to indicate methods as private to make clear that they are better not used outside the class, but it is really up to the class user, since \"we are all consenting adults here\"! \n", - "So, when we want to mark a class method as private, we can put a single underscore in front of it. \n", - "If we additionally want to avoid name clashes with other classes that might use the same method names, we can prefix the name with a double-underscore to invoke the name mangling.\n", - "\n", - "This doesn't prevent the class user to access this class member though, but he has to know the trick and also knows that it his own risk...\n", - "\n", - "Let the following example illustrate what I mean:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "class my_class():\n", - " def public_method(self):\n", - " print('Hello public world!')\n", - " def __private_method(self):\n", - " print('Hello private world!')\n", - " def call_private_method_in_class(self):\n", - " self.__private_method()\n", - " \n", - "my_instance = my_class()\n", - "\n", - "my_instance.public_method()\n", - "my_instance._my_class__private_method()\n", - "my_instance.call_private_method_in_class()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Hello public world!\n", - "Hello private world!\n", - "Hello private world!\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The consequences of modifying a list when looping through it" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It can be really dangerous to modify a list when iterating through it - this is a very common pitfall that can cause unintended behavior! \n", - "Look at the following examples, and for a fun exercise: try to figure out what is going on before you skip to the solution!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = [1, 2, 3, 4, 5]\n", - "for i in a:\n", - " if not i % 2:\n", - " a.remove(i)\n", - "print(a)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "[1, 3, 5]\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "b = [2, 4, 5, 6]\n", - "for i in b:\n", - " if not i % 2:\n", - " b.remove(i)\n", - "print(b)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "[4, 5]\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "**The solution** is that we are iterating through the list index by index, and if we remove one of the items in-between, we inevitably mess around with the indexing, look at the following example, and it will become clear:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "b = [2, 4, 5, 6]\n", - "for index, item in enumerate(b):\n", - " print(index, item)\n", - " if not item % 2:\n", - " b.remove(item)\n", - "print(b)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0 2\n", - "1 5\n", - "2 6\n", - "[4, 5]\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dynamic binding and typos in variable names\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Be careful, dynamic binding is convenient, but can also quickly become dangerous!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('first list:')\n", - "for i in range(3):\n", - " print(i)\n", - " \n", - "print('\\nsecond list:')\n", - "for j in range(3):\n", - " print(i) # I (intentionally) made typo here!" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "first list:\n", - "0\n", - "1\n", - "2\n", - "\n", - "second list:\n", - "2\n", - "2\n", - "2\n" - ] - } - ], - "prompt_number": 14 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## List slicing using indexes that are \"out of range\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we have all encountered it 1 (x10000) time(s) in our live, the infamous `IndexError`:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list = [1, 2, 3, 4, 5]\n", - "print(my_list[5])" - ], - "language": "python", - "metadata": {}, - "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[0mmy_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\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;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmy_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\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": 15 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But suprisingly, it is not raised when we are doing list slicing, which can be a really pain for debugging:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list = [1, 2, 3, 4, 5]\n", - "print(my_list[5:])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "[]\n" - ] - } - ], - "prompt_number": 16 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Reusing global variable names and `UnboundLocalErrors`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Usually, it is no problem to access global variables in the local scope of a function:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def my_func():\n", - " print(var)\n", - "\n", - "var = 'global'\n", - "my_func()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "global\n" - ] - } - ], - "prompt_number": 37 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And is also no problem to use the same variable name in the local scope without affecting the local counterpart: " - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def my_func():\n", - " var = 'locally changed'\n", - "\n", - "var = 'global'\n", - "my_func()\n", - "print(var)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "global\n" - ] - } - ], - "prompt_number": 38 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But we have to be careful if we use a variable name that occurs in the global scope, and we want to access it in the local function scope if we want to reuse this name:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def my_func():\n", - " print(var) # want to access global variable\n", - " var = 'locally changed' # but Python thinks we forgot to define the local variable!\n", - " \n", - "var = 'global'\n", - "my_func()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "ename": "UnboundLocalError", - "evalue": "local variable 'var' referenced before assignment", - "output_type": "pyerr", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mUnboundLocalError\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;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'global'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mmy_func\u001b[0m\u001b[0;34m(\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;36mmy_func\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmy_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# want to access global variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'locally changed'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'global'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'var' referenced before assignment" - ] - } - ], - "prompt_number": 40 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In this case, we have to use the `global` keyword!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def my_func():\n", - " global var\n", - " print(var) # want to access global variable\n", - " var = 'locally changed' # changes the gobal variable\n", - "\n", - "var = 'global'\n", - "\n", - "my_func()\n", - "print(var)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "global\n", - "locally changed\n" - ] - } - ], - "prompt_number": 43 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating copies of mutable objects\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's assume a scenario where we want to duplicate sub`list`s of values stored in another list. If we want to create independent sub`list` object, using the arithmetic multiplication operator could lead to rather unexpected (or undesired) results:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list1 = [[1, 2, 3]] * 2\n", - "\n", - "print('initially ---> ', my_list1)\n", - "\n", - "# modify the 1st element of the 2nd sublist\n", - "my_list1[1][0] = 'a'\n", - "print(\"after my_list1[1][0] = 'a' ---> \", my_list1)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "initially ---> [[1, 2, 3], [1, 2, 3]]\n", - "after my_list1[1][0] = 'a' ---> [['a', 2, 3], ['a', 2, 3]]\n" - ] - } - ], - "prompt_number": 24 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "In this case, we should better create \"new\" objects:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "my_list2 = [[1, 2, 3] for i in range(2)]\n", - "\n", - "print('initially: ---> ', my_list2)\n", - "\n", - "# modify the 1st element of the 2nd sublist\n", - "my_list2[1][0] = 'a'\n", - "print(\"after my_list2[1][0] = 'a': ---> \", my_list2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "initially: ---> [[1, 2, 3], [1, 2, 3]]\n", - "after my_list2[1][0] = 'a': ---> [[1, 2, 3], ['a', 2, 3]]\n" - ] - } - ], - "prompt_number": 25 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "And here is the proof:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "for a,b in zip(my_list1, my_list2):\n", - " print('id my_list1: {}, id my_list2: {}'.format(id(a), id(b)))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "id my_list1: 4350764680, id my_list2: 4350766472\n", - "id my_list1: 4350764680, id my_list2: 4350766664\n" - ] - } - ], - "prompt_number": 26 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Key differences between Python 2 and 3\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There are some good articles already that are summarizing the differences between Python 2 and 3, e.g., \n", - "- [https://wiki.python.org/moin/Python2orPython3](https://wiki.python.org/moin/Python2orPython3)\n", - "- [https://docs.python.org/3.0/whatsnew/3.0.html](https://docs.python.org/3.0/whatsnew/3.0.html)\n", - "- [http://python3porting.com/differences.html](http://python3porting.com/differences.html)\n", - "- [https://docs.python.org/3/howto/pyporting.html](https://docs.python.org/3/howto/pyporting.html) \n", - "etc.\n", - "\n", - "But it might be still worthwhile, especially for Python newcomers, to take a look at some of those!\n", - "(Note: the the code was executed in Python 3.4.0 and Python 2.7.5 and copied from interactive shell sessions.)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Overview - Key differences between Python 2 and 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "- [Unicode](#unicode)\n", - "- [The print statement](#print)\n", - "- [Integer division](#integer_div)\n", - "- [xrange()](#xrange)\n", - "- [Raising exceptions](#raising_exceptions)\n", - "- [Handling exceptions](#handling_exceptions)\n", - "- [next() function and .next() method](#next_next)\n", - "- [Loop variables and leaking into the global scope](#loop_leak)\n", - "- [Comparing unorderable types](#compare_unorder)\n", - "\n", - "
\n", - "
\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unicode..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "####- Python 2: \n", - "We have ASCII `str()` types, separate `unicode()`, but no `byte` type\n", - "####- Python 3: \n", - "Now, we finally have Unicode (utf-8) `str`ings, and 2 byte classes: `byte` and `bytearray`s" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "#############\n", - "# Python 2\n", - "#############\n", - "\n", - ">>> type(unicode('is like a python3 str()'))\n", - "\n", - "\n", - ">>> type(b'byte type does not exist')\n", - "\n", - "\n", - ">>> 'they are really' + b' the same'\n", - "'they are really the same'\n", - "\n", - ">>> type(bytearray(b'bytearray oddly does exist though'))\n", - "\n", - "\n", - "#############\n", - "# Python 3\n", - "#############\n", - "\n", - ">>> print('strings are now utf-8 \\u03BCnico\\u0394\u00e9!')\n", - "strings are now utf-8 \u03bcnico\u0394\u00e9!\n", - "\n", - "\n", - ">>> type(b' and we have byte types for storing data')\n", - "\n", - "\n", - ">>> type(bytearray(b'but also bytearrays for those who prefer them over strings'))\n", - "\n", - "\n", - ">>> 'string' + b'bytes for data'\n", - "Traceback (most recent call last):s\n", - " File \"\", line 1, in \n", - "TypeError: Can't convert 'bytes' object to str implicitly" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The print statement" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Very trivial, but this change makes sense, Python 3 now only accepts `print`s with proper parentheses - just like the other function calls ..." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> print 'Hello, World!'\n", - "Hello, World!\n", - ">>> print('Hello, World!')\n", - "Hello, World!\n", - "\n", - "# Python 3\n", - ">>> print('Hello, World!')\n", - "Hello, World!\n", - ">>> print 'Hello, World!'\n", - " File \"\", line 1\n", - " print 'Hello, World!'\n", - " ^\n", - "SyntaxError: invalid syntax" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And if we want to print the output of 2 consecutive print functions on the same line, you would use a comma in Python 2, and a `end=\"\"` in Python 3:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> print \"line 1\", ; print 'same line'\n", - "line 1 same line\n", - "\n", - "# Python 3\n", - ">>> print(\"line 1\", end=\"\") ; print (\" same line\")\n", - "line 1 same line" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Integer division" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is a pretty dangerous thing if you are porting code, or executing Python 3 code in Python 2 since the change in integer-division behavior can often go unnoticed. \n", - "So, I still tend to use a `float(3)/2` or `3/2.0` instead of a `3/2` in my Python 3 scripts to save the Python 2 guys some trouble ... (PS: and vice versa, you can `from __future__ import division` in your Python 2 scripts)." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> 3 / 2\n", - "1\n", - ">>> 3 // 2\n", - "1\n", - ">>> 3 / 2.0\n", - "1.5\n", - ">>> 3 // 2.0\n", - "1.0\n", - "\n", - "# Python 3\n", - ">>> 3 / 2\n", - "1.5\n", - ">>> 3 // 2\n", - "1\n", - ">>> 3 / 2.0\n", - "1.5\n", - ">>> 3 // 2.0\n", - "1.0" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###`xrange()` " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - " \n", - "`xrange()` was pretty popular in Python 2.x if you wanted to create an iterable object. The behavior was quite similar to a generator ('lazy evaluation'), but you could iterate over it infinitely. The advantage was that it was generally faster than `range()` (e.g., in a for-loop) - not if you had to iterate over the list multiple times, since the generation happens every time from scratch! \n", - "In Python 3, the `range()` was implemented like the `xrange()` function so that a dedicated `xrange()` function does not exist anymore." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - "> python -m timeit 'for i in range(1000000):' ' pass'\n", - "10 loops, best of 3: 66 msec per loop\n", - "\n", - " > python -m timeit 'for i in xrange(1000000):' ' pass'\n", - "10 loops, best of 3: 27.8 msec per loop\n", - "\n", - "# Python 3\n", - "> python3 -m timeit 'for i in range(1000000):' ' pass'\n", - "10 loops, best of 3: 51.1 msec per loop\n", - "\n", - "> python3 -m timeit 'for i in xrange(1000000):' ' pass'\n", - "Traceback (most recent call last):\n", - " File \"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py\", line 292, in main\n", - " x = t.timeit(number)\n", - " File \"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py\", line 178, in timeit\n", - " timing = self.inner(it, self.timer)\n", - " File \"\", line 6, in inner\n", - " for i in xrange(1000000):\n", - "NameError: name 'xrange' is not defined" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Raising exceptions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "Where Python 2 accepts both notations, the 'old' and the 'new' way, Python 3 chokes (and raises a `SyntaxError` in turn) if we don't enclose the exception argument in parentheses:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> raise IOError, \"file error\"\n", - "Traceback (most recent call last):\n", - " File \"\", line 1, in \n", - "IOError: file error\n", - ">>> raise IOError(\"file error\")\n", - "Traceback (most recent call last):\n", - " File \"\", line 1, in \n", - "IOError: file error\n", - "\n", - " \n", - "# Python 3 \n", - ">>> raise IOError, \"file error\"\n", - " File \"\", line 1\n", - " raise IOError, \"file error\"\n", - " ^\n", - "SyntaxError: invalid syntax\n", - ">>> raise IOError(\"file error\")\n", - "Traceback (most recent call last):\n", - " File \"\", line 1, in \n", - "OSError: file error" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Handling exceptions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "Also the handling of exceptions has slightly changed in Python 3. Now, we have to use the `as` keyword!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> try:\n", - "... blabla\n", - "... except NameError, err:\n", - "... print err, '--> our error msg'\n", - "... \n", - "name 'blabla' is not defined --> our error msg\n", - "\n", - "# Python 3\n", - ">>> try:\n", - "... blabla\n", - "... except NameError as err:\n", - "... print(err, '--> our error msg')\n", - "... \n", - "name 'blabla' is not defined --> our error msg" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "\n", - "
\n", - "
" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### The `next()` function and `.next()` method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "Where you can use both function and method in Python 2.7.5, the `next()` function is all that remain in Python 3!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python 2\n", - ">>> my_generator = (letter for letter in 'abcdefg')\n", - ">>> my_generator.next()\n", - "'a'\n", - ">>> next(my_generator)\n", - "'b'\n", - "\n", - "# Python 3\n", - ">>> my_generator = (letter for letter in 'abcdefg')\n", - ">>> next(my_generator)\n", - "'a'\n", - ">>> my_generator.next()\n", - "Traceback (most recent call last):\n", - " File \"\", line 1, in \n", - "AttributeError: 'generator' object has no attribute 'next'" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### In Python 3.x for-loop variables don't leak into the global namespace anymore" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This goes back to a change that was made in Python 3.x and is described in [What\u2019s New In Python 3.0](https://docs.python.org/3/whatsnew/3.0.html) as follows:\n", - "\n", - "\"List comprehensions no longer support the syntactic form `[... for var in item1, item2, ...]`. Use `[... for var in (item1, item2, ...)]` instead. Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a `list()` constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.\"" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from platform import python_version\n", - "print('This code cell was executed in Python', python_version())\n", - "\n", - "i = 1\n", - "print([i for i in range(5)])\n", - "print(i, '-> i in global')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "This code cell was executed in Python 3.3.5\n", - "[0, 1, 2, 3, 4]\n", - "1 -> i in global\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from platform import python_version\n", - "print 'This code cell was executed in Python', python_version()\n", - "\n", - "i = 1\n", - "print [i for i in range(5)]\n", - "print i, '-> i in global' " - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "This code cell was executed in Python 2.7.6\n", - "[0, 1, 2, 3, 4]\n", - "4 -> i in global\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Python 3.x prevents us from comparing unorderable types" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to Python 2.x vs 3.x overview](#py23_overview)]" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from platform import python_version\n", - "print 'This code cell was executed in Python', python_version()\n", - "\n", - "print [1, 2] > 'foo'\n", - "print (1, 2) > 'foo'\n", - "print [1, 2] > (1, 2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "This code cell was executed in Python 2.7.6\n", - "False\n", - "True\n", - "False\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from platform import python_version\n", - "print('This code cell was executed in Python', python_version())\n", - "\n", - "print([1, 2] > 'foo')\n", - "print((1, 2) > 'foo')\n", - "print([1, 2] > (1, 2))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "This code cell was executed in Python 3.3.5\n" - ] - }, - { - "ename": "TypeError", - "evalue": "unorderable types: list() > str()", - "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 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'This code cell was executed in Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mTypeError\u001b[0m: unorderable types: list() > str()" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## Function annotations - What are those `->`'s in my Python code?\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Have you ever seen any Python code that used colons inside the parantheses of a function definition?" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def foo1(x: 'insert x here', y: 'insert x^2 here'):\n", - " print('Hello, World')\n", - " return" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And what about the fancy arrow here?" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def foo2(x, y) -> 'Hi!':\n", - " print('Hello, World')\n", - " return" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Q: Is this valid Python syntax? \n", - "A: Yes!\n", - " \n", - " \n", - "Q: So, what happens if I *just call* the function? \n", - "A: Nothing!\n", - " \n", - "Here is the proof!" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "foo1(1,2)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Hello, World\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "foo2(1,2) " - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Hello, World\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**So, those are function annotations ... ** \n", - "- the colon for the function parameters \n", - "- the arrow for the return value \n", - "\n", - "You probably will never make use of them (or at least very rarely). Usually, we write good function documentations below the function as a docstring - or at least this is how I would do it (okay this case is a little bit extreme, I have to admit):" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def is_palindrome(a):\n", - " \"\"\"\n", - " Case-and punctuation insensitive check if a string is a palindrom.\n", - " \n", - " Keyword arguments:\n", - " a (str): The string to be checked if it is a palindrome.\n", - " \n", - " Returns `True` if input string is a palindrome, else False.\n", - " \n", - " \"\"\"\n", - " stripped_str = [l for l in my_str.lower() if l.isalpha()]\n", - " return stripped_str == stripped_str[::-1]\n", - " " - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, function annotations can be useful to indicate that work is still in progress in some cases. But they are optional and I see them very very rarely.\n", - "\n", - "As it is stated in [PEP3107](http://legacy.python.org/dev/peps/pep-3107/#fundamentals-of-function-annotations):\n", - "\n", - "1. Function annotations, both for parameters and return values, are completely optional.\n", - "\n", - "2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The nice thing about function annotations is their `__annotations__` attribute, which is dictionary of all the parameters and/or the `return` value you annotated." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "foo1.__annotations__" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 17, - "text": [ - "{'y': 'insert x^2 here', 'x': 'insert x here'}" - ] - } - ], - "prompt_number": 17 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "foo2.__annotations__" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 18, - "text": [ - "{'return': 'Hi!'}" - ] - } - ], - "prompt_number": 18 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**When are they useful?**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Function annotations can be useful for a couple of things \n", - "- Documentation in general\n", - "- pre-condition testing\n", - "- [type checking](http://legacy.python.org/dev/peps/pep-0362/#annotation-checker)\n", - " \n", - "..." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Abortive statements in `finally` blocks" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python's `try-except-finally` blocks are very handy for catching and handling errors. The `finally` block is always executed whether an `exception` has been raised or not as illustrated in the following example." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def try_finally1():\n", - " try:\n", - " print('in try:')\n", - " print('do some stuff')\n", - " float('abc')\n", - " except ValueError:\n", - " print('an error occurred')\n", - " else:\n", - " print('no error occurred')\n", - " finally:\n", - " print('always execute finally')\n", - " \n", - "try_finally1()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "in try:\n", - "do some stuff\n", - "an error occurred\n", - "always execute finally\n" - ] - } - ], - "prompt_number": 24 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "But can you also guess what will be printed in the next code cell?" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def try_finally2():\n", - " try:\n", - " print(\"do some stuff in try block\")\n", - " return \"return from try block\"\n", - " finally:\n", - " print(\"do some stuff in finally block\")\n", - " return \"always execute finally\"\n", - " \n", - "print(try_finally2())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "do some stuff in try block\n", - "do some stuff in finally block\n", - "always execute finally\n" - ] - } - ], - "prompt_number": 21 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "Here, the abortive `return` statement in the `finally` block simply overrules the `return` in the `try` block, since **`finally` is guaranteed to always be executed.** So, be careful using abortive statements in `finally` blocks!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#Assigning types to variables as values" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I am not yet sure in which context this can be useful, but it is a nice fun fact to know that we can assign types as values to variables." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "a_var = str\n", - "a_var(123)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "'123'" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from random import choice\n", - "\n", - "a, b, c = float, int, str\n", - "for i in range(5):\n", - " j = choice([a,b,c])(i)\n", - " print(j, type(j))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0 \n", - "1 \n", - "2.0 \n", - "3 \n", - "4 \n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Only the first clause of generators is evaluated immediately" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The main reason why we love to use generators in certain cases (i.e., when we are dealing with large numbers of computations) is that it only computes the next value when it is needed, which is also known as \"lazy\" evaluation.\n", - "However, the first clause of an generator is already checked upon it's creation, as the following example demonstrates:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gen_fails = (i for i in 1/0)" - ], - "language": "python", - "metadata": {}, - "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[0mgen_fails\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m1\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[0m", - "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" - ] - } - ], - "prompt_number": 18 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Certainly, this is a nice feature, since it notifies us about syntax erros immediately. However, this is (unfortunately) not the case if we have multiple cases in our generator." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "gen_succeeds = (i for i in range(5) for j in 1/0)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 19 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print('But obviously fails when we iterate ...')\n", - "for i in gen_succeeds:\n", - " print(i)" - ], - "language": "python", - "metadata": {}, - "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[1;32m 1\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'But obviously fails when we iterate ...'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgen_succeeds\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mgen_succeeds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m1\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[0m", - "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "But obviously fails when we iterate ...\n" - ] - } - ], - "prompt_number": 20 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "##Keyword argument unpacking syntax - `*args` and `**kwargs`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python has a very convenient \"keyword argument unpacking syntax\" (often also referred to as \"splat\"-operators). This is particularly useful, if we want to define a function that can take a arbitrary number of input arguments." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Single-asterisk (*args)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def a_func(*args):\n", - " print('type of args:', type(args))\n", - " print('args contents:', args)\n", - " print('1st argument:', args[0])\n", - "\n", - "a_func(0, 1, 'a', 'b', 'c')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "type of args: \n", - "args contents: (0, 1, 'a', 'b', 'c')\n", - "1st argument: 0\n" - ] - } - ], - "prompt_number": 55 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Double-asterisk (**kwargs)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def b_func(**kwargs):\n", - " print('type of kwargs:', type(kwargs))\n", - " print('kwargs contents: ', kwargs)\n", - " print('value of argument a:', kwargs['a'])\n", - " \n", - "b_func(a=1, b=2, c=3, d=4)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "type of kwargs: \n", - "kwargs contents: {'d': 4, 'a': 1, 'c': 3, 'b': 2}\n", - "value of argument a: 1\n" - ] - } - ], - "prompt_number": 56 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### (Partially) unpacking of iterables\n", - "Another useful application of the \"unpacking\"-operator is the unpacking of lists and other other iterables." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "val1, *vals = [1, 2, 3, 4, 5]\n", - "print('val1:', val1)\n", - "print('vals:', vals)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "val1: 1\n", - "vals: [2, 3, 4, 5]\n" - ] - } - ], - "prompt_number": 57 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Metaclasses - What creates a new instance of a class?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Usually, it is the `__init__` method when we think of instanciating a new object from a class. However, it is the static method `__new__` (it is not a class method!) that creates and returns a new instance before `__init__()` is called. \n", - "More specifically, this is what is returned: \n", - "`return super(, cls).__new__(subcls, *args, **kwargs)` \n", - "\n", - "For more information about the `__new__` method, please see the [documentation](https://www.python.org/download/releases/2.2/descrintro/#__new__).\n", - "\n", - "As a little experiment, let us screw with `__new__` so that it returns `None` and see if `__init__` will be executed:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "class a_class(object):\n", - " def __new__(clss, *args, **kwargs):\n", - " print('excecuted __new__')\n", - " return None\n", - " def __init__(self, an_arg):\n", - " print('excecuted __init__')\n", - " self.an_arg = an_arg\n", - " \n", - "a_object = a_class(1)\n", - "print('Type of a_object:', type(a_object))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "excecuted __new__\n", - "Type of a_object: \n" - ] - } - ], - "prompt_number": 53 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we can see in the code above, `__init__` requires the returned instance from `__new__` in order to called. So, here we just created a `NoneType` object. \n", - "Let us override the `__new__`, now and let us confirm that `__init__` is called now to instantiate the new object\":" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "class a_class(object):\n", - " def __new__(cls, *args, **kwargs):\n", - " print('excecuted __new__')\n", - " inst = super(a_class, cls).__new__(cls)\n", - " return inst\n", - " def __init__(self, an_arg):\n", - " print('excecuted __init__')\n", - " self.an_arg = an_arg\n", - " \n", - "a_object = a_class(1)\n", - "print('Type of a_object:', type(a_object))\n", - "print('a_object.an_arg: ', a_object.an_arg)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "excecuted __new__\n", - "excecuted __init__\n", - "Type of a_object: \n", - "a_object.an_arg: 1\n" - ] - } - ], - "prompt_number": 54 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "for i in range(5):\n", - " if i == 1:\n", - " print('in for')\n", - "else:\n", - " print('in else')\n", - "print('after for-loop')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "in for\n", - "in else\n", - "after for-loop\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "for i in range(5):\n", - " if i == 1:\n", - " break\n", - "else:\n", - " print('in else')\n", - "print('after for-loop')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "after for-loop\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Else-clauses: \"conditional else\" and \"completion else\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "I would claim that the conditional \"else\" is every programmer's daily bread and butter. However, there is a second flavor of \"else\"-clauses in Python, which I will call \"completion else\" (for reason that will become clear later). \n", - "But first, let us take a look at our \"traditional\" conditional else that we all are familiar with. \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "###Conditional else:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# conditional else\n", - "\n", - "a_list = [1,2]\n", - "if a_list[0] == 1:\n", - " print('Hello, World!')\n", - "else:\n", - " print('Bye, World!')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Hello, World!\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# conditional else\n", - "\n", - "a_list = [1,2]\n", - "if a_list[0] == 2:\n", - " print('Hello, World!')\n", - "else:\n", - " print('Bye, World!')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Bye, World!\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Why am I showing those simple examples? I think they are good to highlight some of the key points: It is **either** the code under the `if` clause that is executed, **or** the code under the `else` block, but not both. \n", - "If the condition of the `if` clause evaluates to `True`, the `if`-block is exectured, and if it evaluated to `False`, it is the `else` block. \n", - "\n", - "### Completion else\n", - "**In contrast** to the **either...or*** situation that we know from the conditional `else`, the completion `else` is executed if a code block finished. \n", - "To show you an example, let us use `else` for error-handling:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Completion else (try-except)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "try:\n", - " print('first element:', a_list[0])\n", - "except IndexError:\n", - " print('raised IndexError')\n", - "else:\n", - " print('no error in try-block')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "first element: 1\n", - "no error in try-block\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "try:\n", - " print('third element:', a_list[2])\n", - "except IndexError:\n", - " print('raised IndexError')\n", - "else:\n", - " print('no error in try-block')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "raised IndexError\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "In the code above, we can see that the code under the **`else`-clause is only executed if the `try-block` was executed without encountering an error, i.e., if the `try`-block is \"complete\".** \n", - "The same rule applies to the \"completion\" `else` in while- and for-loops, which you can confirm in the following samples below." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Completion else (while-loop)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "i = 0\n", - "while i < 2:\n", - " print(i)\n", - " i += 1\n", - "else:\n", - " print('in else')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n", - "1\n", - "in else\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "i = 0\n", - "while i < 2:\n", - " print(i)\n", - " i += 1\n", - " break\n", - "else:\n", - " print('completed while-loop')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Completion else (for-loop)" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "for i in range(2):\n", - " print(i)\n", - "else:\n", - " print('completed for-loop')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n", - "1\n", - "completed for-loop\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "for i in range(2):\n", - " print(i)\n", - " break\n", - "else:\n", - " print('completed for-loop')" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "0\n" - ] - } - ], - "prompt_number": 10 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interning of compile-time constants vs. run-time expressions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This might not be particularly useful, but it is nonetheless interesting: Python's interpreter is interning compile-time constants but not run-time expressions (note that this is implementation-specific).\n", - "\n", - "(Original source: [Stackoverflow](http://stackoverflow.com/questions/15541404/python-string-interning))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let us have a look at the simple example below. Here we are creating 3 variables and assign the value \"Hello\" to them in different ways before we test them for identity." - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "hello1 = 'Hello'\n", - "\n", - "hello2 = 'Hell' + 'o'\n", - "\n", - "hello3 = 'Hell'\n", - "hello3 = hello3 + 'o'\n", - "\n", - "print('hello1 is hello2:', hello1 is hello2)\n", - "print('hello1 is hello3:', hello1 is hello3)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "hello1 is hello2: True\n", - "hello1 is hello3: False\n" - ] - } - ], - "prompt_number": 34 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, how does it come that the first expression evaluates to true, but the second does not? To answer this question, we need to take a closer look at the underlying byte codes:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import dis\n", - "def hello1_func():\n", - " s = 'Hello'\n", - " return s\n", - "dis.dis(hello1_func)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - " 3 0 LOAD_CONST 1 ('Hello')\n", - " 3 STORE_FAST 0 (s)\n", - "\n", - " 4 6 LOAD_FAST 0 (s)\n", - " 9 RETURN_VALUE\n" - ] - } - ], - "prompt_number": 38 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def hello2_func():\n", - " s = 'Hell' + 'o'\n", - " return s\n", - "dis.dis(hello2_func)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - " 2 0 LOAD_CONST 3 ('Hello')\n", - " 3 STORE_FAST 0 (s)\n", - "\n", - " 3 6 LOAD_FAST 0 (s)\n", - " 9 RETURN_VALUE\n" - ] - } - ], - "prompt_number": 39 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def hello3_func():\n", - " s = 'Hell'\n", - " s = s + 'o'\n", - " return s\n", - "dis.dis(hello3_func)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - " 2 0 LOAD_CONST 1 ('Hell')\n", - " 3 STORE_FAST 0 (s)\n", - "\n", - " 3 6 LOAD_FAST 0 (s)\n", - " 9 LOAD_CONST 2 ('o')\n", - " 12 BINARY_ADD\n", - " 13 STORE_FAST 0 (s)\n", - "\n", - " 4 16 LOAD_FAST 0 (s)\n", - " 19 RETURN_VALUE\n" - ] - } - ], - "prompt_number": 40 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "It looks like that `'Hello'` and `'Hell'` + `'o'` are both evaluated and stored as `'Hello'` at compile-time, whereas the third version \n", - "`s = 'Hell'` \n", - "`s = s + 'o'` seems to be not interned. Let us quickly confirm the behavior with the following code:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print(hello1_func() is hello2_func())\n", - "print(hello1_func() is hello3_func())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "True\n", - "False\n" - ] - } - ], - "prompt_number": 42 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, to show that this hypothesis is the answer to this rather unexpected observation, let us `intern` the value manually:" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import sys\n", - "\n", - "print(hello1_func() is sys.intern(hello3_func()))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "True\n" - ] - } - ], - "prompt_number": 45 - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "
\n", - "
\n", - "
\n", - "
\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Changelog" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[[back to top](#sections)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### 07/16/2014\n", - "- slight change of wording in the [lambda-closure section](#lambda_closure)\n", - "\n", - "#### 05/24/2014\n", - "- new section: unorderable types in Python 2\n", - "- table of contents for the Python 2 vs. Python 3 topic\n", - " \n", - "#### 05/03/2014\n", - "- new section: else clauses: conditional vs. completion\n", - "- new section: Interning of compile-time constants vs. run-time expressions\n", - "\n", - "#### 05/02/2014\n", - "- new section in Python 3.x and Python 2.x key differences: for-loop leak\n", - "- new section: Metaclasses - What creates a new instance of a class? \n", - "\n", - "#### 05/01/2014\n", - "- new section: keyword argument unpacking syntax\n", - "\n", - "#### 04/27/2014\n", - "- minor fixes of typos \n", - "- new section: \"Only the first clause of generators is evaluated immediately\"" + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Sebastian Raschka](http://sebastianraschka.com) \n", + "\n", + "- [Link to this IPython Notebook on GitHub](https://github.com/rasbt/python_reference/blob/master/tutorials/not_so_obvious_python_stuff.ipynb) \n", + "- [Link to the GitHub repository](https://github.com/rasbt/python_reference) \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%load_ext watermark" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Last updated: 16/07/2014 \n", + "\n", + "CPython 3.4.1\n", + "IPython 2.0.0\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] } ], - "metadata": {} + "source": [ + "%watermark -d -u -v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[More information](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/ipython_magic/watermark.ipynb) about the `watermark` magic command extension.\n", + "\n", + "([Changelog](#changelog))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# A collection of not-so-obvious Python stuff you should know!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "I am really looking forward to your comments and suggestions to improve and \n", + "extend this little collection! Just send me a quick note \n", + "via Twitter: [@rasbt](https://twitter.com/rasbt) \n", + "or Email: [bluewoodtree@gmail.com](mailto:bluewoodtree@gmail.com)\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sections\n", + "- [The C3 class resolution algorithm for multiple class inheritance](#c3_class_res)\n", + "\n", + "- [Assignment operators and lists - simple-add vs. add-AND operators](#pm_in_lists)\n", + "\n", + "- [`True` and `False` in the datetime module](#datetime_module)\n", + "\n", + "- [Python reuses objects for small integers - always use \"==\" for equality, \"is\" for identity](#python_small_int)\n", + "\n", + "- [Shallow vs. deep copies if list contains other structures and objects](#shallow_vs_deep)\n", + "\n", + "- [Picking `True` values from logical `and`s and `or`s](#false_true_expressions)\n", + "\n", + "- [Don't use mutable objects as default arguments for functions!](#def_mutable_func)\n", + "\n", + "- [Be aware of the consuming generator](#consuming_generator)\n", + "\n", + "- [`bool` is a subclass of `int`](#bool_int)\n", + "\n", + "- [About lambda-in-closures and-a-loop pitfall](#lambda_closure)\n", + "\n", + "- [Python's LEGB scope resolution and the keywords `global` and `nonlocal`](#python_legb)\n", + "\n", + "- [When mutable contents of immutable tuples aren't so mutable](#immutable_tuple)\n", + "\n", + "- [List comprehensions are fast, but generators are faster!?](#list_generator)\n", + "\n", + "- [Public vs. private class methods and name mangling](#private_class)\n", + "\n", + "- [The consequences of modifying a list when looping through it](#looping_pitfall)\n", + "\n", + "- [Dynamic binding and typos in variable names](#dynamic_binding)\n", + "\n", + "- [List slicing using indexes that are \"out of range](#out_of_range_slicing)\n", + "\n", + "- [Reusing global variable names and UnboundLocalErrors](#unboundlocalerror)\n", + "\n", + "- [Creating copies of mutable objects](#copy_mutable)\n", + "\n", + "- [Key differences between Python 2 and 3](#python_differences)\n", + "\n", + "- [Function annotations - What are those `->`'s in my Python code?](#function_annotation)\n", + "\n", + "- [Abortive statements in `finally` blocks](#finally_blocks)\n", + "\n", + "- [Assigning types to variables as values](#variable_types)\n", + "\n", + "- [Only the first clause of generators is evaluated immediately](#generator_rhs)\n", + "\n", + "- [Keyword argument unpacking syntax - `*args` and `**kwargs`](#splat_op)\n", + "\n", + "- [Metaclasses - What creates a new instance of a class?](#new_instance)\n", + "\n", + "- [Else-clauses: \"conditional else\" and \"completion else\"](#else_clauses)\n", + "\n", + "- [Interning of compile-time constants vs. run-time expressions](#string_interning)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The C3 class resolution algorithm for multiple class inheritance" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we are dealing with multiple inheritance, according to the newer C3 class resolution algorithm, the following applies: \n", + "Assuming that child class C inherits from two parent classes A and B, \"class A should be checked before class B\".\n", + "\n", + "If you want to learn more, please read the [original blog](http://python-history.blogspot.ru/2010/06/method-resolution-order.html) post by Guido van Rossum.\n", + "\n", + "(Original source: [http://gistroll.com/rolls/21/horizontal_assessments/new](http://gistroll.com/rolls/21/horizontal_assessments/new))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "class A\n" + ] + } + ], + "source": [ + "class A(object):\n", + " def foo(self):\n", + " print(\"class A\")\n", + "\n", + "class B(object):\n", + " def foo(self):\n", + " print(\"class B\")\n", + "\n", + "class C(A, B):\n", + " pass\n", + "\n", + "C().foo()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So what actually happened above was that class `C` looked in the scope of the parent class `A` for the method `.foo()` first (and found it)!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I received an email containing a suggestion which uses a more nested example to illustrate Guido van Rossum's point a little bit better:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "class C\n" + ] + } + ], + "source": [ + "class A(object):\n", + " def foo(self):\n", + " print(\"class A\")\n", + "\n", + "class B(A):\n", + " pass\n", + "\n", + "class C(A):\n", + " def foo(self):\n", + " print(\"class C\")\n", + "\n", + "class D(B,C):\n", + " pass\n", + "\n", + "D().foo()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, class `D` searches in `B` first, which in turn inherits from `A` (note that class `C` also inherits from `A`, but has its own `.foo()` method) so that we come up with the search order: `D, B, C, A`. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Assignment operators and lists - simple-add vs. add-AND operators" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python `list`s are mutable objects as we all know. So, if we are using the `+=` operator on `list`s, we extend the `list` by directly modifying the object directly. \n", + "\n", + "However, if we use the assigment via `my_list = my_list + ...`, we create a new list object, which can be demonstrated by the following code:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ID: 4366496544\n", + "ID (+=): 4366496544\n", + "ID (list = list + ...): 4366495472\n" + ] + } + ], + "source": [ + "a_list = []\n", + "print('ID:', id(a_list))\n", + "\n", + "a_list += [1]\n", + "print('ID (+=):', id(a_list))\n", + "\n", + "a_list = a_list + [2]\n", + "print('ID (list = list + ...):', id(a_list))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Just for reference, the `.append()` and `.extends()` methods are modifying the `list` object in place, just as expected." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] \n", + "ID (initial): 140704077653128 \n", + "\n", + "[1] \n", + "ID (append): 140704077653128 \n", + "\n", + "[1, 2] \n", + "ID (extend): 140704077653128\n" + ] + } + ], + "source": [ + "a_list = []\n", + "print(a_list, '\\nID (initial):',id(a_list), '\\n')\n", + "\n", + "a_list.append(1)\n", + "print(a_list, '\\nID (append):',id(a_list), '\\n')\n", + "\n", + "a_list.extend([2])\n", + "print(a_list, '\\nID (extend):',id(a_list))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## `True` and `False` in the datetime module\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"It often comes as a big surprise for programmers to find (sometimes by way of a hard-to-reproduce bug) that, unlike any other time value, midnight (i.e. `datetime.time(0,0,0)`) is False. A long discussion on the python-ideas mailing list shows that, while surprising, that behavior is desirable—at least in some quarters.\" \n", + "\n", + "(Original source: [http://lwn.net/SubscriberLink/590299/bf73fe823974acea/](http://lwn.net/SubscriberLink/590299/bf73fe823974acea/))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"datetime.time(0,0,0)\" (Midnight) -> False\n", + "\"datetime.time(1,0,0)\" (1 am) -> True\n" + ] + } + ], + "source": [ + "import datetime\n", + "\n", + "print('\"datetime.time(0,0,0)\" (Midnight) ->', bool(datetime.time(0,0,0)))\n", + "\n", + "print('\"datetime.time(1,0,0)\" (1 am) ->', bool(datetime.time(1,0,0)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Python reuses objects for small integers - use \"==\" for equality, \"is\" for identity\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This oddity occurs, because Python keeps an array of small integer objects (i.e., integers between -5 and 256, [see the doc](https://docs.python.org/2/c-api/int.html#PyInt_FromLong))." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a is b True\n", + "c is d False\n" + ] + } + ], + "source": [ + "a = 1\n", + "b = 1\n", + "print('a is b', bool(a is b))\n", + "True\n", + "\n", + "c = 999\n", + "d = 999\n", + "print('c is d', bool(c is d))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "(*I received a comment that this is in fact a CPython artefact and **must not necessarily be true** in all implementations of Python!*)\n", + "\n", + "So the take home message is: always use \"==\" for equality, \"is\" for identity!\n", + "\n", + "Here is a [nice article](http://python.net/%7Egoodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables) explaining it by comparing \"boxes\" (C language) with \"name tags\" (Python)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example demonstrates that this applies indeed for integers in the range in -5 to 256:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "256 is 257-1 True\n", + "257 is 258-1 False\n", + "-5 is -6+1 True\n", + "-7 is -6-1 False\n" + ] + } + ], + "source": [ + "print('256 is 257-1', 256 is 257-1)\n", + "print('257 is 258-1', 257 is 258 - 1)\n", + "print('-5 is -6+1', -5 is -6+1)\n", + "print('-7 is -6-1', -7 is -6-1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### And to illustrate the test for equality (`==`) vs. identity (`is`):" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a is b, False\n", + "a == b, True\n" + ] + } + ], + "source": [ + "a = 'hello world!'\n", + "b = 'hello world!'\n", + "print('a is b,', a is b)\n", + "print('a == b,', a == b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We would think that identity would always imply equality, but this is not always true, as we can see in the next example:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a is a, True\n", + "a == a, False\n" + ] + } + ], + "source": [ + "a = float('nan')\n", + "print('a is a,', a is a)\n", + "print('a == a,', a == a)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Shallow vs. deep copies if list contains other structures and objects\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Shallow copy**: \n", + "If we use the assignment operator to assign one list to another list, we just create a new name reference to the original list. If we want to create a new list object, we have to make a copy of the original list. This can be done via `a_list[:]` or `a_list.copy()`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IDs:\n", + "list1: 4346366472\n", + "list2: 4346366472\n", + "list3: 4346366408\n", + "list4: 4346366536\n", + "\n", + "list1: [3, 2]\n", + "list1: [3, 2]\n" + ] + } + ], + "source": [ + "list1 = [1,2]\n", + "list2 = list1 # reference\n", + "list3 = list1[:] # shallow copy\n", + "list4 = list1.copy() # shallow copy\n", + "\n", + "print('IDs:\\nlist1: {}\\nlist2: {}\\nlist3: {}\\nlist4: {}\\n'\n", + " .format(id(list1), id(list2), id(list3), id(list4)))\n", + "\n", + "list2[0] = 3\n", + "print('list1:', list1)\n", + "\n", + "list3[0] = 4\n", + "list4[1] = 4\n", + "print('list1:', list1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Deep copy** \n", + "As we have seen above, a shallow copy works fine if we want to create a new list with contents of the original list which we want to modify independently. \n", + "\n", + "However, if we are dealing with compound objects (e.g., lists that contain other lists, [read here](https://docs.python.org/2/library/copy.html) for more information) it becomes a little trickier.\n", + "\n", + "In the case of compound objects, a shallow copy would create a new compound object, but it would just insert the references to the contained objects into the new compound object. In contrast, a deep copy would go \"deeper\" and create also new objects \n", + "for the objects found in the original compound object. \n", + "If you follow the code, the concept should become more clear:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IDs:\n", + "list1: 4377956296\n", + "list2: 4377961752\n", + "list3: 4377954928\n", + "\n", + "list1: [[3], [2]]\n", + "list1: [[3], [2]]\n" + ] + } + ], + "source": [ + "from copy import deepcopy\n", + "\n", + "list1 = [[1],[2]]\n", + "list2 = list1.copy() # shallow copy\n", + "list3 = deepcopy(list1) # deep copy\n", + "\n", + "print('IDs:\\nlist1: {}\\nlist2: {}\\nlist3: {}\\n'\n", + " .format(id(list1), id(list2), id(list3)))\n", + "\n", + "list2[0][0] = 3\n", + "print('list1:', list1)\n", + "\n", + "list3[0][0] = 5\n", + "print('list1:', list1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Picking `True` values from logical `and`s and `or`s" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Logical `or`:** \n", + "\n", + "`a or b == a if a else b` \n", + "- If both values in `or` expressions are `True`, Python will select the first value (e.g., select `\"a\"` in `\"a\" or \"b\"`), and the second one in `and` expressions. \n", + "This is also called **short-circuiting** - we already know that the logical `or` must be `True` if the first value is `True` and therefore can omit the evaluation of the second value.\n", + "\n", + "**Logical `and`:** \n", + "\n", + "`a and b == b if a else a` \n", + "- If both values in `and` expressions are `True`, Python will select the second value, since for a logical `and`, both values must be true.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 * 7 = 14\n" + ] + } + ], + "source": [ + "result = (2 or 3) * (5 and 7)\n", + "print('2 * 7 =', result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Don't use mutable objects as default arguments for functions!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Don't use mutable objects (e.g., dictionaries, lists, sets, etc.) as default arguments for functions! You might expect that a new list is created every time when we call the function without providing an argument for the default parameter, but this is not the case: **Python will create the mutable object (default parameter) the first time the function is defined - not when it is called**, see the following code:\n", + "\n", + "(Original source: [http://docs.python-guide.org/en/latest/writing/gotchas/](http://docs.python-guide.org/en/latest/writing/gotchas/)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1]\n", + "[1, 2]\n" + ] + } + ], + "source": [ + "def append_to_list(value, def_list=[]):\n", + " def_list.append(value)\n", + " return def_list\n", + "\n", + "my_list = append_to_list(1)\n", + "print(my_list)\n", + "\n", + "my_other_list = append_to_list(2)\n", + "print(my_other_list)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another good example showing that demonstrates that default arguments are created when the function is created (**and not when it is called!**):" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1397764090.456688\n", + "1397764090.456688\n" + ] + } + ], + "source": [ + "import time\n", + "def report_arg(my_default=time.time()):\n", + " print(my_default)\n", + "\n", + "report_arg()\n", + "\n", + "time.sleep(5)\n", + "\n", + "report_arg()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Be aware of the consuming generator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Be aware of what is happening when combining \"`in`\" checks with generators, since they won't evaluate from the beginning once a position is \"consumed\"." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 in gen, True\n", + "3 in gen, True\n", + "1 in gen, False\n" + ] + } + ], + "source": [ + "gen = (i for i in range(5))\n", + "print('2 in gen,', 2 in gen)\n", + "print('3 in gen,', 3 in gen)\n", + "print('1 in gen,', 1 in gen) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although this defeats the purpose of an generator (in most cases), we can convert a generator into a list to circumvent the problem. " + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 in l, True\n", + "3 in l, True\n", + "1 in l, True\n" + ] + } + ], + "source": [ + "gen = (i for i in range(5))\n", + "a_list = list(gen)\n", + "print('2 in l,', 2 in a_list)\n", + "print('3 in l,', 3 in a_list)\n", + "print('1 in l,', 1 in a_list) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## `bool` is a subclass of `int`\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Chicken or egg? In the history of Python (Python 2.2 to be specific) truth values were implemented via 1 and 0 (similar to the old C). In order to avoid syntax errors in old (but perfectly working) Python code, `bool` was added as a subclass of `int` in Python 2.3.\n", + "\n", + "Original source: [http://www.peterbe.com/plog/bool-is-int](http://www.peterbe.com/plog/bool-is-int)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "isinstance(True, int): True\n", + "True + True: 2\n", + "3*True + True: 4\n", + "3*True - False: 3\n" + ] + } + ], + "source": [ + "print('isinstance(True, int):', isinstance(True, int))\n", + "print('True + True:', True + True)\n", + "print('3*True + True:', 3*True + True)\n", + "print('3*True - False:', 3*True - False)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## About lambda-in-closures-and-a-loop pitfall" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Remember the section about the [\"consuming generators\"](consuming_generators)? This example is somewhat related, but the result might still come unexpected. \n", + "\n", + "(Original source: [http://openhome.cc/eGossip/Blog/UnderstandingLambdaClosure3.html](http://openhome.cc/eGossip/Blog/UnderstandingLambdaClosure3.html))\n", + "\n", + "In the first example below, we call a `lambda` function in a list comprehension, and the value `i` will be dereferenced every time we call `lambda` within the scope of the list comprehension. Since the list comprehension has already been constructed and evaluated when we for-loop through the list, the closure-variable will be set to the last value 4." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4\n", + "4\n", + "4\n", + "4\n", + "4\n" + ] + } + ], + "source": [ + "my_list = [lambda: i for i in range(5)]\n", + "for l in my_list:\n", + " print(l())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, by using a generator expression, we can make use of its stepwise evaluation (note that the returned variable still stems from the same closure, but the value changes as we iterate over the generator)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "my_gen = (lambda: n for n in range(5))\n", + "for l in my_gen:\n", + " print(l())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And if you are really keen on using lists, there is a nifty trick that circumvents this problem as a reader nicely pointed out in the comments: We can simply pass the loop variable `i` as a default argument to the lambdas." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "my_list = [lambda x=i: x for i in range(5)]\n", + "for l in my_list:\n", + " print(l())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Python's LEGB scope resolution and the keywords `global` and `nonlocal`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There is nothing particularly surprising about Python's LEGB scope resolution (Local -> Enclosed -> Global -> Built-in), but it is still useful to take a look at some examples!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `global` vs. `local`\n", + "\n", + "According to the LEGB rule, Python will first look for a variable in the local scope. So if we set the variable `x = 1` `local`ly in the function's scope, it won't have an effect on the `global` `x`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "in_func: 1\n", + "global: 0\n" + ] + } + ], + "source": [ + "x = 0\n", + "def in_func():\n", + " x = 1\n", + " print('in_func:', x)\n", + " \n", + "in_func()\n", + "print('global:', x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we want to modify the `global` x via a function, we can simply use the `global` keyword to import the variable into the function's scope:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "in_func: 1\n", + "global: 1\n" + ] + } + ], + "source": [ + "x = 0\n", + "def in_func():\n", + " global x\n", + " x = 1\n", + " print('in_func:', x)\n", + " \n", + "in_func()\n", + "print('global:', x)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `local` vs. `enclosed`\n", + "\n", + "Now, let us take a look at `local` vs. `enclosed`. Here, we set the variable `x = 1` in the `outer` function and set `x = 1` in the enclosed function `inner`. Since `inner` looks in the local scope first, it won't modify `outer`'s `x`." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "outer before: 1\n", + "inner: 2\n", + "outer after: 1\n" + ] + } + ], + "source": [ + "def outer():\n", + " x = 1\n", + " print('outer before:', x)\n", + " def inner():\n", + " x = 2\n", + " print(\"inner:\", x)\n", + " inner()\n", + " print(\"outer after:\", x)\n", + "outer()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is where the `nonlocal` keyword comes in handy - it allows us to modify the `x` variable in the `enclosed` scope:" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "outer before: 1\n", + "inner: 2\n", + "outer after: 2\n" + ] + } + ], + "source": [ + "def outer():\n", + " x = 1\n", + " print('outer before:', x)\n", + " def inner():\n", + " nonlocal x\n", + " x = 2\n", + " print(\"inner:\", x)\n", + " inner()\n", + " print(\"outer after:\", x)\n", + "outer()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## When mutable contents of immutable tuples aren't so mutable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we all know, tuples are immutable objects in Python, right!? But what happens if they contain mutable objects? \n", + "\n", + "First, let us have a look at the expected behavior: a `TypeError` is raised if we try to modify immutable types in a tuple: " + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "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[0mtup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtup\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "tup = (1,)\n", + "tup[0] += 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### But what if we put a mutable object into the immutable tuple? Well, modification works, but we **also** get a `TypeError` at the same time." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tup before: ([],)\n" + ] + }, + { + "ename": "TypeError", + "evalue": "'tuple' object does not support item assignment", + "output_type": "error", + "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[0mtup\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m]\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'tup before: '\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtup\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mtup\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment" + ] + } + ], + "source": [ + "tup = ([],)\n", + "print('tup before: ', tup)\n", + "tup[0] += [1]" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tup after: ([1],)\n" + ] + } + ], + "source": [ + "print('tup after: ', tup)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "However, **there are ways** to modify the mutable contents of the tuple without raising the `TypeError`, the solution is the `.extend()` method, or alternatively `.append()` (for lists):" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tup before: ([],)\n", + "tup after: ([1],)\n" + ] + } + ], + "source": [ + "tup = ([],)\n", + "print('tup before: ', tup)\n", + "tup[0].extend([1])\n", + "print('tup after: ', tup)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tup before: ([],)\n", + "tup after: ([1],)\n" + ] + } + ], + "source": [ + "tup = ([],)\n", + "print('tup before: ', tup)\n", + "tup[0].append(1)\n", + "print('tup after: ', tup)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Explanation\n", + "\n", + "**A. Jesse Jiryu Davis** has a nice explanation for this phenomenon (Original source: [http://emptysqua.re/blog/python-increment-is-weird-part-ii/](http://emptysqua.re/blog/python-increment-is-weird-part-ii/))\n", + "\n", + "If we try to extend the list via `+=` *\"then the statement executes `STORE_SUBSCR`, which calls the C function `PyObject_SetItem`, which checks if the object supports item assignment. In our case the object is a tuple, so `PyObject_SetItem` throws the `TypeError`. Mystery solved.\"*" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### One more note about the `immutable` status of tuples. Tuples are famous for being immutable. However, how comes that this code works?" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 4, 5)\n" + ] + } + ], + "source": [ + "my_tup = (1,)\n", + "my_tup += (4,)\n", + "my_tup = my_tup + (5,)\n", + "print(my_tup)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What happens \"behind\" the curtains is that the tuple is not modified, but every time a new object is generated, which will inherit the old \"name tag\":" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4337381840\n", + "4357415496\n", + "4357289952\n" + ] + } + ], + "source": [ + "my_tup = (1,)\n", + "print(id(my_tup))\n", + "my_tup += (4,)\n", + "print(id(my_tup))\n", + "my_tup = my_tup + (5,)\n", + "print(id(my_tup))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## List comprehensions are fast, but generators are faster!?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"List comprehensions are fast, but generators are faster!?\" - No, not really (or significantly, see the benchmarks below). So what's the reason to prefer one over the other?\n", + "- use lists if you want to use the plethora of list methods \n", + "- use generators when you are dealing with huge collections to avoid memory issues" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import timeit\n", + "\n", + "def plainlist(n=100000):\n", + " my_list = []\n", + " for i in range(n):\n", + " if i % 5 == 0:\n", + " my_list.append(i)\n", + " return my_list\n", + "\n", + "def listcompr(n=100000):\n", + " my_list = [i for i in range(n) if i % 5 == 0]\n", + " return my_list\n", + "\n", + "def generator(n=100000):\n", + " my_gen = (i for i in range(n) if i % 5 == 0)\n", + " return my_gen\n", + "\n", + "def generator_yield(n=100000):\n", + " for i in range(n):\n", + " if i % 5 == 0:\n", + " yield i" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### To be fair to the list, let us exhaust the generators:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "plain_list: 10 loops, best of 3: 22.4 ms per loop\n", + "\n", + "listcompr: 10 loops, best of 3: 20.8 ms per loop\n", + "\n", + "generator: 10 loops, best of 3: 22 ms per loop\n", + "\n", + "generator_yield: 10 loops, best of 3: 21.9 ms per loop\n" + ] + } + ], + "source": [ + "def test_plainlist(plain_list):\n", + " for i in plain_list():\n", + " pass\n", + "\n", + "def test_listcompr(listcompr):\n", + " for i in listcompr():\n", + " pass\n", + "\n", + "def test_generator(generator):\n", + " for i in generator():\n", + " pass\n", + "\n", + "def test_generator_yield(generator_yield):\n", + " for i in generator_yield():\n", + " pass\n", + "\n", + "print('plain_list: ', end = '')\n", + "%timeit test_plainlist(plainlist)\n", + "print('\\nlistcompr: ', end = '')\n", + "%timeit test_listcompr(listcompr)\n", + "print('\\ngenerator: ', end = '')\n", + "%timeit test_generator(generator)\n", + "print('\\ngenerator_yield: ', end = '')\n", + "%timeit test_generator_yield(generator_yield)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Public vs. private class methods and name mangling\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Who has not stumbled across this quote \"we are all consenting adults here\" in the Python community, yet? Unlike in other languages like C++ (sorry, there are many more, but that's one I am most familiar with), we can't really protect class methods from being used outside the class (i.e., by the API user). \n", + "All we can do is to indicate methods as private to make clear that they are better not used outside the class, but it is really up to the class user, since \"we are all consenting adults here\"! \n", + "So, when we want to mark a class method as private, we can put a single underscore in front of it. \n", + "If we additionally want to avoid name clashes with other classes that might use the same method names, we can prefix the name with a double-underscore to invoke the name mangling.\n", + "\n", + "This doesn't prevent the class user to access this class member though, but he has to know the trick and also knows that it his own risk...\n", + "\n", + "Let the following example illustrate what I mean:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello public world!\n", + "Hello private world!\n", + "Hello private world!\n" + ] + } + ], + "source": [ + "class my_class():\n", + " def public_method(self):\n", + " print('Hello public world!')\n", + " def __private_method(self):\n", + " print('Hello private world!')\n", + " def call_private_method_in_class(self):\n", + " self.__private_method()\n", + " \n", + "my_instance = my_class()\n", + "\n", + "my_instance.public_method()\n", + "my_instance._my_class__private_method()\n", + "my_instance.call_private_method_in_class()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The consequences of modifying a list when looping through it" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It can be really dangerous to modify a list when iterating through it - this is a very common pitfall that can cause unintended behavior! \n", + "Look at the following examples, and for a fun exercise: try to figure out what is going on before you skip to the solution!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 3, 5]\n" + ] + } + ], + "source": [ + "a = [1, 2, 3, 4, 5]\n", + "for i in a:\n", + " if not i % 2:\n", + " a.remove(i)\n", + "print(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[4, 5]\n" + ] + } + ], + "source": [ + "b = [2, 4, 5, 6]\n", + "for i in b:\n", + " if not i % 2:\n", + " b.remove(i)\n", + "print(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "**The solution** is that we are iterating through the list index by index, and if we remove one of the items in-between, we inevitably mess around with the indexing, look at the following example, and it will become clear:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 2\n", + "1 5\n", + "2 6\n", + "[4, 5]\n" + ] + } + ], + "source": [ + "b = [2, 4, 5, 6]\n", + "for index, item in enumerate(b):\n", + " print(index, item)\n", + " if not item % 2:\n", + " b.remove(item)\n", + "print(b)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dynamic binding and typos in variable names\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Be careful, dynamic binding is convenient, but can also quickly become dangerous!" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first list:\n", + "0\n", + "1\n", + "2\n", + "\n", + "second list:\n", + "2\n", + "2\n", + "2\n" + ] + } + ], + "source": [ + "print('first list:')\n", + "for i in range(3):\n", + " print(i)\n", + " \n", + "print('\\nsecond list:')\n", + "for j in range(3):\n", + " print(i) # I (intentionally) made typo here!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## List slicing using indexes that are \"out of range\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we have all encountered it 1 (x10000) time(s) in our live, the infamous `IndexError`:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "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[0mmy_list\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\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;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmy_list\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m]\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" + ] + } + ], + "source": [ + "my_list = [1, 2, 3, 4, 5]\n", + "print(my_list[5])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But suprisingly, it is not raised when we are doing list slicing, which can be a really pain for debugging:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[]\n" + ] + } + ], + "source": [ + "my_list = [1, 2, 3, 4, 5]\n", + "print(my_list[5:])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Reusing global variable names and `UnboundLocalErrors`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Usually, it is no problem to access global variables in the local scope of a function:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "global\n" + ] + } + ], + "source": [ + "def my_func():\n", + " print(var)\n", + "\n", + "var = 'global'\n", + "my_func()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And is also no problem to use the same variable name in the local scope without affecting the local counterpart: " + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "global\n" + ] + } + ], + "source": [ + "def my_func():\n", + " var = 'locally changed'\n", + "\n", + "var = 'global'\n", + "my_func()\n", + "print(var)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But we have to be careful if we use a variable name that occurs in the global scope, and we want to access it in the local function scope if we want to reuse this name:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "UnboundLocalError", + "evalue": "local variable 'var' referenced before assignment", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mUnboundLocalError\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;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'global'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mmy_func\u001b[0m\u001b[0;34m(\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;36mmy_func\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmy_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvar\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# want to access global variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'locally changed'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mvar\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'global'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mUnboundLocalError\u001b[0m: local variable 'var' referenced before assignment" + ] + } + ], + "source": [ + "def my_func():\n", + " print(var) # want to access global variable\n", + " var = 'locally changed' # but Python thinks we forgot to define the local variable!\n", + " \n", + "var = 'global'\n", + "my_func()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, we have to use the `global` keyword!" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "global\n", + "locally changed\n" + ] + } + ], + "source": [ + "def my_func():\n", + " global var\n", + " print(var) # want to access global variable\n", + " var = 'locally changed' # changes the gobal variable\n", + "\n", + "var = 'global'\n", + "\n", + "my_func()\n", + "print(var)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating copies of mutable objects\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's assume a scenario where we want to duplicate sub`list`s of values stored in another list. If we want to create independent sub`list` object, using the arithmetic multiplication operator could lead to rather unexpected (or undesired) results:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "initially ---> [[1, 2, 3], [1, 2, 3]]\n", + "after my_list1[1][0] = 'a' ---> [['a', 2, 3], ['a', 2, 3]]\n" + ] + } + ], + "source": [ + "my_list1 = [[1, 2, 3]] * 2\n", + "\n", + "print('initially ---> ', my_list1)\n", + "\n", + "# modify the 1st element of the 2nd sublist\n", + "my_list1[1][0] = 'a'\n", + "print(\"after my_list1[1][0] = 'a' ---> \", my_list1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "In this case, we should better create \"new\" objects:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "initially: ---> [[1, 2, 3], [1, 2, 3]]\n", + "after my_list2[1][0] = 'a': ---> [[1, 2, 3], ['a', 2, 3]]\n" + ] + } + ], + "source": [ + "my_list2 = [[1, 2, 3] for i in range(2)]\n", + "\n", + "print('initially: ---> ', my_list2)\n", + "\n", + "# modify the 1st element of the 2nd sublist\n", + "my_list2[1][0] = 'a'\n", + "print(\"after my_list2[1][0] = 'a': ---> \", my_list2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "And here is the proof:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "id my_list1: 4350764680, id my_list2: 4350766472\n", + "id my_list1: 4350764680, id my_list2: 4350766664\n" + ] + } + ], + "source": [ + "for a,b in zip(my_list1, my_list2):\n", + " print('id my_list1: {}, id my_list2: {}'.format(id(a), id(b)))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Key differences between Python 2 and 3\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are some good articles already that are summarizing the differences between Python 2 and 3, e.g., \n", + "- [https://wiki.python.org/moin/Python2orPython3](https://wiki.python.org/moin/Python2orPython3)\n", + "- [https://docs.python.org/3.0/whatsnew/3.0.html](https://docs.python.org/3.0/whatsnew/3.0.html)\n", + "- [http://python3porting.com/differences.html](http://python3porting.com/differences.html)\n", + "- [https://docs.python.org/3/howto/pyporting.html](https://docs.python.org/3/howto/pyporting.html) \n", + "etc.\n", + "\n", + "But it might be still worthwhile, especially for Python newcomers, to take a look at some of those!\n", + "(Note: the the code was executed in Python 3.4.0 and Python 2.7.5 and copied from interactive shell sessions.)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Overview - Key differences between Python 2 and 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "- [Unicode](#unicode)\n", + "- [The print statement](#print)\n", + "- [Integer division](#integer_div)\n", + "- [xrange()](#xrange)\n", + "- [Raising exceptions](#raising_exceptions)\n", + "- [Handling exceptions](#handling_exceptions)\n", + "- [next() function and .next() method](#next_next)\n", + "- [Loop variables and leaking into the global scope](#loop_leak)\n", + "- [Comparing unorderable types](#compare_unorder)\n", + "\n", + "
\n", + "
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unicode..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "####- Python 2: \n", + "We have ASCII `str()` types, separate `unicode()`, but no `byte` type\n", + "####- Python 3: \n", + "Now, we finally have Unicode (utf-8) `str`ings, and 2 byte classes: `byte` and `bytearray`s" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "#############\n", + "# Python 2\n", + "#############\n", + "\n", + ">>> type(unicode('is like a python3 str()'))\n", + "\n", + "\n", + ">>> type(b'byte type does not exist')\n", + "\n", + "\n", + ">>> 'they are really' + b' the same'\n", + "'they are really the same'\n", + "\n", + ">>> type(bytearray(b'bytearray oddly does exist though'))\n", + "\n", + "\n", + "#############\n", + "# Python 3\n", + "#############\n", + "\n", + ">>> print('strings are now utf-8 \\u03BCnico\\u0394é!')\n", + "strings are now utf-8 μnicoΔé!\n", + "\n", + "\n", + ">>> type(b' and we have byte types for storing data')\n", + "\n", + "\n", + ">>> type(bytearray(b'but also bytearrays for those who prefer them over strings'))\n", + "\n", + "\n", + ">>> 'string' + b'bytes for data'\n", + "Traceback (most recent call last):s\n", + " File \"\", line 1, in \n", + "TypeError: Can't convert 'bytes' object to str implicitly" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The print statement" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Very trivial, but this change makes sense, Python 3 now only accepts `print`s with proper parentheses - just like the other function calls ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> print 'Hello, World!'\n", + "Hello, World!\n", + ">>> print('Hello, World!')\n", + "Hello, World!\n", + "\n", + "# Python 3\n", + ">>> print('Hello, World!')\n", + "Hello, World!\n", + ">>> print 'Hello, World!'\n", + " File \"\", line 1\n", + " print 'Hello, World!'\n", + " ^\n", + "SyntaxError: invalid syntax" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And if we want to print the output of 2 consecutive print functions on the same line, you would use a comma in Python 2, and a `end=\"\"` in Python 3:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> print \"line 1\", ; print 'same line'\n", + "line 1 same line\n", + "\n", + "# Python 3\n", + ">>> print(\"line 1\", end=\"\") ; print (\" same line\")\n", + "line 1 same line" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Integer division" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a pretty dangerous thing if you are porting code, or executing Python 3 code in Python 2 since the change in integer-division behavior can often go unnoticed. \n", + "So, I still tend to use a `float(3)/2` or `3/2.0` instead of a `3/2` in my Python 3 scripts to save the Python 2 guys some trouble ... (PS: and vice versa, you can `from __future__ import division` in your Python 2 scripts)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> 3 / 2\n", + "1\n", + ">>> 3 // 2\n", + "1\n", + ">>> 3 / 2.0\n", + "1.5\n", + ">>> 3 // 2.0\n", + "1.0\n", + "\n", + "# Python 3\n", + ">>> 3 / 2\n", + "1.5\n", + ">>> 3 // 2\n", + "1\n", + ">>> 3 / 2.0\n", + "1.5\n", + ">>> 3 // 2.0\n", + "1.0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###`xrange()` " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " \n", + "`xrange()` was pretty popular in Python 2.x if you wanted to create an iterable object. The behavior was quite similar to a generator ('lazy evaluation'), but you could iterate over it infinitely. The advantage was that it was generally faster than `range()` (e.g., in a for-loop) - not if you had to iterate over the list multiple times, since the generation happens every time from scratch! \n", + "In Python 3, the `range()` was implemented like the `xrange()` function so that a dedicated `xrange()` function does not exist anymore." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + "> python -m timeit 'for i in range(1000000):' ' pass'\n", + "10 loops, best of 3: 66 msec per loop\n", + "\n", + " > python -m timeit 'for i in xrange(1000000):' ' pass'\n", + "10 loops, best of 3: 27.8 msec per loop\n", + "\n", + "# Python 3\n", + "> python3 -m timeit 'for i in range(1000000):' ' pass'\n", + "10 loops, best of 3: 51.1 msec per loop\n", + "\n", + "> python3 -m timeit 'for i in xrange(1000000):' ' pass'\n", + "Traceback (most recent call last):\n", + " File \"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py\", line 292, in main\n", + " x = t.timeit(number)\n", + " File \"/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py\", line 178, in timeit\n", + " timing = self.inner(it, self.timer)\n", + " File \"\", line 6, in inner\n", + " for i in xrange(1000000):\n", + "NameError: name 'xrange' is not defined" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Raising exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Where Python 2 accepts both notations, the 'old' and the 'new' way, Python 3 chokes (and raises a `SyntaxError` in turn) if we don't enclose the exception argument in parentheses:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> raise IOError, \"file error\"\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "IOError: file error\n", + ">>> raise IOError(\"file error\")\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "IOError: file error\n", + "\n", + " \n", + "# Python 3 \n", + ">>> raise IOError, \"file error\"\n", + " File \"\", line 1\n", + " raise IOError, \"file error\"\n", + " ^\n", + "SyntaxError: invalid syntax\n", + ">>> raise IOError(\"file error\")\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "OSError: file error" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Handling exceptions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Also the handling of exceptions has slightly changed in Python 3. Now, we have to use the `as` keyword!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> try:\n", + "... blabla\n", + "... except NameError, err:\n", + "... print err, '--> our error msg'\n", + "... \n", + "name 'blabla' is not defined --> our error msg\n", + "\n", + "# Python 3\n", + ">>> try:\n", + "... blabla\n", + "... except NameError as err:\n", + "... print(err, '--> our error msg')\n", + "... \n", + "name 'blabla' is not defined --> our error msg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### The `next()` function and `.next()` method" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "Where you can use both function and method in Python 2.7.5, the `next()` function is all that remain in Python 3!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Python 2\n", + ">>> my_generator = (letter for letter in 'abcdefg')\n", + ">>> my_generator.next()\n", + "'a'\n", + ">>> next(my_generator)\n", + "'b'\n", + "\n", + "# Python 3\n", + ">>> my_generator = (letter for letter in 'abcdefg')\n", + ">>> next(my_generator)\n", + "'a'\n", + ">>> my_generator.next()\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "AttributeError: 'generator' object has no attribute 'next'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### In Python 3.x for-loop variables don't leak into the global namespace anymore" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This goes back to a change that was made in Python 3.x and is described in [What’s New In Python 3.0](https://docs.python.org/3/whatsnew/3.0.html) as follows:\n", + "\n", + "\"List comprehensions no longer support the syntactic form `[... for var in item1, item2, ...]`. Use `[... for var in (item1, item2, ...)]` instead. Also note that list comprehensions have different semantics: they are closer to syntactic sugar for a generator expression inside a `list()` constructor, and in particular the loop control variables are no longer leaked into the surrounding scope.\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This code cell was executed in Python 3.3.5\n", + "[0, 1, 2, 3, 4]\n", + "1 -> i in global\n" + ] + } + ], + "source": [ + "from platform import python_version\n", + "print('This code cell was executed in Python', python_version())\n", + "\n", + "i = 1\n", + "print([i for i in range(5)])\n", + "print(i, '-> i in global')" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This code cell was executed in Python 2.7.6\n", + "[0, 1, 2, 3, 4]\n", + "4 -> i in global\n" + ] + } + ], + "source": [ + "from platform import python_version\n", + "print 'This code cell was executed in Python', python_version()\n", + "\n", + "i = 1\n", + "print [i for i in range(5)]\n", + "print i, '-> i in global' " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Python 3.x prevents us from comparing unorderable types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to Python 2.x vs 3.x overview](#py23_overview)]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This code cell was executed in Python 2.7.6\n", + "False\n", + "True\n", + "False\n" + ] + } + ], + "source": [ + "from platform import python_version\n", + "print 'This code cell was executed in Python', python_version()\n", + "\n", + "print [1, 2] > 'foo'\n", + "print (1, 2) > 'foo'\n", + "print [1, 2] > (1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This code cell was executed in Python 3.3.5\n" + ] + }, + { + "ename": "TypeError", + "evalue": "unorderable types: list() > str()", + "output_type": "error", + "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 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'This code cell was executed in Python'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpython_version\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m'foo'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mTypeError\u001b[0m: unorderable types: list() > str()" + ] + } + ], + "source": [ + "from platform import python_version\n", + "print('This code cell was executed in Python', python_version())\n", + "\n", + "print([1, 2] > 'foo')\n", + "print((1, 2) > 'foo')\n", + "print([1, 2] > (1, 2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Function annotations - What are those `->`'s in my Python code?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Have you ever seen any Python code that used colons inside the parantheses of a function definition?" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def foo1(x: 'insert x here', y: 'insert x^2 here'):\n", + " print('Hello, World')\n", + " return" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And what about the fancy arrow here?" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def foo2(x, y) -> 'Hi!':\n", + " print('Hello, World')\n", + " return" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Q: Is this valid Python syntax? \n", + "A: Yes!\n", + " \n", + " \n", + "Q: So, what happens if I *just call* the function? \n", + "A: Nothing!\n", + " \n", + "Here is the proof!" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World\n" + ] + } + ], + "source": [ + "foo1(1,2)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World\n" + ] + } + ], + "source": [ + "foo2(1,2) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**So, those are function annotations ... ** \n", + "- the colon for the function parameters \n", + "- the arrow for the return value \n", + "\n", + "You probably will never make use of them (or at least very rarely). Usually, we write good function documentations below the function as a docstring - or at least this is how I would do it (okay this case is a little bit extreme, I have to admit):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def is_palindrome(a):\n", + " \"\"\"\n", + " Case-and punctuation insensitive check if a string is a palindrom.\n", + " \n", + " Keyword arguments:\n", + " a (str): The string to be checked if it is a palindrome.\n", + " \n", + " Returns `True` if input string is a palindrome, else False.\n", + " \n", + " \"\"\"\n", + " stripped_str = [l for l in my_str.lower() if l.isalpha()]\n", + " return stripped_str == stripped_str[::-1]\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, function annotations can be useful to indicate that work is still in progress in some cases. But they are optional and I see them very very rarely.\n", + "\n", + "As it is stated in [PEP3107](http://legacy.python.org/dev/peps/pep-3107/#fundamentals-of-function-annotations):\n", + "\n", + "1. Function annotations, both for parameters and return values, are completely optional.\n", + "\n", + "2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The nice thing about function annotations is their `__annotations__` attribute, which is dictionary of all the parameters and/or the `return` value you annotated." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'y': 'insert x^2 here', 'x': 'insert x here'}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "foo1.__annotations__" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'return': 'Hi!'}" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "foo2.__annotations__" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**When are they useful?**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function annotations can be useful for a couple of things \n", + "- Documentation in general\n", + "- pre-condition testing\n", + "- [type checking](http://legacy.python.org/dev/peps/pep-0362/#annotation-checker)\n", + " \n", + "..." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Abortive statements in `finally` blocks" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python's `try-except-finally` blocks are very handy for catching and handling errors. The `finally` block is always executed whether an `exception` has been raised or not as illustrated in the following example." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "in try:\n", + "do some stuff\n", + "an error occurred\n", + "always execute finally\n" + ] + } + ], + "source": [ + "def try_finally1():\n", + " try:\n", + " print('in try:')\n", + " print('do some stuff')\n", + " float('abc')\n", + " except ValueError:\n", + " print('an error occurred')\n", + " else:\n", + " print('no error occurred')\n", + " finally:\n", + " print('always execute finally')\n", + " \n", + "try_finally1()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "But can you also guess what will be printed in the next code cell?" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "do some stuff in try block\n", + "do some stuff in finally block\n", + "always execute finally\n" + ] + } + ], + "source": [ + "def try_finally2():\n", + " try:\n", + " print(\"do some stuff in try block\")\n", + " return \"return from try block\"\n", + " finally:\n", + " print(\"do some stuff in finally block\")\n", + " return \"always execute finally\"\n", + " \n", + "print(try_finally2())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "Here, the abortive `return` statement in the `finally` block simply overrules the `return` in the `try` block, since **`finally` is guaranteed to always be executed.** So, be careful using abortive statements in `finally` blocks!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#Assigning types to variables as values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I am not yet sure in which context this can be useful, but it is a nice fun fact to know that we can assign types as values to variables." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'123'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a_var = str\n", + "a_var(123)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 \n", + "1 \n", + "2.0 \n", + "3 \n", + "4 \n" + ] + } + ], + "source": [ + "from random import choice\n", + "\n", + "a, b, c = float, int, str\n", + "for i in range(5):\n", + " j = choice([a,b,c])(i)\n", + " print(j, type(j))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Only the first clause of generators is evaluated immediately" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The main reason why we love to use generators in certain cases (i.e., when we are dealing with large numbers of computations) is that it only computes the next value when it is needed, which is also known as \"lazy\" evaluation.\n", + "However, the first clause of an generator is already checked upon it's creation, as the following example demonstrates:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "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[0mgen_fails\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m1\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[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + } + ], + "source": [ + "gen_fails = (i for i in 1/0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Certainly, this is a nice feature, since it notifies us about syntax erros immediately. However, this is (unfortunately) not the case if we have multiple cases in our generator." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "gen_succeeds = (i for i in range(5) for j in 1/0)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "ename": "ZeroDivisionError", + "evalue": "division by zero", + "output_type": "error", + "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[1;32m 1\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'But obviously fails when we iterate ...'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgen_succeeds\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mgen_succeeds\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;36m1\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[0m", + "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "But obviously fails when we iterate ...\n" + ] + } + ], + "source": [ + "print('But obviously fails when we iterate ...')\n", + "for i in gen_succeeds:\n", + " print(i)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##Keyword argument unpacking syntax - `*args` and `**kwargs`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Python has a very convenient \"keyword argument unpacking syntax\" (often also referred to as \"splat\"-operators). This is particularly useful, if we want to define a function that can take a arbitrary number of input arguments." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Single-asterisk (*args)" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "type of args: \n", + "args contents: (0, 1, 'a', 'b', 'c')\n", + "1st argument: 0\n" + ] + } + ], + "source": [ + "def a_func(*args):\n", + " print('type of args:', type(args))\n", + " print('args contents:', args)\n", + " print('1st argument:', args[0])\n", + "\n", + "a_func(0, 1, 'a', 'b', 'c')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Double-asterisk (**kwargs)" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "type of kwargs: \n", + "kwargs contents: {'d': 4, 'a': 1, 'c': 3, 'b': 2}\n", + "value of argument a: 1\n" + ] + } + ], + "source": [ + "def b_func(**kwargs):\n", + " print('type of kwargs:', type(kwargs))\n", + " print('kwargs contents: ', kwargs)\n", + " print('value of argument a:', kwargs['a'])\n", + " \n", + "b_func(a=1, b=2, c=3, d=4)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### (Partially) unpacking of iterables\n", + "Another useful application of the \"unpacking\"-operator is the unpacking of lists and other other iterables." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "val1: 1\n", + "vals: [2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "val1, *vals = [1, 2, 3, 4, 5]\n", + "print('val1:', val1)\n", + "print('vals:', vals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Metaclasses - What creates a new instance of a class?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Usually, it is the `__init__` method when we think of instanciating a new object from a class. However, it is the static method `__new__` (it is not a class method!) that creates and returns a new instance before `__init__()` is called. \n", + "More specifically, this is what is returned: \n", + "`return super(, cls).__new__(subcls, *args, **kwargs)` \n", + "\n", + "For more information about the `__new__` method, please see the [documentation](https://www.python.org/download/releases/2.2/descrintro/#__new__).\n", + "\n", + "As a little experiment, let us screw with `__new__` so that it returns `None` and see if `__init__` will be executed:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "excecuted __new__\n", + "Type of a_object: \n" + ] + } + ], + "source": [ + "class a_class(object):\n", + " def __new__(clss, *args, **kwargs):\n", + " print('excecuted __new__')\n", + " return None\n", + " def __init__(self, an_arg):\n", + " print('excecuted __init__')\n", + " self.an_arg = an_arg\n", + " \n", + "a_object = a_class(1)\n", + "print('Type of a_object:', type(a_object))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we can see in the code above, `__init__` requires the returned instance from `__new__` in order to called. So, here we just created a `NoneType` object. \n", + "Let us override the `__new__`, now and let us confirm that `__init__` is called now to instantiate the new object\":" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "excecuted __new__\n", + "excecuted __init__\n", + "Type of a_object: \n", + "a_object.an_arg: 1\n" + ] + } + ], + "source": [ + "class a_class(object):\n", + " def __new__(cls, *args, **kwargs):\n", + " print('excecuted __new__')\n", + " inst = super(a_class, cls).__new__(cls)\n", + " return inst\n", + " def __init__(self, an_arg):\n", + " print('excecuted __init__')\n", + " self.an_arg = an_arg\n", + " \n", + "a_object = a_class(1)\n", + "print('Type of a_object:', type(a_object))\n", + "print('a_object.an_arg: ', a_object.an_arg)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "in for\n", + "in else\n", + "after for-loop\n" + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 1:\n", + " print('in for')\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "after for-loop\n" + ] + } + ], + "source": [ + "for i in range(5):\n", + " if i == 1:\n", + " break\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Else-clauses: \"conditional else\" and \"completion else\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I would claim that the conditional \"else\" is every programmer's daily bread and butter. However, there is a second flavor of \"else\"-clauses in Python, which I will call \"completion else\" (for reason that will become clear later). \n", + "But first, let us take a look at our \"traditional\" conditional else that we all are familiar with. \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "###Conditional else:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 1:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Bye, World!\n" + ] + } + ], + "source": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 2:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Why am I showing those simple examples? I think they are good to highlight some of the key points: It is **either** the code under the `if` clause that is executed, **or** the code under the `else` block, but not both. \n", + "If the condition of the `if` clause evaluates to `True`, the `if`-block is exectured, and if it evaluated to `False`, it is the `else` block. \n", + "\n", + "### Completion else\n", + "**In contrast** to the **either...or*** situation that we know from the conditional `else`, the completion `else` is executed if a code block finished. \n", + "To show you an example, let us use `else` for error-handling:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (try-except)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first element: 1\n", + "no error in try-block\n" + ] + } + ], + "source": [ + "try:\n", + " print('first element:', a_list[0])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "raised IndexError\n" + ] + } + ], + "source": [ + "try:\n", + " print('third element:', a_list[2])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "In the code above, we can see that the code under the **`else`-clause is only executed if the `try-block` was executed without encountering an error, i.e., if the `try`-block is \"complete\".** \n", + "The same rule applies to the \"completion\" `else` in while- and for-loops, which you can confirm in the following samples below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (while-loop)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "in else\n" + ] + } + ], + "source": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + "else:\n", + " print('in else')" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + " break\n", + "else:\n", + " print('completed while-loop')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (for-loop)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "completed for-loop\n" + ] + } + ], + "source": [ + "for i in range(2):\n", + " print(i)\n", + "else:\n", + " print('completed for-loop')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "for i in range(2):\n", + " print(i)\n", + " break\n", + "else:\n", + " print('completed for-loop')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interning of compile-time constants vs. run-time expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This might not be particularly useful, but it is nonetheless interesting: Python's interpreter is interning compile-time constants but not run-time expressions (note that this is implementation-specific).\n", + "\n", + "(Original source: [Stackoverflow](http://stackoverflow.com/questions/15541404/python-string-interning))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us have a look at the simple example below. Here we are creating 3 variables and assign the value \"Hello\" to them in different ways before we test them for identity." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello1 is hello2: True\n", + "hello1 is hello3: False\n" + ] + } + ], + "source": [ + "hello1 = 'Hello'\n", + "\n", + "hello2 = 'Hell' + 'o'\n", + "\n", + "hello3 = 'Hell'\n", + "hello3 = hello3 + 'o'\n", + "\n", + "print('hello1 is hello2:', hello1 is hello2)\n", + "print('hello1 is hello3:', hello1 is hello3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, how does it come that the first expression evaluates to true, but the second does not? To answer this question, we need to take a closer look at the underlying byte codes:" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 3 0 LOAD_CONST 1 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 4 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "source": [ + "import dis\n", + "def hello1_func():\n", + " s = 'Hello'\n", + " return s\n", + "dis.dis(hello1_func)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 2 0 LOAD_CONST 3 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "source": [ + "def hello2_func():\n", + " s = 'Hell' + 'o'\n", + " return s\n", + "dis.dis(hello2_func)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " 2 0 LOAD_CONST 1 ('Hell')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 LOAD_CONST 2 ('o')\n", + " 12 BINARY_ADD\n", + " 13 STORE_FAST 0 (s)\n", + "\n", + " 4 16 LOAD_FAST 0 (s)\n", + " 19 RETURN_VALUE\n" + ] + } + ], + "source": [ + "def hello3_func():\n", + " s = 'Hell'\n", + " s = s + 'o'\n", + " return s\n", + "dis.dis(hello3_func)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "It looks like that `'Hello'` and `'Hell'` + `'o'` are both evaluated and stored as `'Hello'` at compile-time, whereas the third version \n", + "`s = 'Hell'` \n", + "`s = s + 'o'` seems to be not interned. Let us quickly confirm the behavior with the following code:" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "False\n" + ] + } + ], + "source": [ + "print(hello1_func() is hello2_func())\n", + "print(hello1_func() is hello3_func())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, to show that this hypothesis is the answer to this rather unexpected observation, let us `intern` the value manually:" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "import sys\n", + "\n", + "print(hello1_func() is sys.intern(hello3_func()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
\n", + "
\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Changelog" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 07/16/2014\n", + "- slight change of wording in the [lambda-closure section](#lambda_closure)\n", + "\n", + "#### 05/24/2014\n", + "- new section: unorderable types in Python 2\n", + "- table of contents for the Python 2 vs. Python 3 topic\n", + " \n", + "#### 05/03/2014\n", + "- new section: else clauses: conditional vs. completion\n", + "- new section: Interning of compile-time constants vs. run-time expressions\n", + "\n", + "#### 05/02/2014\n", + "- new section in Python 3.x and Python 2.x key differences: for-loop leak\n", + "- new section: Metaclasses - What creates a new instance of a class? \n", + "\n", + "#### 05/01/2014\n", + "- new section: keyword argument unpacking syntax\n", + "\n", + "#### 04/27/2014\n", + "- minor fixes of typos \n", + "- new section: \"Only the first clause of generators is evaluated immediately\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.0" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/tutorials/scope_resolution_legb_rule.ipynb b/tutorials/scope_resolution_legb_rule.ipynb index 18ff06c..58adb8a 100644 --- a/tutorials/scope_resolution_legb_rule.ipynb +++ b/tutorials/scope_resolution_legb_rule.ipynb @@ -1151,7 +1151,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.1" + "version": "3.5.0" } }, "nbformat": 4,