html
可是,若是您開始一個新項目並能夠選擇,該怎麼辦?我要說的是,只要Python 2.7.x和Python 3.x都支持您計劃使用的庫,那麼目前就沒有「對」或「錯」。可是,這兩個最受歡迎的Python版本之間的主要區別值得一看,以此避免在爲其中任何一個編寫代碼或計劃移植項目時碰見陷阱。python
__future__
模塊Python 3.x引入了一些Python 2不兼容的關鍵字和功能,能夠經過__future__
Python 2 的內置模塊導入這些關鍵字和功能。__future__
若是您打算爲代碼提供Python 3.x支持,建議使用import導入。例如,若是咱們想要Python 2中Python 3.x的整數除法行爲,則能夠經過如下方式將其導入git
from __future__ import division
__future__
下表列出了能夠從模塊導入的更多功能:github
特徵 | 可選(版本) | 強制(版本) | 影響 |
---|---|---|---|
nested_scopes | 2.1.0b1 | 2.2 | PEP 227: 靜態嵌套的合併範圍 |
generators | 2.2.0a1 | 2.3 | PEP 255: 簡單生成器 |
division | 2.2.0a2 | 3.0 | PEP 238: 更改除法運算符 |
absolute_import | 2.5.0a1 | 3.0 | PEP 328: 導入:多行和絕對/相對 |
with_statement | 2.5.0a1 | 2.6 | PEP 343: 「 with」聲明 |
print_function | 2.6.0a2 | 3.0 | PEP 3105: 使打印功能 |
unicode_literals | 2.6.0a2 | 3.0 | PEP 3112: Python 3000中的字節字面量 |
(來源:[https://docs.python.org/2/library/__future__.html](https://docs.python.org/2/library/__future__.html#module-__future__))算法
from platform import python_version
很是瑣碎,而且print語法中的更改多是最廣爲人知的更改,但仍然值得一提:Python 2的print語句已被該print()
函數替換,這意味着咱們必須把要打印的對象包裝在括號中。app
若是咱們用Python 2的不帶括號的方式調用print功能, Python 2不會出現其餘額外問題,可是相反,Python 3會拋出一個SyntaxError
。函數
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
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
注意:oop
經過Python 2在上面打印「 Hello,World」看起來很「正常」。可是,若是在括號內有多個對象,則將建立一個元組,由於這個print
在Python 2中是「語句」,而不是函數調用。ui
print 'Python', python_version()
print('a', 'b')
print 'a', 'b'
Python 2.7.7
('a', 'b')
a b
若是您正在移植代碼,或者您正在Python 2中執行Python 3代碼,則此更改特別危險,由於整數除法行爲的更改一般不會引發注意(它不會引起SyntaxError
)。 所以,我仍然傾向於在個人Python 3腳本中使用float(3)/2
或3/2.0
代替a 3/2
,以避免給Python 2帶來麻煩(反之亦然,我建議在您的Python 2腳本中使用from __future__ import division
)。this
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
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)str
ings和2個字節的類:byte
和bytearray
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'>
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.x中,用xrange()
在建立可迭代對象很是流行(例如在for循環或list / set-dictionary-comprehension 中)。 該行爲與生成器很是類似(即「惰性求值」),可是這裏的xrange-iterable不是窮舉的--這意味着,您能夠對其進行無限迭代。
因爲其「惰性求值」,range()
的常規優點在於,若是您只須要對其進行一次迭代(例如,在for循環中),一般xrange()
更快。可是,與一次迭代相反,這裏不建議重複屢次,由於每次生成都是從頭開始的!
在Python 3中,range()
的實現相似於xrange()
函數,所以xrange()
再也不是專有函數(在Python 3中xrange()
引起了NameError
)。
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
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
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
range
對象的__contains__
方法值得一提的另外一件事是在Python 3.x 中range
有了一個「新」 __contains__
方法(感謝Yuchen Ying指出了這一點)。對於整數和布爾類型,該__contains__
方法能夠大大加快Python 3.x中range
的「查找」速度。
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
結果,您能夠看到當「查找」的執行對象是整數類型而不是浮點型時,執行速度提升了約60,000。可是,因爲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 的「proofs」之下:
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 3 range()
和Python2 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會阻塞(並引起一個SyntaxError
的返回):
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
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
在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
」關鍵字
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
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()
方法會引起AttributeError
)。(本段待修正--譯者按)
print 'Python', python_version()
my_generator = (letter for letter in 'abcdefg')
next(my_generator)
my_generator.next()
Python 2.7.6
'b'
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中所作的更改,並在Python 3.0的新增功能中進行了以下描述:
"列表理解再也不支持語法形式[... for var in item1, item2, ...]
。使用[... for var in (item1, item2, ...)]
代替。還要注意,列表理解具備不一樣的語義:對於list()
構造函數內部的生成器表達式,它們更接近語法糖,而且尤爲是循環控制變量再也不泄漏到周圍的範圍中。"
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
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的另外一個不錯的變化是,若是咱們嘗試比較無序類型,則會引起TypeError
警告。
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
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中使用strings
之外的其餘類型進行讀取的危險行爲,咱們必須改變raw_input()
的使用。
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.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
-objects的狀況,咱們能夠簡單的經過list()
函數將可迭代對象轉換爲list
。
print 'Python', python_version()
print range(3)
print type(range(3))
Python 2.7.6
[0, 1, 2]
<type 'list'>
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()
字典的.keys()
方法
字典的.values()
方法
字典的.items()
方法
當Python 3在最後一個有效數字處產生並列(.5)時,Python 3採用瞭如今的四捨五入方式。如今,在Python 3中,小數點將四捨五入爲最接近的偶數。儘管這對於代碼可移植性帶來了不便,可是與四捨五入相比,它被認爲是一種更好的舍入方式,由於它避免了偏向大數的狀況。有關更多信息,請參見優秀的的Wikipedia文章和段落:
print 'Python', python_version()
Python 2.7.12
round(15.5)
16.0
round(16.5)
17.0
print('Python', python_version())
Python 3.5.1
round(15.5)
16
round(16.5)
16