本文部分轉載自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優化
python 3.xui
5 |
SyntaxError: invalid token |
運算符
(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:
Python 3.x:
而對於//除法,這種除法叫作floor除法,會對除法的結果自動進行一個floor操做,在python 2.x和python 3.x中是一致的。
python 2.x:
python 3.x:
注意的是並非捨棄小數部分,而是執行floor操做,若是要截取小數部分,那麼須要使用math模塊的trunc函數
python 3.x:
(4) 比較運算符
Python 2.x中容許不一樣類型的對象進行比較,好比:
Python 3.x中則不容許這類不一樣類型之間含糊不清的比較:
2 |
Traceback (most recent call last): |
3 |
File "<stdin>" , line 1 , in <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是語句
向打開的輸出流file中輸出x, y變量的值
在python 3.x中這句要這麼寫
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中的序列賦值一直是這門語言宣傳時候的一個亮點,能把一個序列解開進行賦值:
python 3.x對這一功能更加進行了強化,支持擴展序列解包:
內置集合類型
內置集合的差異主要體如今字典對象的幾個視圖方法上,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(): |
03 |
... def in_function(): |
05 |
... return in_function |
08 |
Traceback (most recent call last): |
09 |
File "<stdin>" , line 1 , in <module> |
10 |
File "<stdin>" , line 4 , in in_function |
11 |
UnboundLocalError: local variable 'call_count' referenced before assignment |
可是在Python 3.x中只要使用nonlocal關鍵字對變量進行修飾,就會自動去外層函數尋找變量:
1 |
>>> def out_function(): |
4 |
... nonlocal call_count |
(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:
2 |
<built - in function map > |
4 |
<built - in function filter > |
它們輸出的結果類型都是列表:
1 |
>>> map ( lambda x:x * 2 ,[ 1 , 2 , 3 ]) |
3 |
>>> filter ( lambda x:x % 2 = = 0 , range ( 10 )) |
可是在Python 3.x中它們卻不是這個樣子了:
4 |
< map object at 0xa6fd70c > |
7 |
>>> filter ( lambda x:x % 2 = = 0 , range ( 10 )) |
8 |
< filter object at 0xa6eeeac > |
首先它們從函數變成了類,其次,它們的返回結果也從當初的列表成了一個可迭代的對象, 咱們嘗試用next函數來進行手工迭代:
1 |
>>> f = filter ( lambda x:x % 2 = = 0 , range ( 10 )) |
對於比較高端的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 1 , in <module> |
4 |
AttributeError: class ClassicClass has no attribute '__class__' |
5 |
>>> class NewClass( object ): |
python 3.x:
1 |
>>> class NewClass: pass |
(2) 無綁定方法
在Python 2.x中除了類方法和靜態方法,其他的方法都必須在第一個參數傳遞self跟實例綁定,可是在Python 3.x中廢除了這條規定,容許方法不綁定實例,這樣的方法跟普通的函數沒有區別:
Python 2.x:
05 |
>>> MyClass.function() |
06 |
Traceback (most recent call last): |
07 |
File "<stdin>" , line 1 , in <module> |
08 |
TypeError: unbound method function() must be called with MyClass instance as first argument (got nothing instead) |
11 |
Traceback (most recent call last): |
12 |
File "<stdin>" , line 1 , in <module> |
13 |
TypeError: function() takes no arguments ( 1 given) |
Python 3.x:
05 |
>>> MyClass.function() |
09 |
Traceback (most recent call last): |
10 |
File "<stdin>" , line 1 , in <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 |
異常
先來看一段代碼
python 2.x:
02 |
... def __init__( self , msg): |
06 |
... raise Person, "woca" |
07 |
... except Person as p: |
python 3.x:
02 |
... def __init__( self , msg): |
06 |
... raise Person( "woca" ) |
07 |
... except Person as p: |
09 |
Traceback (most recent call last): |
10 |
File "<stdin>" , line 2 , in <module> |
11 |
TypeError: exceptions must derive from BaseException |
13 |
During handling of the above exception, another exception occurred: |
15 |
Traceback (most recent call last): |
16 |
File "<stdin>" , line 3 , in <module> |
17 |
TypeError: catching classes that do not inherit from BaseException is not allowed |
接下來講不一樣:
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:" ) |
python 3.x
1 |
>>> x = input ( "please input x" ) |
(2) 返回結果 列表->迭代器
取消了原先的xrange,如今range函數的返回結果同以前的xrange的返回結果同樣,不是列表,而是一個可迭代的對象。其它經常使用的內置函數,如zip也是如此。
(3) Unicode字符串
能很好的支持Unicode
python 2.x
3 |
'\xe6\x88\x91\xe7\x88\xb1\xe5\x8c\x97\xe4\xba\xac\xe5\xa4\xa9\xe5\xae\x89\xe9\x97\xa8' |
6 |
u '\u6211\u7231\u5317\u4eac\u5929\u5b89\u95e8' |
python 3.x
沒有去考究核心庫的差異,經常使用語法上的差異就總結了這些,感受……還好吧,不少經常使用的框架如今都開始支持Python 3.x, 雖然如今本身主要是在用2.7進行開發,但多瞭解一下最新的動態老是好的,Python 最第一版本設計的年代距離如今已經很長時間了,貌似跟本身同歲,可能那時的一些概念尚不清晰,但Python出現的比Java早多了,能設計到這種先進地步,已經至關厲害,Python 3.x雖然跟Python 2.x不兼容,可是修改之處都是讓Python看起來更合理,全部的風格更一致,更Pythonic,總之是個好方向。