Python 2.7.x 和 Python 3.x 的主要區別

注:本文的原文地址爲
Key differences between Python 2.7.x and Python 3.xhtml

許多 Python 初學者想知道他們應該從 Python 的哪一個版本開始學習。對於這個問題個人答案是 「你學習你喜歡的教程的版本,而後檢查他們之間的不一樣。"python

可是若是你開始一個新項目,而且有選擇權?我想說的是目前沒有對錯,只要你計劃使用的庫 Python 2.7.x 和 Python 3.x 雙方都支持的話。儘管如此,當在編寫它們中的任何一個的代碼,或者是你計劃移植你的項目的時候,是很是值得看看這兩個主要流行的 Python 版本之間的差異的,以便避免常見的陷阱,git

章節

future 模塊

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 函數

[跳轉到章節預覽]函數

很瑣碎,而 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)/23/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) 字符串,以及一個字節類:bytebytearrays

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

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 '\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

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 的擁護者和反對者

相關文章
相關標籤/搜索