Python2.x和3.x主要差別總結

本文部分轉載自http://my.oschina.net/chihz/blog/123437,部分來自自身修改python

開始使用Python以後就處處宣揚Python如何如何好,宣傳工做的一大重要訣竅就是作對比,好比原先用Java的時候作個什麼東西要寫多少代碼,怎麼個別扭,如今用Python實現一樣的功能怎麼個簡單等等。不過談Python,無論怎麼談,老會談到Python2.x和3.x的版本差別問題,這個差別真不是通常的大,從一個簡單的print到核心庫的改進都牽扯到了不少,如今總結了一些主要的差別點。編程

 

基本類型

 

(1) 整形

在python 2.x中,有兩種整數類型,通常的32位整數和長整數,長整數都是以L或者l(不建議使用小寫l, 容易跟1搞混),超過32位長度以後會自動轉換爲長整形。閉包

在python 3.x中,容許咱們更爲所欲爲更天然的使用整數,只有一種類型,沒有長度限制。框架

python 2.x編程語言

1 >>> 1000000000000000000000000000000000
2 1000000000000000000000000000000000L

python 3.x函數式編程

1 >>> 1000000000000000000000000000000
2 1000000000000000000000000000000

(2) 八進制字面量表示

在Python 2.x中,表示八進制字面量有兩種方式,一是同衆多咱們所熟悉的編程語言同樣,直接在數字前加0,好比01000, 另外是加0o(0和小寫字母o)0o1000函數

在Python 3.x中,表示八進制字面量的方式只有一種,就是0o1000測試

python 2.x優化

1 >>> 0o1000
2 512
3 >>> 01000
4 512

python 3.xui

1 >>> 01000
2   File "<stdin>", line 1
3     01000
4         ^
5 SyntaxError: invalid token
6 >>> 0o1000
7 512

 

運算符

 

(1) 不等於測試

Python 2.x中不等於有兩種寫法 != 和 <>

Python 3.x中去掉了<>, 只有!=一種寫法,還好,我歷來沒有使用<>的習慣

(2) 去掉了repr表達式``

Python 2.x 中反引號``至關於repr函數的做用

Python 3.x 中去掉了``這種寫法,只容許使用repr函數,這樣作的目的是爲了使代碼看上去更清晰麼?不過我感受用repr的機會不多,通常只在debug的時候才用,多數時候仍是用str函數來用字符串描述對象。

(3) 除法運算

Python中的除法較其它語言顯得很是高端,有套很複雜的規則。Python中的除法有兩個運算符,/和//

首先來講/除法:

在python 2.x中/除法就跟咱們熟悉的大多數語言,好比Java啊C啊差很少,整數相除的結果是一個整數,把小數部分徹底忽略掉,浮點數除法會保留小數點的部分獲得一個浮點數的結果。

在python 3.x中/除法再也不這麼作了,對於整數之間的相除,結果也會是浮點數。

Python 2.x:

1 >>> 1 / 2
2 0
3 >>> 1.0 / 2.0
4 0.5

Python 3.x:

1 >>> 1/2
2 0.5

而對於//除法,這種除法叫作floor除法,會對除法的結果自動進行一個floor操做,在python 2.x和python 3.x中是一致的。

python 2.x:

1 >>> -1 // 2
2 -1

python 3.x:

1 >>> -1 // 2
2 -1

注意的是並非捨棄小數部分,而是執行floor操做,若是要截取小數部分,那麼須要使用math模塊的trunc函數

python 3.x:

1 >>> import math
2 >>> math.trunc(1 / 2)
3 0
4 >>> math.trunc(-1 / 2)
5 0

(4) 比較運算符

Python 2.x中容許不一樣類型的對象進行比較,好比:

1 >>> -1 < ''
2 True
3 >>> 1 > ''
4 False

Python 3.x中則不容許這類不一樣類型之間含糊不清的比較:

1 >>> 1 > ''
2 Traceback (most recent call last):
3   File "<stdin>", line 1in <module>
4 TypeError: unorderable types: int() > str()

我覺着即便在2.x中也不該該使用這種含糊不清的比較,1 > '' 返回了False, 這個是基於什麼判斷的?說不清楚。

 

語句

 

(1) print

1-一、這是應該算是最廣爲人知的一個差異了吧,Python 2.x和Python 3.x之間連Hello World都是不兼容的。

python 2.x中print是語句

1 print >> file x, y

向打開的輸出流file中輸出x, y變量的值

在python 3.x中這句要這麼寫

1 print(x,y,file=file)

file參數定義的默認值是sys.stdout

 

1-二、因爲print輸出後結束(即會換行),而在python2.X裏,你想不讓一個輸出結束(即不換行)要用:;在python3.X裏,就用: end=''

如:
在python3.X裏用:
print('123' ,end='');
print('ABC')
 
而在python2.X裏用:
print '123' ,
print('ABC')   #在輸出字符串時能夠不用()括號
 
 
 

(2) 擴展序列解包

python中的序列賦值一直是這門語言宣傳時候的一個亮點,能把一個序列解開進行賦值:

01 >>> x, y = [12]
02 >>> x
03 1
04 >>> y
05 2
06 >>> x, y = 12
07 >>> x
08 1
09 >>> y
10 2
11 >>> x, y = y, x
12 >>> x
13 2
14 >>> y
15 1

python 3.x對這一功能更加進行了強化,支持擴展序列解包:

1 >>> x, *= 123
2 >>> x
3 1
4 >>> y
5 [23]

 

內置集合類型

 

內置集合的差異主要體如今字典對象的幾個視圖方法上,keys\items和values,在2.x中這幾個試圖方法每次都是赤裸裸的返回一個新的列表,3.x對這種粗魯的行爲作了優化,返回的是迭代器對象。

另外原先字典對象有個has_key方法來判斷key在字典中是否存在,這個方法實現的功能跟in運算符徹底同樣,所以在3.x就把這個方法給幹掉了。

 


函數

 

(1) nonlocal做用域

在2.x的時代,Python只有兩個做用域,模塊裏面的全局做用域和函數的局部做用域,可是隨着在函數中定義函數的狀況愈來愈多,好比裝飾器、閉包等等,這裏面就出現了內層函數引用外層函數變量的問題:

好比我要在內層函數修改外層函數的一個變量,在Python 2.x的時代就會出現錯誤:

01 >>> def out_function():
02 ...   call_count = 0
03 ...   def in_function():
04 ...     call_count += 1
05 ...   return in_function
06 ...
07 >>> out_function()()
08 Traceback (most recent call last):
09   File "<stdin>", line 1in <module>
10   File "<stdin>", line 4in in_function
11 UnboundLocalError: local variable 'call_count' referenced before assignment

可是在Python 3.x中只要使用nonlocal關鍵字對變量進行修飾,就會自動去外層函數尋找變量:

1 >>> def out_function():
2 ...   call_count = 0
3 ...   def in_function():
4 ...     nonlocal call_count
5 ...     call_count+=1
6 ...   return in_function
7 ...
8 >>> out_function()()

(2) Key-word only 參數

前面咱們說到print在Python 3.x中是做爲函數提供的。print的參數設計是這樣的:

1 print(*value, sep=' ', end='\n'file=sys.stdout)

若是瞭解Python參數的順序規則,咱們知道在Python 2.x中,參數的順序必須遵循如下規則去定義:

def function(通常參數 or 帶默認值參數,*sequence, **dict)

而這個地方卻容許先定義*sequence再去定義通常參數,這就是Python 3.x所支持的key-word only的參數形式。在一個*以後容許去定義一些參數,這些參數在函數調用的時候必須指定參數名稱。這樣本質上其實就是在*sequence類型的參數以後固定寫死了一個**dict, 固然也能夠在後面繼續定義一個**dict:

1 def test(*value, name, **dict):

但這樣寫就不對了def test(*value, **dict, name)

(3) map、filter和reduce

這三個函數號稱是函數式編程的表明。在Python3.x和Python2.x中也有了很大的差別。

首先咱們先簡單的在Python2.x的交互下輸入map和filter,看到它們二者的類型是built-in function:

1 >>> map
2 <built-in function map>
3 >>> filter
4 <built-in function filter>

它們輸出的結果類型都是列表:

1 >>> map(lambda x:x * 2,[1,2,3])
2 [246]
3 >>> filter(lambda x:x % 2 == 0range(10))
4 [02468]

可是在Python 3.x中它們卻不是這個樣子了:

1 >>> map
2 <class 'map'>
3 >>> map(print,[1,2,3])
4 <map object at 0xa6fd70c>
5 >>> filter
6 <class 'filter'>
7 >>> filter(lambda x:x % 2 == 0range(10))
8 <filter object at 0xa6eeeac>

首先它們從函數變成了類,其次,它們的返回結果也從當初的列表成了一個可迭代的對象, 咱們嘗試用next函數來進行手工迭代:

1 >>> f = filter(lambda x:x % 2 == 0range(10))
2 >>> next(f)
3 0
4 >>> next(f)
5 2
6 >>> next(f)
7 4
8 >>> next(f)
9 6

對於比較高端的reduce函數,它在Python 3.x中已經不屬於built-in了,被挪到functools模塊當中。

 

模塊

 

Python 2.x和3.x模塊之間的差異主要體如今相對導入這部分的規則上。

在Python2.x和3.x當中都是使用點號來指定在當前包下進行相對導入,可是在沒有點號的狀況下,Python2.x會以先相對再絕對的模塊搜索順序,3.x的順序跟這個相反,默認是絕對的,缺乏點號的時候,導入忽略包含包自身並在sys.path搜索路徑上進行查找。

 

面向對象

 

(1) 經典類和新式類

Python OO最神奇的地方就是有兩種類,經典類和新式類。

新式類跟經典類的差異主要是如下幾點:

1. 新式類對象能夠直接經過__class__屬性獲取自身類型:type

2. 繼承搜索的順序發生了改變,經典類多繼承屬性搜索順序: 先深刻繼承樹左側,再返回,開始找右側;新式類多繼承屬性搜索順序: 先水平搜索,而後再向上移動

3. 新式類增長了__slots__內置屬性, 能夠把實例屬性的種類鎖定到__slots__規定的範圍之中。

4. 新式類增長了__getattribute__方法

Python 2.x中默認都是經典類,只有顯式繼承了object纔是新式類

Python 3.x中默認都是新式類,沒必要顯式的繼承object

python 2.x:

1 >>> ClassicClass.__class__
2 Traceback (most recent call last):
3   File "<stdin>", line 1in <module>
4 AttributeError: class ClassicClass has no attribute '__class__'
5 >>> class NewClass(object):
6 ...   pass
7 ...
8 >>> NewClass.__class__
9 <type 'type'>

python 3.x:

1 >>> class NewClass:pass
2 ...
3 >>> NewClass.__class__
4 <class 'type'>

(2) 無綁定方法

在Python 2.x中除了類方法和靜態方法,其他的方法都必須在第一個參數傳遞self跟實例綁定,可是在Python 3.x中廢除了這條規定,容許方法不綁定實例,這樣的方法跟普通的函數沒有區別:

Python 2.x:

01 >>> class MyClass:
02 ...   def function():
03 ...     print "function"
04 ...
05 >>> MyClass.function()
06 Traceback (most recent call last):
07   File "<stdin>", line 1in <module>
08 TypeError: unbound method function() must be called with MyClass instance as first argument (got nothing instead)
09 >>> m = MyClass()
10 >>> m.function()
11 Traceback (most recent call last):
12   File "<stdin>", line 1in <module>
13 TypeError: function() takes no arguments (1 given)

Python 3.x:

01 >>> class MyClass:
02 ...   def function():
03 ...     print("function")
04 ...
05 >>> MyClass.function()
06 function
07 >>> m = MyClass()
08 >>> m.function()
09 Traceback (most recent call last):
10   File "<stdin>", line 1in <module>
11 TypeError: function() takes no arguments (1 given)

(3) 重要的重載

1. next()和__next__():這應該是繼print以後第二大坑爹的不兼容吧,Python程序漫山遍野都是迭代器,可是2和3之間迭代器的實現接口方法名是不一樣的……嗯,啥都不說了。

2. 分片攔截:Python 3.x以前, 類能夠定義__getslice__和__setslice__方法來專門攔截分片,而且這兩個方法優先於__getitem__和__setitem__, 可是Python 3.x時代這倆方法不再存在了,所有的工做都交給了__getitem__和__setitem__,所以在使用這兩個方法以前要先判斷傳遞進參數的類型是否是slice對象。

3. __bool__方法:咱們知道Python中默認將全部的空對象定義爲布爾意義上的False,在本身定義的類中咱們也能夠加入自定義的布爾判斷標準,在2.x中這個方法名叫作__nonzero__, 這個名字顯然很是不直觀而且不科學!全部考試交白卷的孩子咱們都要否認他們的才能麼?顯然不能!所以Python 3.x中這個方法被重名命爲__bool__

4. 3.x 取消了用於大小比較的__cmp__方法,取而代之的是:__lt__、__gt__、__le__、__ge__、__eq__、__ne__,嗯,我感受這個想法真是不能苟同……有誰能說服我給我洗腦讓我愛上這一堆__lt__、__gt__、__le__、__ge__、__eq__、__ne__麼。。。

(4) 類修飾器

在個人上一篇博客中秀了一把函數裝飾器在表單驗證中的使用,http://my.oschina.net/chihz/blog/122897

在3.x的時代,類也有裝飾器了,這個裝飾器威力巨大,能把裝飾的類搞的面目全非,總之想怎麼搞就怎麼搞,用法同函數裝飾器基本一致,只不過傳遞的參數是類型:

01 >>> def shutdown(cls):
02 ...   def shutdown_func(self):
03 ...     print("do something...")
04 ...   cls.shutdown = shutdown_func
05 ...   return cls
06 ...
07 >>> @shutdown
08 ... class Test:pass
09 ...
10 >>> t = Test()
11 >>> t.shutdown()
12 do something...

 

異常

 

先來看一段代碼

python 2.x:

01 >>> class Person:
02 ...   def __init__(self, msg):
03 ...     self.msg = msg
04 ...
05 >>> try:
06 ...   raise Person, "woca"
07 ... except Person as p:
08 ...   print p.msg
09 ...
10 woca

python 3.x:

01 >>> class Person:
02 ...   def __init__(self, msg):
03 ...     self.msg = msg
04 ...
05 >>> try:
06 ...   raise Person("woca")
07 ... except Person as p:
08 ...   print(p.msg)
09 Traceback (most recent call last):
10   File "<stdin>", line 2in <module>
11 TypeError: exceptions must derive from BaseException
12  
13 During handling of the above exception, another exception occurred:
14  
15 Traceback (most recent call last):
16   File "<stdin>", line 3in <module>
17 TypeError: catching classes that do not inherit from BaseException is not allowed
18 >>>

接下來講不一樣:

1. 在2.x時代,全部類型的對象都是能夠被直接拋出的,在3.x時代,只有繼承自BaseException的對象才能夠被拋出。

2. 2.x raise語句使用逗號將拋出對象類型和參數分開,3.x取消了這種奇葩的寫法,直接調用構造函數拋出對象便可。

在2.x時代,異常在代碼中除了表示程序錯誤,還常常作一些普通控制結構應該作的事情,在3.x中能夠看出,設計者讓異常變的更加專注,只有在錯誤發生的狀況才能去用異常捕獲語句來處理。

 

其它

 

(1) 編碼

Py3.X源碼文件默認使用utf-8編碼, 不須要再在文件頭上聲明 # -*- coding: utf-8 -*- 

(2) input函數

取消了蛋疼的raw_input,input函數的功能跟原先的raw_input同樣

python 2.x

1 >>> x = input("please input x:")
2 please input x:1
3 >>> x
4 1

python 3.x

1 >>> x = input("please input x")
2 please input x1
3 >>> x
4 '1'

(2) 返回結果 列表->迭代器

取消了原先的xrange,如今range函數的返回結果同以前的xrange的返回結果同樣,不是列表,而是一個可迭代的對象。其它經常使用的內置函數,如zip也是如此。

(3) Unicode字符串

能很好的支持Unicode

python 2.x

1 >>> str = "我愛北京天安門"
2 >>> str
3 '\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8'
4 >>> str = u"我愛北京天安門"
5 >>> str
6 u'\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8'

python 3.x

1 >>> str = "我愛北京天安門"
2 >>> str
3 '我愛北京天安門'

沒有去考究核心庫的差異,經常使用語法上的差異就總結了這些,感受……還好吧,不少經常使用的框架如今都開始支持Python 3.x, 雖然如今本身主要是在用2.7進行開發,但多瞭解一下最新的動態老是好的,Python 最第一版本設計的年代距離如今已經很長時間了,貌似跟本身同歲,可能那時的一些概念尚不清晰,但Python出現的比Java早多了,能設計到這種先進地步,已經至關厲害,Python 3.x雖然跟Python 2.x不兼容,可是修改之處都是讓Python看起來更合理,全部的風格更一致,更Pythonic,總之是個好方向。

相關文章
相關標籤/搜索