diff --git a/README.md b/README.md index db00ca9..feebdc1 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A collection of useful scripts, tutorials, and other Python-related things - Python's scope resolution for variable names and the LEGB rule [[IPython nb](http://nbviewer.ipython.org/github/rasbt/python_reference/blob/master/tutorials/scope_resolution_legb_rule.ipynb?create=1)] -- Key differences between Python 2.x and Python 3.x [[Markdown](./tutorials/key_differences_python2_python3.md)] +- Key differences between Python 2.x and Python 3.x [[HTML](http://htmlpreview.github.io/?https://github.com/rasbt/python_reference/blob/master/tutorials/key_differences_between_python_2_and_3.html)] - A thorough guide to SQLite database operations in Python [[Markdown](./sqlite3_howto/README.md)] diff --git a/tutorials/key_differences_between_python_2_and_3.html b/tutorials/key_differences_between_python_2_and_3.html new file mode 100644 index 0000000..431d7c9 --- /dev/null +++ b/tutorials/key_differences_between_python_2_and_3.html @@ -0,0 +1,632 @@ + + +
+ + + +Sebastian Raschka
+last updated: 05/24/2014
This is a subsection of "A collection of not-so-obvious Python stuff you should know!"
+ + + +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://docs.python.org/3.0/whatsnew/3.0.html
+- http://python3porting.com/differences.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.)
+ + + +
+
[back to Python 2.x vs 3.x overview]
+ +We have ASCII str()
types, separate unicode()
, but no byte
type
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 +
[back to Python 2.x vs 3.x 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 +
[back to Python 2.x vs 3.x 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]
+ +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 +
[back to Python 2.x vs 3.x 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 +
[back to Python 2.x vs 3.x 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 +
next()
function and .next()
method[back to Python 2.x vs 3.x 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' +
[back to Python 2.x vs 3.x 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 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 +
[back to Python 2.x vs 3.x 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() +