Python 2 --> Python 3
1. 使用__future__模塊
Python 3.X引入了一些與Python 2.X不兼容的關鍵字和特性。在Python 2.X中,可以通過內置的__future__模塊導入這些新內容。如果你希望在Python 2.X環境下寫的代碼也可以在Python 3.X中運行,那么建議使用__future__模塊。
2. print函數
雖然print語法是Python 3中一個很小的改動,而且應該已經廣為人知,但是依然值得一提:
Python 2中的print語句被Python 3中的print()函數代替,這意味着在Python 3中必須依然用括號將需要輸出的對象括起來。在Python 2中使用額外的括號也可以,但是如果在Python 3中以Python 2的形式不帶括號調用print函數,就會觸發SyntaxError(語法錯誤)。
3. 整數除法
由於人們常常會忽視Python 3在整數除法上的改動(寫錯了也不會除法SyntaxError),因此在移植代碼或在Python 2中執行Python 3的代碼時需要特別注意這個改動。
4. Unicode
Python 2有基於ASCII的str()類型, 可通過單獨的unicode()函數轉成unicode類型,但沒有byte類型。在Python 3中有了Unicode (UTF-8)字符串和兩個字節類(bytes 和 bytearrays)。
5. xrange
在Python 2.X中,經常會用xrange()創建一個可迭代對象,通常出現在“for循環”或“列表/集合/字典推導式”中。在Python 3中,range()的實現方式與xrange()函數相同,所以不存在專用的xrange()(在Python 3中使用xrange()會觸發NameError)。
6. 觸發異常
Python 2支持新舊兩種異常除法語法,而Python 3只支持帶括號的語法(不然會觸發SyntaxError)。
7.處理異常
Python 3中的異常處理發生了一點變化。在Python 3中必須使用as關鍵字,Python 2中不需要。
8.next()函數和.next()方法
由於會經常用到next()函數(.next()方法)——返回迭代器的下一個項目。,因此要提到一個語法改動(實現方面也做了改動);在Python 2中,函數形式和方法形式都可以使用;在Python 3中,只能使用next()函數(試圖調用.next()方法會觸發AttributeError——python找不到對應的對象的屬性)。
9.for循環變量與全局命名空間泄漏
在Python 3.X中,for循環中的變量不再會泄漏到全局命名空間中了。Python中循環引用導致內存泄漏的小案例:https://www.cnblogs.com/chen55555/p/11079223.html
10.比較無序類型
Python 3中另一個優秀的改動是,如果我們試圖比較無序類型,就會觸發一個TypeError。
11.使用input()解析輸入內容
Python 3改進了input()函數,這樣該函數就會總是將用戶的輸入存儲為str對象。在Python 2中,為了避免讀取費字符串類型會發生的一些危險行為,不得不使用raw_input()代替input()。
12.返回可迭代對象,而不是列表
某些函數和方法在Python 3中返回的是可迭代對象,而不像在Python 2中返回列表。對象只遍歷一次,會節省很多內存,如果通過生成器多次迭代這些對象,效率就不高了。此時如果需要列表對象,可以通過Python 3的list()函數簡單地將可迭代對象轉換成列表。
Python 3.6 --> Python 3.7
PEP 538, 默認解釋器的命令行接口將通過一個新的環境變量PYTHONCOERCECLOCALE自動在有可行的基於UTF-8的環境(如C.UTF-8,C.utf8和 UTF-8)時轉換,取決於環境變量是否設置和其值。
PEP 539, 為CPython帶來了一個針對本地線程存儲的新C-API。PEP 540, 加入了一個新的UTF-8模式,默認在POSIX兼容的操作系統上開啟,可通過環境變量PYTHONUTF8 更改。
PEP 552, 為pyc格式文件新增了一個基於哈希值的源碼文件驗證。
PEP 553, 一個新增的內置函數breakpoint()使你可以更方便的進入Python或自定義Debugger。
PEP 557, 一個新裝飾器Data Classes, 減少創建類時所需要的代碼量,並可自動創建__init__,__repr__, 和 __eq__ 等方法。
PEP 560, 通過修改解釋器核心部分優化了typing模塊和泛型類型。
PEP 562, 對模塊屬性的自定義訪問。
PEP 563, 通過推遲對注釋語句的分析從而優化Python的類型提示。
PEP 564, 為time模塊新增支持返回int整數形式的納秒精度方法。
PEP 565, 再一次默認在 __main__ 處顯示Deprecation Warning如SyntaxWarning, RuntimeWarning。
PEP 567,新增contextvars模塊,通過上下文管理器以防止變量的狀態在並發代碼中意外泄漏到其它代碼。
我們最需要注意的是 相對於 3.6 來說, 3.7 在語法上幾乎是完全 兼容, 除了如下這一點:
1 Backwards incompatible syntax changes: 2 async and await are now reserved keywords.
這段話,指出了 3.7 的唯一不兼容 3.6 的地方就是 async 和 await 變成了Python語言中保留的關鍵字了。
什么意思?
就是async和await這兩個詞就像 import, for, while這些詞一樣, 是系統保留特殊用途的關鍵字。
也就是說在Python 3.7 里面你不能使用定義變量、函數之類的名字。
Python 3.7 --> Python 3.8
賦值表達式
Python 3.8最明顯的變化就是賦值表達式,即:=操作符。賦值表達式可以講一個值賦給一個變量,即使變量不存在也可以。它可以用在表達式中,無需作為單獨的語句出現。
1 while (line := file.readline()) != "end": print(chunk)"end": 2 print(chunk)
上例中,如果變量line不存在則會被創建,然后將file.readline()的返回值賦給它。然后檢查line是否為"end"。如果不是,則讀取下一行,保存在line中,然后繼續測試。
賦值表達式遵循了Python一貫簡潔的傳統,就像列表解析式一樣。其目的在於避免在特定的Python編程模式中出現一些枯燥的樣板代碼。例如,上述代碼用一般寫法需要多寫兩行代碼。
僅通過位置指定的參數
僅通過位置指定的參數是函數定義中的一個新語法,可以讓程序員強迫某個參數只能通過位置來指定。這樣可以解決Python函數定義中哪個參數是位置參數、哪個參數是關鍵字參數的模糊性。
僅通過位置指定的參數可以用於如下情況:某個函數接受任意關鍵字參數,但也能接受一個或多個未知參數。Python的內置函數通常都是這種情況,所以允許程序員這樣做,能增強Python語言的一致性。
Python文檔中給出的例子如下:
1 def pow(x, y, z=None, /): r = x**y if z is not None: r %= z return r 2 r = x**y 3 if z is not None: 4 r %= z 5 return r
符號 / 分隔了位置參數和關鍵字參數。在這個例子中,所有參數都是未知參數。在以前版本的Python中,z會被認為是關鍵字參數。但采用上述函數定義,pow(2, 10)和pow(2, 10, 5)都是正確的調用方式,而pow(2, 10, z=5)是不正確的。
支持f字符串調試
f字符串格式可以更方便地在同一個表達式內進行輸出文本和值或變量的計算,而且效率更高。
1 x = 3 print(f'{x+1}') 2 print(f'{x+1}')
輸出4。
在f字符串表達式末未添加=可以輸出f表達式本身的值,后面是計算后的值。
1 x = 3print (f'{x+1=}') 2 print (f'{x+1=}')
輸出為x+1=4。
多進程共享內存
在Python 3.8中,multiprocessing模塊提供了SharedMemory類,可以在不同的Python進城之間創建共享的內存區域。
在舊版本的Python中,進程間共享數據只能通過寫入文件、通過網絡套接字發送,或采用Python的pickle模塊進行序列化等方式。共享內存提供了進程間傳遞數據的更快的方式,從而使得Python的多處理器和多內核編程更有效率。
共享內存片段可以作為單純的字節區域來分配,也可以作為不可修改的類似於列表的對象來分配,其中能保存數字類型、字符串、字節對象、None對象等一小部分Python對象。
Typing模塊的改進
Python是動態類型語言,但可以通過typing模塊添加類型提示,以便第三方工具驗證Python代碼。Python 3.8給typing添加了一些新元素,因此它能夠支持更健壯的檢查:
-
final修飾器和Final類型標注表明,被修飾或被標注的對象在任何時候都不應該被重寫、繼承,也不能被重新賦值。
-
Literal類型將表達式限定為特定的值或值的列表(不一定是同一個類型的值)。
-
TypedDict可以用來創建字典,其特定鍵的值被限制在一個或多個類型上。注意這些限制僅用於編譯時確定值的合法性,而不能在運行時進行限制。
新版本的pickle協議
Python的pickle模塊提供了一種序列化和反序列化Python數據結構或實例的方法,可以將字典原樣保存下來供以后讀取。不同版本的Python支持的pickle協議不同,而最新版本的支持范圍更廣、更強大、更有效的序列化。
Python 3.8引入的第5版pickle協議可以用一種新方法pickle對象,它能支持Python的緩沖區協議,如bytes、memoryviews或Numpy array等。新的pickle避免了許多在pickle這些對象時的內存復制操作。
NumPy、Apache Arrow等外部庫在各自的Python綁定中支持新的pickle協議。新的pickle也可以作為Python 3.6和3.7的插件使用,可以從PyPI上安裝。
可反轉字典
Python3.6中重寫了字典,其使用了PyPy項目貢獻的一個新實現。除了更快、更緊湊之外,現在的字典還會繼承元素的順序——元素會按照添加的順序排列,就像列表一樣。Python 3.8還允許在字典上使用reversed()。
性能改進
-
許多內置方法和函數的速度都提高了20%~50%,因為之前許多函數都需要進行不必要的參數轉換。
-
一個新的opcode緩存可以提高解釋器中特定指令的速度。但是,目前實現了速度改進的只有LOAD_GLOBAL opcode,其速度提高了40%。以后的版本中也會進行類似的優化。
-
文件復制操作如shutil.copyfile()和shutil.copytree()現在使用平台特定的調用和其他優化措施,來提高操作速度。
-
新創建的列表現在平均比以前小了12%,這要歸功於列表構造函數如果能提前知道列表長度的情況下進行的優化。
-
Python 3.8中向新型類(如class A(object))的類變量中的寫入操作變得更快。operator.itemgetter()和collections.namedtuple()也得到了速度優化。
Python C API和CPython實現
Python最近的版本在CPython(C語言編寫的Python的參考實現)中使用的C API重構方面下了很大功夫。到目前為止這些工作還在不斷添加,現有的成果包括:
-
Python初始化配置(Python Initialization Configuration)有了個新的C API,可以實現對Python初始化例程更緊密的控制和更詳細的反饋。如此一來,將Python運行時嵌入到其他應用程序中就會更容易,也可以以編程方式給Python程序傳遞啟動參數。新的API還確保了所有Python配置控制都有一個單一的、一致的位置,因此以后的改變(如Python的新的UTF-8模式)也更為容易。
-
CPython的另一個新的C API——"vectorcall"調用協議——可以實現針對Python內部方法更快的調用,而無需創建臨時對象。該API依然不穩定,但已有了明顯的改善。該API計划在Python 3.9中成熟。
-
Python運行時的審計鈎子為Python運行時提供了兩個API,可以用來勾住事件,從而保證測試框架、日志和審計系統等外部工具能夠監視到它們。
