注:本文的原文地址為
http://segmentfault.com/a/1190000000618286
許多 Python 初學(xué)者想知道他們應(yīng)該從 Python 的哪個(gè)版本開始學(xué)習(xí)。對(duì)于這個(gè)問題我的答案是 “你學(xué)習(xí)你喜歡的教程的版本,然后檢查他們之間的不同。"
但是如果你開始一個(gè)新項(xiàng)目,并且有選擇權(quán)?我想說的是目前沒有對(duì)錯(cuò),只要你計(jì)劃使用的庫 Python 2.7.x 和 Python 3.x 雙方都支持的話。盡管如此,當(dāng)在編寫它們中的任何一個(gè)的代碼,或者是你計(jì)劃移植你的項(xiàng)目的時(shí)候,是非常值得看看這兩個(gè)主要流行的 Python 版本之間的差別的,以便避免常見的陷阱,
更多的關(guān)于 Python 2 和 Python 3 的文章
Python 3.x 介紹的 一些Python 2 不兼容的關(guān)鍵字和特性可以通過在 Python 2 的內(nèi)置__future__模塊導(dǎo)入。如果你計(jì)劃讓你的代碼支持 Python 3.x,建議你使用__future__模塊導(dǎo)入。例如,如果我想要 在Python 2 中表現(xiàn) Python 3.x 中的整除,我們可以通過如下導(dǎo)入
from__future__importdivision
更多的__future__模塊可被導(dǎo)入的特性被列在下表中:
featureoptional inmandatory ineffect
nested_scopes2.1.0b12.2PEP 227: Statically Nested Scopes
generators2.2.0a12.3PEP 255: Simple Generators
division2.2.0a23.0PEP 238: Changing the Division Operator
absolute_import2.5.0a13.0PEP 328: Imports: Multi-Line and Absolute/Relative
with_statement2.5.0a12.6PEP 343: The “with” Statement
print_function2.6.0a23.0PEP 3105: Make print a function
unicode_literals2.6.0a23.0PEP 3112: Bytes literals in Python 3000
(Source:https://docs.python.org/2/library/future.html)
fromplatformimportpython_version
print函數(shù)
很瑣碎,而print語法的變化可能是最廣為人知的了,但是仍值得一提的是: Python 2 的print聲明已經(jīng)被print()函數(shù)取代了,這意味著我們必須包裝我們想打印在小括號(hào)中的對(duì)象。
Python 2 不具有額外的小括號(hào)問題。但對(duì)比一下,如果我們按照 Python 2 的方式不使用小括號(hào)調(diào)用print函數(shù),Python 3 將拋出一個(gè)語法異常(SyntaxError)。
Python 2
print'Python', python_version()print'Hello, World!'print('Hello, World!')print"text", ;print'print more text on the same line'
Python2.7.6Hello, World!Hello, World!textprintmore textonthe same line
Python 3
print('Python', python_version())print('Hello, World!')print("some text,", end="")print(' print more text on the same line')
Python3.4.1Hello, World!some text,printmore textonthe same line
print'Hello, World!'
File"", line1print'Hello, World!'^SyntaxError: invalid syntax
注意
以上通過 Python 2 使用Printing "Hello, World"是非常正常的,盡管如此,如果你有多個(gè)對(duì)象在小括號(hào)中,我們將創(chuàng)建一個(gè)元組,因?yàn)閜rint在 Python 2 中是一個(gè)聲明,而不是一個(gè)函數(shù)調(diào)用。
print'Python', python_version()print('a','b')print'a','b'
Python2.7.7('a','b')a b
整除
如果你正在移植代碼,這個(gè)變化是特別危險(xiǎn)的?;蛘吣阍?Python 2 上執(zhí)行 Python 3 的代碼。因?yàn)檫@個(gè)整除的變化表現(xiàn)在它會(huì)被忽視(即它不會(huì)拋出語法異常)。
因此,我還是傾向于使用一個(gè)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/2print'3 // 2 =',3// 2print'3 / 2.0 =',3/2.0print'3 // 2.0 =',3// 2.0
Python2.7.63/2=13// 2 = 13/2.0=1.53// 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)
Python3.4.13/2=1.53// 2 = 13/2.0=1.53// 2.0 = 1.0
Unicode
Python 2 有ASCII str()類型,unicode()是單獨(dú)的,不是 byte 類型。
現(xiàn)在, 在 Python 3,我們最終有了Unicode (utf-8)字符串,以及一個(gè)字節(jié)類:byte和bytearrays。
Python 2
print'Python', python_version()
Python2.7.6
printtype(unicode('this is like a python3 str type'))
printtype(b'byte type does not exist')
print'they are really'+ b' the same'they are really the same
printtype(bytearray(b'bytearray oddly does exist though'))
Python 3
print('Python', python_version())print('strings are now utf-8 \u03BCnico\u0394é!')Python3.4.1strings are now utf-8μnicoΔé!
print('Python', python_version(), end="")print(' has', type(b' bytes for storing data'))Python3.4.1has
print('and Python', python_version(), end="")print(' also has', type(bytearray(b'bytearrays')))andPython3.4.1also has
'note that we cannot add a string' + b'bytes for data'---------------------------------------------------------------------------TypeError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()----> 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()創(chuàng)建迭代對(duì)象的用法是非常流行的。比如: for 循環(huán)或者是列表/集合/字典推導(dǎo)式。
這個(gè)表現(xiàn)十分像生成器(比如。“惰性求值”)。但是這個(gè)xrange-iterable是無窮的,意味著你可以無限遍歷。
由于它的惰性求值,如果你不得僅僅不遍歷它一次,xrange()函數(shù) 比range()更快(比如 for 循環(huán))。盡管如此,對(duì)比迭代一次,不建議你重復(fù)迭代多次,因?yàn)樯善髅看味紡念^開始。
在 Python 3 中,range()是像xrange()那樣實(shí)現(xiàn)以至于一個(gè)專門的xrange()函數(shù)都不再存在(在 Python 3 中xrange()會(huì)拋出命名異常)。
importtimeitn =10000deftest_range(n):returnforiinrange(n):passdeftest_xrange(n):foriinxrange(n):pass
Python 2
print'Python', python_version()print'\ntiming range()'%timeit test_range(n)print'\n\ntiming xrange()'%timeit test_xrange(n)Python2.7.6timing range()1000loops, bestof3:433μs perlooptiming xrange()1000loops, bestof3:350μs perloop
Python 3
print('Python', python_version())print('\ntiming range()')%timeit test_range(n)Python3.4.1timing range()1000loops, bestof3:520μs perloop
print(xrange(10))---------------------------------------------------------------------------NameError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()----> 1 print(xrange(10))NameError: name'xrange'isnotdefined
Python 3 中的range對(duì)象的__contains__方法
另外一件值得一提的事情就是在 Python 3 中range有一個(gè)新的__contains__方法(感謝Yuchen Ying指出了這個(gè)),__contains__方法可以加速"查找"在 Python 3.x 中顯著的整數(shù)和布爾類型。
x =10000000defval_in_range(x, val):returnvalinrange(x)defval_in_xrange(x, val):returnvalinxrange(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)Python3.4.11loops, best of3:742ms per loop1000000loops, best of3:1.19μs per loop
基于以上的timeit的結(jié)果,當(dāng)它使一個(gè)整數(shù)類型,而不是浮點(diǎn)類型的時(shí)候,你可以看到執(zhí)行查找的速度是 60000 倍快。盡管如此,因?yàn)?Python 2.x 的range或者是xrange沒有一個(gè)__contains__方法,這個(gè)整數(shù)類型或者是浮點(diǎn)類型的查詢速度不會(huì)相差太大。
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)Python2.7.71loops, bestof3:285ms perloop1loops, bestof3:179ms perloop1loops, bestof3:658ms perloop1loops, bestof3:556ms perloop
下面說下__contain__方法并沒有加入到 Python 2.x 中的證據(jù):
print('Python', python_version())range.__contains__Python3.4.1
print 'Python', python_version()range.__contains__Python 2.7.7---------------------------------------------------------------------------AttributeError? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()1print'Python', python_version()----> 2 range.__contains__AttributeError:'builtin_function_or_method'object hasnoattribute'__contains__'
print 'Python', python_version()xrange.__contains__Python 2.7.7---------------------------------------------------------------------------AttributeError? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()1print'Python', python_version()----> 2 xrange.__contains__AttributeError: type object'xrange'hasnoattribute'__contains__'
**注意在 Python 2 和 Python 3 中速度的不同***
有些猿類指出了 Python 3 的range()和 Python 2 的xrange()之間的速度不同。因?yàn)樗麄兪怯孟嗤姆椒▽?shí)現(xiàn)的,因此期望相同的速度。盡管如此,這事實(shí)在于 Python 3 傾向于比 Python 2 運(yùn)行的慢一點(diǎn)。
deftest_while():i =0whilei <20000:? ? ? ? i +=1return
print('Python', python_version())%timeit test_while()Python3.4.1100loops, bestof3:2.68ms perloop
print'Python', python_version()%timeit test_while()Python2.7.61000loops, bestof3:1.72ms perloop
Raising exceptions
Python 2 接受新舊兩種語法標(biāo)記,在 Python 3 中如果我不用小括號(hào)把異常參數(shù)括起來就會(huì)阻塞(并且反過來引發(fā)一個(gè)語法異常)。
Python 2
print'Python', python_version()Python2.7.6
raise IOError, "file error"---------------------------------------------------------------------------IOError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()----> 1 raise IOError, "file error"IOError: file error
raise IOError("file error")---------------------------------------------------------------------------IOError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()----> 1 raise IOError("file error")IOError: file error
Python 3
print('Python', python_version())Python3.4.1
raiseIOError,"file error"
File"", line1raiseIOError,"file error"^SyntaxError: invalid syntaxThe proper way toraisean exceptioninPython3:
print('Python', python_version())raise IOError("file error")Python3.4.1---------------------------------------------------------------------------OSError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recent call last)in()1print('Python', python_version())---->2raise IOError("file error")OSError: file error
Handling exceptions
在 Python 3 中處理異常也輕微的改變了,在 Python 3 中我們現(xiàn)在使用as作為關(guān)鍵詞。
python 2
print'Python', python_version()try:? ? let_us_cause_a_NameErrorexcept NameError, err:printerr,'--> our error message'Python2.7.6name'let_us_cause_a_NameError'isnotdefined-->ourerror message
Python 3
print('Python', python_version())try:? ? let_us_cause_a_NameErrorexcept NameError as err:print(err,'--> our error message')Python3.4.1name'let_us_cause_a_NameError'isnotdefined-->ourerror message
next() 函數(shù) and .next() 方法
因?yàn)閚ext() (.next())是一個(gè)如此普通的使用函數(shù)(方法),這里有另外一個(gè)語法改變(或者是實(shí)現(xiàn)上改變了),值得一提的是:在 Python 2.7.5 中函數(shù)和方法你都可以使用,next()函數(shù)在 Python 3 中一直保留著(調(diào)用.next()拋出屬性異常)。
Python 2
print'Python', python_version()my_generator = (letterforletterin'abcdefg')next(my_generator)my_generator.next()
Python2.7.6'b'
Python 3
print('Python', python_version())my_generator = (letterforletterin'abcdefg')next(my_generator)
Python3.4.1'a'
my_generator.next()
---------------------------------------------------------------------------AttributeError? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recentcalllast)in()----> 1 my_generator.next()AttributeError:'generator'object hasnoattribute'next'
For 循環(huán)變量和全局命名空間泄漏
好消息:在 Python 3.x 中 for 循環(huán)變量不會(huì)再導(dǎo)致命名空間泄漏。
在 Python 3.x 中做了一個(gè)改變,在What’s New In Python 3.0中有如下描述:
"列表推導(dǎo)不再支持[... for var in item1, item2, ...]這樣的語法。使用[... for var in (item1, item2, ...)]代替。也需要提醒的是列表推導(dǎo)有不同的語義:? 他們關(guān)閉了在 `list()` 構(gòu)造器中的生成器表達(dá)式的語法糖, 并且特別是循環(huán)控制變量不再泄漏進(jìn)周圍的作用范圍域."
Python 2
print'Python', python_version()i =1print'before: i =', iprint'comprehension: ', [iforiinrange(5)]print'after: i =', iPython2.7.6before: i =1comprehension:? [0,1,2,3,4]after: i =4
Python 3
print('Python', python_version())i =1print('before: i =', i)print('comprehension:', [iforiinrange(5)])print('after: i =', i)Python3.4.1before: i =1comprehension: [0,1,2,3,4]after: i =1
比較不可排序類型
在 Python 3 中的另外一個(gè)變化就是當(dāng)對(duì)不可排序類型做比較的時(shí)候,會(huì)拋出一個(gè)類型錯(cuò)誤。
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)Python2.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))Python3.4.1---------------------------------------------------------------------------TypeError? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Traceback (most recent call last)in()1print('Python', python_version())---->2print("[1, 2] > 'foo' = ", [1,2] >'foo')3print("(1, 2) > 'foo' = ", (1,2) >'foo')4print("[1, 2] > (1, 2) = ", [1,2] > (1,2))TypeError: unorderabletypes: list() > str()
通過input()解析用戶的輸入
幸運(yùn)的是,在 Python 3 中已經(jīng)解決了把用戶的輸入存儲(chǔ)為一個(gè)str對(duì)象的問題。為了避免在 Python 2 中的讀取非字符串類型的危險(xiǎn)行為,我們不得不使用raw_input()代替。
Python 2
Python2.7.6[GCC4.0.1(AppleInc. build5493)] on darwinType"help","copyright","credits"or"license"formore information.>>> my_input = input('enter a number: ')enter anumber:123>>> type(my_input)>>> my_input = raw_input('enter a number: ')enter anumber:123>>> type(my_input)
Python 3
Python3.4.1[GCC4.2.1(AppleInc. build5577)] on darwinType"help","copyright","credits"or"license"formore information.>>> my_input = input('enter a number: ')enter anumber:123>>> type(my_input)
返回可迭代對(duì)象,而不是列表
如果在 xrange 章節(jié)看到的,現(xiàn)在在 Python 3 中一些方法和函數(shù)返回迭代對(duì)象 -- 代替 Python 2 中的列表
因?yàn)槲覀兺ǔD切┍闅v只有一次,我認(rèn)為這個(gè)改變對(duì)節(jié)約內(nèi)存很有意義。盡管如此,它也是可能的,相對(duì)于生成器 --- 如需要遍歷多次。它是不那么高效的。
而對(duì)于那些情況下,我們真正需要的是列表對(duì)象,我們可以通過list()函數(shù)簡單的把迭代對(duì)象轉(zhuǎn)換成一個(gè)列表。
Python 2
print'Python', python_version()printrange(3)printtype(range(3))Python2.7.6[0,1,2]
Python 3
print('Python', python_version())print(range(3))print(type(range(3)))print(list(range(3)))Python3.4.1range(0,3)[0,1,2]
在 Python 3 中一些經(jīng)常使用到的不再返回列表的函數(shù)和方法:
zip()
map()
filter()
dictionary's .keys() method
dictionary's .values() method
dictionary's .items() method
更多的關(guān)于 Python 2 和 Python 3 的文章
下面是我建議后續(xù)的關(guān)于 Python 2 和 Python 3 的一些好文章。
移植到 Python 3
Should I use Python 2 or Python 3 for my development activity?
Porting Python 2 Code to Python 3
How keep Python 3 moving forward
Python 3 的擁護(hù)者和反對(duì)者
10 awesome features of Python that you can't use because you refuse to upgrade to Python 3
Everything you did not want to know about Unicode in Python 3