注:本文的原文地址为
Key differences between Python 2.7.x and Python 3.xhtml
许多 Python 初学者想知道他们应该从 Python 的哪一个版本开始学习。对于这个问题个人答案是 “你学习你喜欢的教程的版本,而后检查他们之间的不一样。"python
可是若是你开始一个新项目,而且有选择权?我想说的是目前没有对错,只要你计划使用的库 Python 2.7.x 和 Python 3.x 双方都支持的话。尽管如此,当在编写它们中的任何一个的代码,或者是你计划移植你的项目的时候,是很是值得看看这两个主要流行的 Python 版本之间的差异的,以便避免常见的陷阱,git
__future__
模块print
函数next()
函数 和 .next()
方法input()
解析用户的输入Python 3.x 介绍的 一些Python 2 不兼容的关键字和特性能够经过在 Python 2 的内置 __future__
模块导入。若是你计划让你的代码支持 Python 3.x,建议你使用 __future__
模块导入。例如,若是我想要 在Python 2 中表现 Python 3.x 中的整除,咱们能够经过以下导入github
from __future__ import division
更多的 __future__
模块可被导入的特性被列在下表中:app
feature | optional in | mandatory in | effect |
---|---|---|---|
nested_scopes | 2.1.0b1 | 2.2 | PEP 227: Statically Nested Scopes |
generators | 2.2.0a1 | 2.3 | PEP 255: Simple Generators |
division | 2.2.0a2 | 3.0 | PEP 238: Changing the Division Operator |
absolute_import | 2.5.0a1 | 3.0 | PEP 328: Imports: Multi-Line and Absolute/Relative |
with_statement | 2.5.0a1 | 2.6 | PEP 343: The “with” Statement |
print_function | 2.6.0a2 | 3.0 | PEP 3105: Make print a function |
unicode_literals | 2.6.0a2 | 3.0 | PEP 3112: Bytes literals in Python 3000 |
(Source: https://docs.python.org/2/library/future.html)ide
from platform import python_version
很琐碎,而 print
语法的变化多是最广为人知的了,可是仍值得一提的是: Python 2 的 print
声明已经被 print()
函数取代了,这意味着咱们必须包装咱们想打印在小括号中的对象。
Python 2 不具备额外的小括号问题。但对比一下,若是咱们按照 Python 2 的方式不使用小括号调用 print
函数,Python 3 将抛出一个语法异常(SyntaxError
)。oop
Python 2学习
print 'Python', python_version() print 'Hello, World!' print('Hello, World!') print "text", ; print 'print more text on the same line'
Python 2.7.6 Hello, World! Hello, World! text print more text on the same line
Python 3ui
print('Python', python_version()) print('Hello, World!') print("some text,", end="") print(' print more text on the same line')
Python 3.4.1 Hello, World! some text, print more text on the same line
print 'Hello, World!'
File "<ipython-input-3-139a7c5835bd>", line 1 print 'Hello, World!' ^ SyntaxError: invalid syntax
注意
以上经过 Python 2 使用 Printing "Hello, World"
是很是正常的,尽管如此,若是你有多个对象在小括号中,咱们将建立一个元组,由于 print
在 Python 2 中是一个声明,而不是一个函数调用。
print 'Python', python_version() print('a', 'b') print 'a', 'b'
Python 2.7.7 ('a', 'b') a b
若是你正在移植代码,这个变化是特别危险的。或者你在 Python 2 上执行 Python 3 的代码。由于这个整除的变化表如今它会被忽视(即它不会抛出语法异常)。
所以,我仍是倾向于使用一个 float(3)/2
或 3/2.0
代替在个人 Python 3 脚本保存在 Python 2 中的 3/2
的一些麻烦(而且反而过来也同样,我建议在你的 Python 2 脚本中使用 from __future__ import division
)
Python 2
print 'Python', python_version() print '3 / 2 =', 3 / 2 print '3 // 2 =', 3 // 2 print '3 / 2.0 =', 3 / 2.0 print '3 // 2.0 =', 3 // 2.0
Python 2.7.6 3 / 2 = 1 3 // 2 = 1 3 / 2.0 = 1.5 3 // 2.0 = 1.0
Python 3
print('Python', python_version()) print('3 / 2 =', 3 / 2) print('3 // 2 =', 3 // 2) print('3 / 2.0 =', 3 / 2.0) print('3 // 2.0 =', 3 // 2.0)
Python 3.4.1 3 / 2 = 1.5 3 // 2 = 1 3 / 2.0 = 1.5 3 // 2.0 = 1.0
Python 2 有 ASCII str()
类型,unicode()
是单独的,不是 byte 类型。
如今, 在 Python 3,咱们最终有了 Unicode (utf-8)
字符串,以及一个字节类:byte
和 bytearrays
。
Python 2
print 'Python', python_version()
Python 2.7.6
print type(unicode('this is like a python3 str type')) <type 'unicode'>
print type(b'byte type does not exist') <type 'str'>
print 'they are really' + b' the same' they are really the same
print type(bytearray(b'bytearray oddly does exist though')) <type 'bytearray'>
Python 3
print('Python', python_version()) print('strings are now utf-8 \u03BCnico\u0394é!') Python 3.4.1 strings are now utf-8 μnicoΔé!
print('Python', python_version(), end="") print(' has', type(b' bytes for storing data')) Python 3.4.1 has <class 'bytes'>
print('and Python', python_version(), end="") print(' also has', type(bytearray(b'bytearrays'))) and Python 3.4.1 also has <class 'bytearray'>
'note that we cannot add a string' + b'bytes for data' --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-13-d3e8942ccf81> in <module>() ----> 1 'note that we cannot add a string' + b'bytes for data' TypeError: Can't convert 'bytes' object to str implicitly
在 Python 2 中 xrange()
建立迭代对象的用法是很是流行的。好比: for 循环或者是列表/集合/字典推导式。
这个表现十分像生成器(好比。“惰性求值”)。可是这个 xrange-iterable
是无穷的,意味着你能够无限遍历。
因为它的惰性求值,若是你不得仅仅不遍历它一次,xrange()
函数 比 range()
更快(好比 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代屡次,由于生成器每次都从头开始。
在 Python 3 中,range()
是像 xrange()
那样实现以致于一个专门的 xrange()
函数都再也不存在(在 Python 3 中 xrange()
会抛出命名异常)。
import timeit n = 10000 def test_range(n): return for i in range(n): pass def test_xrange(n): for i in xrange(n): pass
Python 2
print 'Python', python_version() print '\ntiming range()' %timeit test_range(n) print '\n\ntiming xrange()' %timeit test_xrange(n) Python 2.7.6 timing range() 1000 loops, best of 3: 433 µs per loop timing xrange() 1000 loops, best of 3: 350 µs per loop
Python 3
print('Python', python_version()) print('\ntiming range()') %timeit test_range(n) Python 3.4.1 timing range() 1000 loops, best of 3: 520 µs per loop
print(xrange(10)) --------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-5-5d8f9b79ea70> in <module>() ----> 1 print(xrange(10)) NameError: name 'xrange' is not defined
Python 3 中的 range
对象的 __contains__
方法
另一件值得一提的事情就是在 Python 3 中 range
有一个新的 __contains__
方法(感谢 Yuchen Ying 指出了这个),__contains__
方法能够加速 "查找"
在 Python 3.x 中显著的整数和布尔类型。
x = 10000000 def val_in_range(x, val): return val in range(x) def val_in_xrange(x, val): return val in xrange(x) print('Python', python_version()) assert(val_in_range(x, x/2) == True) assert(val_in_range(x, x//2) == True) %timeit val_in_range(x, x/2) %timeit val_in_range(x, x//2) Python 3.4.1 1 loops, best of 3: 742 ms per loop 1000000 loops, best of 3: 1.19 µs per loop
基于以上的 timeit
的结果,当它使一个整数类型,而不是浮点类型的时候,你能够看到执行查找的速度是 60000 倍快。尽管如此,由于 Python 2.x 的 range
或者是 xrange
没有一个 __contains__
方法,这个整数类型或者是浮点类型的查询速度不会相差太大。
print 'Python', python_version() assert(val_in_xrange(x, x/2.0) == True) assert(val_in_xrange(x, x/2) == True) assert(val_in_range(x, x/2) == True) assert(val_in_range(x, x//2) == True) %timeit val_in_xrange(x, x/2.0) %timeit val_in_xrange(x, x/2) %timeit val_in_range(x, x/2.0) %timeit val_in_range(x, x/2) Python 2.7.7 1 loops, best of 3: 285 ms per loop 1 loops, best of 3: 179 ms per loop 1 loops, best of 3: 658 ms per loop 1 loops, best of 3: 556 ms per loop
下面说下 __contain__
方法并无加入到 Python 2.x 中的证据:
print('Python', python_version()) range.__contains__ Python 3.4.1 <slot wrapper '__contains__' of 'range' objects>
print 'Python', python_version() range.__contains__ Python 2.7.7 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-7-05327350dafb> in <module>() 1 print 'Python', python_version() ----> 2 range.__contains__ AttributeError: 'builtin_function_or_method' object has no attribute '__contains__'
print 'Python', python_version() xrange.__contains__ Python 2.7.7 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-8-7d1a71bfee8e> in <module>() 1 print 'Python', python_version() ----> 2 xrange.__contains__ AttributeError: type object 'xrange' has no attribute '__contains__'
**注意在 Python 2 和 Python 3 中速度的不一样***
有些猿类指出了 Python 3 的 range()
和 Python 2 的 xrange()
之间的速度不一样。由于他们是用相同的方法实现的,所以指望相同的速度。尽管如此,这事实在于 Python 3 倾向于比 Python 2 运行的慢一点。
def test_while(): i = 0 while i < 20000: i += 1 return
print('Python', python_version()) %timeit test_while() Python 3.4.1 100 loops, best of 3: 2.68 ms per loop
print 'Python', python_version() %timeit test_while() Python 2.7.6 1000 loops, best of 3: 1.72 ms per loop
Python 2 接受新旧两种语法标记,在 Python 3 中若是我不用小括号把异常参数括起来就会阻塞(而且反过来引起一个语法异常)。
Python 2
print 'Python', python_version() Python 2.7.6
raise IOError, "file error" --------------------------------------------------------------------------- IOError Traceback (most recent call last) <ipython-input-8-25f049caebb0> in <module>() ----> 1 raise IOError, "file error" IOError: file error
raise IOError("file error") --------------------------------------------------------------------------- IOError Traceback (most recent call last) <ipython-input-9-6f1c43f525b2> in <module>() ----> 1 raise IOError("file error") IOError: file error
Python 3
print('Python', python_version()) Python 3.4.1
raise IOError, "file error"
File "<ipython-input-10-25f049caebb0>", line 1 raise IOError, "file error" ^ SyntaxError: invalid syntax The proper way to raise an exception in Python 3:
print('Python', python_version()) raise IOError("file error") Python 3.4.1 --------------------------------------------------------------------------- OSError Traceback (most recent call last) <ipython-input-11-c350544d15da> in <module>() 1 print('Python', python_version()) ----> 2 raise IOError("file error") OSError: file error
在 Python 3 中处理异常也轻微的改变了,在 Python 3 中咱们如今使用 as
做为关键词。
python 2
print 'Python', python_version() try: let_us_cause_a_NameError except NameError, err: print err, '--> our error message' Python 2.7.6 name 'let_us_cause_a_NameError' is not defined --> our error message
Python 3
print('Python', python_version()) try: let_us_cause_a_NameError except NameError as err: print(err, '--> our error message') Python 3.4.1 name 'let_us_cause_a_NameError' is not defined --> our error message
由于 next() (.next())
是一个如此普通的使用函数(方法),这里有另一个语法改变(或者是实现上改变了),值得一提的是:在 Python 2.7.5 中函数和方法你均可以使用,next()
函数在 Python 3 中一直保留着(调用 .next()
抛出属性异常)。
Python 2
print 'Python', python_version() my_generator = (letter for letter in 'abcdefg') next(my_generator) my_generator.next()
Python 2.7.6 'b'
Python 3
print('Python', python_version()) my_generator = (letter for letter in 'abcdefg') next(my_generator)
Python 3.4.1 'a'
my_generator.next()
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-14-125f388bb61b> in <module>() ----> 1 my_generator.next() AttributeError: 'generator' object has no attribute 'next'
好消息:在 Python 3.x 中 for 循环变量不会再致使命名空间泄漏。
在 Python 3.x 中作了一个改变,在 What’s New In Python 3.0 中有以下描述:
"列表推导再也不支持 [... for var in item1, item2, ...] 这样的语法。使用 [... for var in (item1, item2, ...)] 代替。也须要提醒的是列表推导有不一样的语义: 他们关闭了在 `list()` 构造器中的生成器表达式的语法糖, 而且特别是循环控制变量再也不泄漏进周围的做用范围域."
Python 2
print 'Python', python_version() i = 1 print 'before: i =', i print 'comprehension: ', [i for i in range(5)] print 'after: i =', i Python 2.7.6 before: i = 1 comprehension: [0, 1, 2, 3, 4] after: i = 4
Python 3
print('Python', python_version()) i = 1 print('before: i =', i) print('comprehension:', [i for i in range(5)]) print('after: i =', i) Python 3.4.1 before: i = 1 comprehension: [0, 1, 2, 3, 4] after: i = 1
在 Python 3 中的另一个变化就是当对不可排序类型作比较的时候,会抛出一个类型错误。
Python 2
print 'Python', python_version() print "[1, 2] > 'foo' = ", [1, 2] > 'foo' print "(1, 2) > 'foo' = ", (1, 2) > 'foo' print "[1, 2] > (1, 2) = ", [1, 2] > (1, 2) Python 2.7.6 [1, 2] > 'foo' = False (1, 2) > 'foo' = True [1, 2] > (1, 2) = False
Python 3
print('Python', python_version()) print("[1, 2] > 'foo' = ", [1, 2] > 'foo') print("(1, 2) > 'foo' = ", (1, 2) > 'foo') print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2)) Python 3.4.1 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-16-a9031729f4a0> in <module>() 1 print('Python', python_version()) ----> 2 print("[1, 2] > 'foo' = ", [1, 2] > 'foo') 3 print("(1, 2) > 'foo' = ", (1, 2) > 'foo') 4 print("[1, 2] > (1, 2) = ", [1, 2] > (1, 2)) TypeError: unorderable types: list() > str()
input()
解析用户的输入幸运的是,在 Python 3 中已经解决了把用户的输入存储为一个 str
对象的问题。为了不在 Python 2 中的读取非字符串类型的危险行为,咱们不得不使用 raw_input()
代替。
Python 2
Python 2.7.6 [GCC 4.0.1 (Apple Inc. build 5493)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> my_input = input('enter a number: ') enter a number: 123 >>> type(my_input) <type 'int'> >>> my_input = raw_input('enter a number: ') enter a number: 123 >>> type(my_input) <type 'str'>
Python 3
Python 3.4.1 [GCC 4.2.1 (Apple Inc. build 5577)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> my_input = input('enter a number: ') enter a number: 123 >>> type(my_input) <class 'str'>
若是在 xrange 章节看到的,如今在 Python 3 中一些方法和函数返回迭代对象 -- 代替 Python 2 中的列表
由于咱们一般那些遍历只有一次,我认为这个改变对节约内存颇有意义。尽管如此,它也是可能的,相对于生成器 --- 如须要遍历屡次。它是不那么高效的。
而对于那些状况下,咱们真正须要的是列表对象,咱们能够经过 list()
函数简单的把迭代对象转换成一个列表。
Python 2
print 'Python', python_version() print range(3) print type(range(3)) Python 2.7.6 [0, 1, 2] <type 'list'>
Python 3
print('Python', python_version()) print(range(3)) print(type(range(3))) print(list(range(3))) Python 3.4.1 range(0, 3) <class 'range'> [0, 1, 2]
在 Python 3 中一些常用到的再也不返回列表的函数和方法:
zip()
map()
filter()
dictionary's .keys() method
dictionary's .values() method
dictionary's .items() method
下面是我建议后续的关于 Python 2 和 Python 3 的一些好文章。
移植到 Python 3
Python 3 的拥护者和反对者