快樂的遷移到 Python3
Python部落(python.freelycode.com)組織翻譯,禁止轉載,歡迎轉發。
為數據科學家提供的 Python3 特性簡述
在機器學習和其他需大批量操作數據的科學領域,Python 已經成為了一門主流語言.它擁有各種深度學習框架和一系列完善的數據處理和可視化工具.
然而,Python 體系中 Python2 和 Python3 共存,而且很多數據科學家仍然在使用 Python2.到2019年底,科學棧將停止支持 Python2.至於 numpy,2018年以後的所有新版本將只支持 Python3.
為了讓過渡更輕鬆一些,我整理了一些 Python3 你可能覺得有用的特性.
圖片來自Dario Bertini post (toptal)
使用pathlib更好的處理路徑
pathlib 是 Python3的一個默認模塊,有助於避免大量使用 os.path.join
之前,人們傾向於使用字元串連接(簡潔,但明顯不好). 現在,使用 pathlib 之後,代碼更加安全,簡潔,可讀性強.
另外 pathlib.Path 還有大量的方法和屬性,每個 python 初學者在早期都需要通過 Google 來了解:
pathlib 應該會為你節省大量時間,請參閱文檔和參考資料以獲取更多信息.
類型提示現在已經是語言的一部分
pycharm 中類型提示的例子:
Python 已不再是一個小的腳本語言了,現如今,數據管道包含了不同的級別,每個級別又涉及到不同的框架(有時會有千差萬別的邏輯).
引入類型提示是為了應對日益增加的程序複雜性,這樣機器能夠對代碼進行輔助驗證.以前不同的模塊使用自定義的方式來在文檔字元串中指明變數的類型)(提示:pycharm 可以將舊的文檔字元串轉換為新的類型提示).
舉一個簡單的例子,下面的代碼適用於不同的數據類型(這也是我們喜歡 python 數據棧的地方)
這段代碼可適用於 numpy.array(包含多維的), astropy.Table 和 astropy.Column, bcolz, cupy, mxnet.ndarray 和其他組件.
這段代碼雖然也適用於 pandas.Series, 但卻是錯誤的使用方式:
這只是兩行代碼而已.設想一下僅僅一個函數運行不當,會導致一個複雜系統有多少不可預知的行為.在一個大型系統中,明確指出方法所需的類型是非常有幫助的.如果給函數傳遞了異常參數,它會給出警告.
如果你有一個重要的代碼庫,像 MyPy 這樣的提示工具很可能會成為你持續集成管道的一部分. Daniel Pyrathon 主持的"Putting Type Hints to Work"網路研討會是一個很好的簡介.
旁註:不幸的是,提示信息還不能強大到為 ndarrays/tensors 提供細粒度的類型支持,可終將會實現,這也將成為 DS 的一大特色.
類型提示→運行期類型檢查
默認情況下,函數注釋並不會影響你代碼的運行方式,它僅僅會幫助你指出代碼的意圖.
然而,你可以使用類似 enforce 這樣的工具在代碼運行期進行類型檢查,這對你進行 debug 是很有幫助的(很多情況下,類型提示並不生效).
函數注釋的其他用途
如前文所述, 注釋對代碼運行沒有影響,只是提供了一些元信息,你可以隨意使用.
例如, 測量單位是科學領域常見的痛點, astropy 包提供了一個簡單的裝飾器來控制輸入數量的單位並轉換為輸出部分所需的單位.
如果你正在用 python 處理表格式的科學數據(沒必要是天文數字), 你可以嘗試一下 astropy.
你也可以自定義專用的裝飾器,以同樣的方式來執行輸入和輸出的控制/轉換.
矩陣乘法 @
讓我們來實現一個最簡單的 ML 模型 — 一個具有 l2正則化的線性回歸(又名嶺回歸)
使用@後的代碼在深度學習框架之間更有可讀性和可轉換性:對於單層感知器, 相同的代碼X @ W + b[None, :] 都可運行於 numpy, cupy, pytorch, tensorflow (以及其他基於張量運行的框架)
通配符**
在 Python2 中遞歸文件夾通配並不容易,即使 glob2 的自定義模塊已經解決了這個問題.自 Python 3.5以來便支持了遞歸標識:
一個更好的選擇是在 python3 中使用 pathlib(減少了一個導入!):
注意:在 glob.glob, Path.glob 和 bash 通配符之間有些微不同.
Print 現在是一個函數
代碼中雖然多了一些煩人的括弧,但也有一些好處:
簡化了使用文件描述符的語法
不使用 str.join 列印製表符對齊表:
結束/重定向列印輸出:
在 jupyter 中,最好將每個輸出記錄到一個單獨的文件中(以便追蹤斷開連接後發生的情況),現在ni 可以重寫 print 了.
下面是一個暫時覆蓋列印行為的上下文管理器:
並不推薦這種方法,但卻是現在能接受的一個非常規的解決方式.
print 可以加入列表推導和其他語言結構
數字中的下劃線(千位分隔符)
PEP-515 在數字中引入了下劃線.在 Python3 中,下劃線可用於在整數,浮點數以及複雜數字中以可視化的方式對數字進行分組.
用於簡單可靠格式化的 f-strings
默認的格式化系統提供了數據實驗中不必要的靈活性.由此產生的代碼對於任何修改都顯得太冗餘和太脆弱.通常數據科學家會以一種固定格式反覆輸出一些日誌信息.如下代碼就是常見的一段:
例子輸出:
Python3.6 引入了格式化字元串 f-strings:
true 除法 和 floor 除法的明顯區別
對於數據科學來說,這絕對是一個便利的改變
Python2 中的結果取決於 "time" 和 "distance"(以米和秒計量)是否存儲為整數.而在 Python3 中,因為除法的計算結果是浮點數,因此在兩種情況下結果都是正確的.
另外一種情況是 floor 除法,現在可以精確運算了:
簡言之:
注意:這都適用於內置類型和數據包提供的自定義類型(如 numpy 和 pandas)
嚴格排序
防止偶爾對不同類型的實例進行排序
有助於發現處理原始數據時出現的一些問題
旁註:合理檢查 None (兩個 Python 版本中都有)
用於 NLP(神經語言程序學 Neuro-Linguistic Programming) 的 Unicode
輸出:
Python 2: 6
Python 3: 2 您好
Python2 失敗了, Python3 符合預期(因為我在字元串中使用了俄文)
在 Python3 中,str 是 unicode 字元串, 對於非英文文本的 NLP 處理更為方便.
還有一些有趣的東西,比如:
Python 2: Counter({"?": 2, "b": 1, "e": 1, "c": 1, "k": 1, "M": 1, "l": 1, "s": 1, "t": 1, "?": 1, "?": 1})
Python 3: Counter({"M": 1, "?": 1, "b": 1, "e": 1, "l": 1, "s": 1, "t": 1, "ü": 1, "c": 1, "k": 1})
雖然在 Python2 中你可以正確的處理這一切,但 Python3 顯得更加友好.
保持字典和 **kwargs 的順序
在 CPython 3.6+ 中,字典的默認行為與 OrderedDict 類似(並且在 Python 3.7+ 中也已經得到保證),這樣在字典推導時保持了順序(還有一些其他操作,比如: json 的序列化/反序列化)
同樣適用於 **kwargs (Python 3.6+),他們會保持跟傳參時一樣的順序.順序對於數據管道來說至關重要,但以前我們只能以繁瑣的方式來編寫:
你注意到了嗎?名字的唯一性也會自動檢查.
可迭代對象的解壓
默認的 pickle 引擎為數組提供更好的壓縮
Pickling 是線程/進程間傳輸數據的一種機制, 特別應用在多處理包中.
只用了1/3的空間,並且快了很多.實際上設置參數protocol=2也可以實現類似的壓縮操作(速度不會這麼快),但開發者們一般都會忽略這個選項(或者根本不知道有這個選項).
注意: pickle 並不安全(且非常不好轉換), 所以在接收到不信任或者未經證實的數據後,不要 unpickle 這些數據.
更安全的推導式
超簡單的 super 函數
Python2中的 super(...) 曾是代碼中最常見的錯誤源.
stackoverflow 上有更多關於 super 和 MRO(方法解析順序)的信息。
IDE 提供了更好的變數注釋建議
在 Java, C#以及其他類似語言編程過程中,最享受的事情就是 IDE 可以給出很好的建議, 這是因為程序運行前每個標識符的類型都是已知的.
在 Python 中這很難實現, 但注釋可以提供一些幫助
用清晰的格式寫下期望的類型
從 IDE 中獲取到好的建議
這是 PyCharm 中帶有變數注釋的建議示例。即使在使用的函數沒有注釋的情況下,依舊有效(取決於向後的兼容性).
多重 unpacking
現在展示如何合併兩個字典:
請參照在 StackOverflow 中的這一過程, 與 Python2 做個比較.
對 lists, tuples 和 sets(a, b, c 是可任意迭代的), 此方法同樣有效:
同樣支持函數中的 *args 和 **kwargs 參數:
具有關鍵字參數的面向未來的 API
讓我們看一下這段代碼:
顯然, 代碼作者並不了解 Python 的編碼風格(很有可能是從 app 或者 rust 轉到 Python 的).不幸的是, 這還不僅僅是一個品位的問題, 因為在 SVC 中改變參數的順序(添加/刪除)會破壞這段代碼. 尤其是 為了提供一致性的 API, sklearn 會時不時的對大量演算法參數做重排序/重命名操作, 每個這樣的重構都有可能會破壞代碼.
在 Python3中, 類庫作者通過使用 * 來要求明確命名參數:
用戶現在必須指定參數名稱為 sklearn.svm.SVC(C=2, kernel="poly", degree=2, gamma=4, coef0=0.5)
這種機制提供了 API 完美結合的可靠性和靈活性
次要:math模塊中的常量
次要:單一的整數類型
Python2 提供了兩種基本整數類型: int(64位有符號整數)和針對長整型計算的long(從C++之後就變得非常混亂)
Python3 只有一個單一的 int 類型, 同時融合了長整型計算.
以下展示了如何校驗一個值是整數:
其他事項
Enum 理論上很有用, 但是
string-typing 在 python 數據棧中已經廣泛被採用了
Enum 似乎不會與 numpy 以及 pandas categorical 相互作用
coroutines 在數據管道中的前景看似光明(參看David Beazley的幻燈片),但我並沒有看到他們被更廣泛的採用.
Python3 有穩定的 ABI(Application Binary Interface: 應用程序二進位介面 描述了應用程序和操作系統之間,一個應用和它的庫之間,或者應用的組成部分之間的低介面)
Python3 支持 unicode 標識(甚至 ω = Δφ / Δt 也可以), 但最好還是使用舊的ASCII名稱
一些類庫如 jupyterhub(雲端jupyter), django 和最新的 ipython 只支持 Python3, 因此一些你聽起來無用的功能,對於你可能使用的類庫卻很有用.
具體的數據科學的代碼遷移問題(以及如何解決這些問題)
不再支持嵌套參數
但是,仍然完全適用於不同的(列表)解析:
一般來說, 從 Python2 到 Python3之間的解析也有著更好的"可翻譯性".
map, .keys, .values, .items 等返回的是迭代器(iterators),而不是列表(lists). 迭代器的主要問題如下:
沒有細小的切片
不能迭代兩次
幾乎所有問題在轉化為列表後都可以解決
遇到問題時可以參考Python 幫助:如何遷移到 Python3?
機器學習和數據科學 Python 教學中的主要問題
課程作者應該花時間在第一講中解釋清楚什麼是迭代器,為什麼它不能像字元串一樣被分割/連接/相乘/迭代兩次(以及如何處理這個問題).
我想大多數課程作者都曾很樂於避開這些細節, 但現在幾乎不可能了.
結論
雖然 Python2 和 Python3 已經共存快10年了, 但我們還是應該遷移到 Python3.
轉向使用唯一的 Python3 代碼庫之後, 科研和生產代碼應該會更簡潔, 更有可讀性, 更安全.
目前大部分類庫都支持兩個 Python 版本. 我已迫不及待的等待依賴包們放棄 Python2,享受新語言特性的那個光明時刻.
後續的遷移會更加順暢:"我們再也不想做這種不向後兼容的變化了".
英文原文:https://github.com/arogozhnikov/python3_with_pleasure
譯者:郭明
※強大的視頻(中美日)下載器:Lulu
※2017年10篇優秀的Python文章
TAG:Python部落 |