許多 Python 初學者想知道他們應該從 Python 的哪個版本開始學習。對於這個問題我的答案是 “你學習你喜歡的教程的版本,然後檢查他們之間的不同。"
但是如果你開始一個新項目,並且有選擇權?我想說的是目前沒有對錯,只要你計劃使用的庫 Python 2.7.x 和 Python 3.x 雙方都支持的話。儘管如此,當在編寫它們中的任何一個的代碼,或者是你計劃移植你的項目的時候,是非常值得看看這兩個主要流行的 Python 版本之間的差別的,以便避免常見的陷阱,
章節
-
使用
__future__
模塊 -
print
函數 - Integer division
- Unicode
- xrange
- Raising exceptions
- Handling exceptions
-
next()
函數 和.next()
方法 - For 循環變量和全局命名空間泄漏
- 比較不可排序類型
-
通過
input()
解析用戶的輸入 - 返回可迭代對象,而不是列表
- 更多的關於 Python 2 和 Python 3 的文章
future模塊
Python 3.x 介紹的 一些Python 2 不兼容的關鍵字和特性可以通過在 Python 2 的內置__future__
模塊導入。如果你計劃讓你的代碼支持
Python 3.x,建議你使用__future__
模塊導入。例如,如果我想要 在Python 2 中表現 Python 3.x
中的整除,我們可以通過如下導入
from __future__ import division
更多的__future__
模塊可被導入的特性被列在下表中:
feature | optional in | mandatory in | effect |
---|---|---|---|
nested_scopes | 2.1.0b1 | 2.2 | PEP 255: Simple Generators |
division | 2.2.0a2 | 3.0 | PEP 328: Imports: Multi-Line and Absolute/Relative |
with_statement | 2.5.0a1 | 2.6 | PEP 3105: Make print a function |
unicode_literals | 2.6.0a2 | 3.0 | <a rel="nofollow" href="http://legacy.python.org/dev/peps/pep-3112/" """="" target="_blank" style="color: rgb(128, 0, 128); text-decoration: none; outline-style: none;">PEP 3112: Bytes literals in Python 3000 |
(Source:https://docs.python.org/2/library/future.html)
from platform import python_version
print函數
很瑣碎,而print
語法的變化可能是最廣爲人知的了,但是仍值得一提的是: Python 2 的print
聲明已經被print()
函數取代了,這意味着我們必須包裝我們想打印在小括號中的對象。
Python 2 不具有額外的小括號問題。但對比一下,如果我們按照 Python 2 的方式不使用小括號調用print
函數,Python
3 將拋出一個語法異常(SyntaxError
)。
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 3
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
Unicode
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 u03BCnicou0394é!') 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
xrange
在 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 'nntiming 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
Raising exceptions
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
Handling exceptions
在 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() 函數 and .next() 方法
因爲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'
For 循環變量和全局命名空間泄漏
好消息:在 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 2 和 Python 3 的一些好文章。
移植到 Python 3
Python 3 的擁護者和反對者