\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": {},
@@ -3936,6 +4281,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
+ "#### 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",
diff --git a/tutorials/key_differences_between_python_2_and_3.md b/tutorials/key_differences_between_python_2_and_3.md
new file mode 100644
index 0000000..d7a7d78
--- /dev/null
+++ b/tutorials/key_differences_between_python_2_and_3.md
@@ -0,0 +1,411 @@
+[Sebastian Raschka](http://sebastianraschka.com)
+last updated: 05/24/2014
+
+
+
+**This is a subsection of ["A collection of not-so-obvious Python stuff you should know!"](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/not_so_obvious_python_stuff.ipynb?create=1)**
+
+
+
+
+
+## Key differences between Python 2 and 3
+
+
+There are some good articles already that are summarizing the differences between Python 2 and 3, e.g.,
+- [https://wiki.python.org/moin/Python2orPython3](https://wiki.python.org/moin/Python2orPython3)
+- [https://docs.python.org/3.0/whatsnew/3.0.html](https://docs.python.org/3.0/whatsnew/3.0.html)
+- [http://python3porting.com/differences.html](http://python3porting.com/differences.html)
+- [https://docs.python.org/3/howto/pyporting.html](https://docs.python.org/3/howto/pyporting.html)
+etc.
+
+But it might be still worthwhile, especially for Python newcomers, to take a look at some of those!
+(Note: the the code was executed in Python 3.4.0 and Python 2.7.5 and copied from interactive shell sessions.)
+
+
+
+
+
+### Overview - Key differences between Python 2 and 3
+
+
+
+
+- [Unicode](#unicode)
+- [The print statement](#print)
+- [Integer division](#integer_div)
+- [xrange()](#xrange)
+- [Raising exceptions](#raising_exceptions)
+- [Handling exceptions](#handling_exceptions)
+- [next() function and .next() method](#next_next)
+- [Loop variables and leaking into the global scope](#loop_leak)
+- [Comparing unorderable types](#compare_unorder)
+
+
+
+
+
+
+
+
+
+### Unicode...
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+####- Python 2:
+We have ASCII `str()` types, separate `unicode()`, but no `byte` type
+####- Python 3:
+Now, we finally have Unicode (utf-8) `str`ings, and 2 byte classes: `byte` and `bytearray`s
+
+
+
+#############
+# Python 2
+#############
+
+>>> type(unicode('is like a python3 str()'))
+<type 'unicode'>
+
+>>> type(b'byte type does not exist')
+<type 'str'>
+
+>>> 'they are really' + b' the same'
+'they are really the same'
+
+>>> type(bytearray(b'bytearray oddly does exist though'))
+<type 'bytearray'>
+
+#############
+# Python 3
+#############
+
+>>> print('strings are now utf-8 \u03BCnico\u0394é!')
+strings are now utf-8 μnicoΔé!
+
+
+>>> type(b' and we have byte types for storing data')
+<class 'bytes'>
+
+>>> type(bytearray(b'but also bytearrays for those who prefer them over strings'))
+<class 'bytearray'>
+
+>>> 'string' + b'bytes for data'
+Traceback (most recent call last):s
+ File "<stdin>", line 1, in <module>
+TypeError: Can't convert 'bytes' object to str implicitly
+
+
+
+
+
+
+
+### The print statement
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+Very trivial, but this change makes sense, Python 3 now only accepts `print`s with proper parentheses - just like the other function calls ...
+
+
+# Python 2
+>>> print 'Hello, World!'
+Hello, World!
+>>> print('Hello, World!')
+Hello, World!
+
+# Python 3
+>>> print('Hello, World!')
+Hello, World!
+>>> print 'Hello, World!'
+ File "<stdin>", line 1
+ print 'Hello, World!'
+ ^
+SyntaxError: invalid syntax
+
+
+
+
+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:
+
+
+
+# Python 2
+>>> print "line 1", ; print 'same line'
+line 1 same line
+
+# Python 3
+>>> print("line 1", end="") ; print (" same line")
+line 1 same line
+
+
+
+
+
+
+
+### Integer division
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+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.
+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).
+
+
+# Python 2
+>>> 3 / 2
+1
+>>> 3 // 2
+1
+>>> 3 / 2.0
+1.5
+>>> 3 // 2.0
+1.0
+
+# Python 3
+>>> 3 / 2
+1.5
+>>> 3 // 2
+1
+>>> 3 / 2.0
+1.5
+>>> 3 // 2.0
+1.0
+
+
+
+
+
+
+
+###`xrange()`
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+`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!
+In Python 3, the `range()` was implemented like the `xrange()` function so that a dedicated `xrange()` function does not exist anymore.
+
+
+# Python 2
+> python -m timeit 'for i in range(1000000):' ' pass'
+10 loops, best of 3: 66 msec per loop
+
+ > python -m timeit 'for i in xrange(1000000):' ' pass'
+10 loops, best of 3: 27.8 msec per loop
+
+# Python 3
+> python3 -m timeit 'for i in range(1000000):' ' pass'
+10 loops, best of 3: 51.1 msec per loop
+
+> python3 -m timeit 'for i in xrange(1000000):' ' pass'
+Traceback (most recent call last):
+ File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py", line 292, in main
+ x = t.timeit(number)
+ File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/timeit.py", line 178, in timeit
+ timing = self.inner(it, self.timer)
+ File "<timeit-src>", line 6, in inner
+ for i in xrange(1000000):
+NameError: name 'xrange' is not defined
+
+
+
+
+
+
+
+### Raising exceptions
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+
+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:
+
+
+# Python 2
+>>> raise IOError, "file error"
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+IOError: file error
+>>> raise IOError("file error")
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+IOError: file error
+
+
+# Python 3
+>>> raise IOError, "file error"
+ File "<stdin>", line 1
+ raise IOError, "file error"
+ ^
+SyntaxError: invalid syntax
+>>> raise IOError("file error")
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+OSError: file error
+
+
+
+
+
+
+
+### Handling exceptions
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+
+Also the handling of exceptions has slightly changed in Python 3. Now, we have to use the `as` keyword!
+
+# Python 2
+>>> try:
+... blabla
+... except NameError, err:
+... print err, '--> our error msg'
+...
+name 'blabla' is not defined --> our error msg
+
+# Python 3
+>>> try:
+... blabla
+... except NameError as err:
+... print(err, '--> our error msg')
+...
+name 'blabla' is not defined --> our error msg
+
+
+
+
+
+
+
+### The `next()` function and `.next()` method
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+Where you can use both function and method in Python 2.7.5, the `next()` function is all that remain in Python 3!
+
+# Python 2
+>>> my_generator = (letter for letter in 'abcdefg')
+>>> my_generator.next()
+'a'
+>>> next(my_generator)
+'b'
+
+# Python 3
+>>> my_generator = (letter for letter in 'abcdefg')
+>>> next(my_generator)
+'a'
+>>> my_generator.next()
+Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+AttributeError: 'generator' object has no attribute 'next'
+
+
+
+
+
+
+
+### In Python 3.x for-loop variables don't leak into the global namespace anymore
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+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:
+
+"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."
+
+
+`[In:]`
+from platform import python_version
+print('This code cell was executed in Python', python_version())
+
+i = 1
+print([i for i in range(5)])
+print(i, '-> i in global')
+
+
+
+`[Out:]`
+This code cell was executed in Python 3.3.5
+[0, 1, 2, 3, 4]
+1 -> i in global
+
+
+
+
+
+
+`[In:]`
+from platform import python_version
+print 'This code cell was executed in Python', python_version()
+
+i = 1
+print [i for i in range(5)]
+print i, '-> i in global'
+
+
+
+`[Out:]`
+This code cell was executed in Python 2.7.6
+[0, 1, 2, 3, 4]
+4 -> i in global
+
+
+
+
+
+
+
+#### Python 3.x prevents us from comparing unorderable types
+
+[[back to Python 2.x vs 3.x overview](#py23_overview)]
+
+
+`[In:]`
+from platform import python_version
+print 'This code cell was executed in Python', python_version()
+
+print [1, 2] > 'foo'
+print (1, 2) > 'foo'
+print [1, 2] > (1, 2)
+
+
+
+`[Out:]`
+This code cell was executed in Python 2.7.6
+False
+True
+False
+
+
+
+
+
+
+`[In:]`
+from platform import python_version
+print('This code cell was executed in Python', python_version())
+
+print([1, 2] > 'foo')
+print((1, 2) > 'foo')
+print([1, 2] > (1, 2))
+
+
+`[Out:]`
+This code cell was executed in Python 3.3.5
+---------------------------------------------------------------------------
+TypeError Traceback (most recent call last)
+<ipython-input-3-1d774c677f73> in <module>()
+ 2 print('This code cell was executed in Python', python_version())
+ 3
+----> 4 [1, 2] > 'foo'
+ 5 (1, 2) > 'foo'
+ 6 [1, 2] > (1, 2)
+
+TypeError: unorderable types: list() > str()
+