繼3.7版本之後Python再次發布了新版本,雖然新版本帶來了不少調整,但是其中很大一部分都是對代碼底層設計的修改,又或是typing、pickle等不常用的功能,對多數用戶而言影響不大,今天我想重點聊一聊那些將對我們的代碼編寫產生較大影響的新功能。
在體驗開始前先說下準備工作,由於Python3.8還沒有正式發布,因此通過Anaconda的多版本管理搭建Python3.8新環境的方法是行不通的,我的做法是到官網下載對應的最新版本後單獨安裝。
為了避免與現有環境衝突,將其更名為Python38(下圖),下文中的Python如無特殊說明均為Python3.6, Python38為Python3.8。接下來就正式開始新特性體驗。
我們都知道Python中的字典是無序的,Python3.6對這一問題進行了修訂,默認情況下會按照鍵的創建順序進行排序,但也僅限於此,你無法像列表那樣對字典直接進行排序操作。
這一情況在Python3.8中進一步得到改善,Python3.8中reversed()方法增加了對字典對象的支持,可以對字典進行逆序操作。
在下面這段代碼中,對字典進行簡單的迭代,將會按照順序輸出字典的鍵。
現在改變一下代碼,加入reversed()方法:
先來看使用Python3.6的運行結果(下圖),可以看到在Python3.6中,字典是不支持recersed()方法的。
然後用Python3.8運行結果如下可以看到,字典按照鍵創建順序的逆序進行了輸出。雖然只是非常小的一點功能提升,但是在某些場景下對於字典對象的應用可能會起到非常關鍵的作用。
在Python3.8中的參數傳遞方面引入了一個新的特性:PEP 570 Positional-Only Argument——限定位置參數,下面就詳細聊聊這究竟是怎麼回事。
一般來說,Python中的參數傳遞有三種形式:位置參數、關鍵字參數和可變參數,為了避免不必要的麻煩,規定在可變參數之後只允許使用關鍵字參數。可是即便如此還是給程式設計師們留下了很大的自由空間,比如在可變參數之前,位置參數和關鍵字參數的使用幾乎不受限制。這樣就出現了一個問題,假如一個團隊中很多人進行合作開發,函數的定義形式和調用模式是很難規範和統一的。
因此Python3.8就引入了一個「Positional-Only Argument」的概念和分隔符「/」,在分隔符「/」左側的參數,只允許使用位置參數的形式進行傳遞。
舉個例子來進行說明,首先建立下面這樣一個函數,由於函數中使用了分隔符「/」,因此只能使用Python3.8運行。
def add_num(x, y, z=100, /, a=100):
print(x + y + z + a)
嘗試以下面這種方式調用函數:
結果在運行的時候發生了報錯:
接著嘗試全部以位置參數的形式調用函數(如下),結果順利執行。可見「Positional-Only Argument」對分隔符「/」右側的參數形式並沒有限制。
# 輸入
add_num(1, 2, 4, 5)
# 輸出
12
那麼如果只給定前兩個參數,後面兩個參數使用默認值又如何呢?通過下面的調用可以發現,也是可以正常運行的。
通過上面這個例子我們發現Python3.8對於參數傳遞的限制僅僅作用於分隔符「/」的左側,而且只是在函數調用時發生作用。
Python3.8中新增了賦值表達式「:=」操作符,簡單來說就是把運算操作和賦值操作放在了一起,有點類似於「a+=b」這種表達方式,我想賦值表達式的出現應該是python追求簡潔的傳統理念所致。
來看下面這段代碼,在func函數的if語句中,運算、賦值、判斷操作在同一條語句中完成,即使變量a原本不存在也沒關係。
# 輸入
def func(x, y, z):
if (a := x + y) != z:
print(a)
else:
print(z)
func(1, 2, 5)
# 輸出
3
當然,就上面這段代碼本身來看,將 x+y 的結果進行賦值似乎意義不大,但是如果運算表達式的計算量非常大或者要進行大規模獨寫等操作的話,重複執行對代碼的效率將造成大的影響;而如果事先對運算表達式賦值則需要多寫一行代碼。
目前來看,賦值表達式最重要的作用就是使代碼變得更加簡潔,至於運行效率的差異,目前還沒有驗證。
在之前的Python版本中,「f表達式」——f'{expr}'的作用與eval()函數基本相同,例如:
f'{[1, 2, 3, 4, 5, 6]}'的結果是列表[1, 2, 3, 4, 5, 6];
f'{3 + 2}'的結果是運算後的值5。
Python3.8中對該功能進行了優化,f'{expr}'語句中增加了對等號「=」的支持,在保留原來功能的基礎上,還能夠同時輸出運算表達式本身。
例如執行先面這段代碼,除了計算並輸出運算結果外,還會將「=」和其左側的算式一併輸出:
x = 3
print(f'{x * 2 = }')
執行結果:
f'{expr}'不僅適用於基本的算術運算,還能夠進行其他對象的操作,以列表為例,令lst=[1, 2, 3, 4, 5, 6],現在對其進行擴展操作:
lst = eval('[1, 2, 3, 4, 5, 6]')
print(f'{lst = }')
print(f'{lst + [7] = }')
運行結果如下:
函數運算同樣適用,例如對兩個列表求交集,執行下面這段代碼:
lst1 = [1, 2, 3, 4, 5]
lst2 = [3, 5, 7]
print(f'{list(set(lst1).intersection(set(lst2))) = }')
運行結果:
相比僅輸出結果,連帶運算表達式一起輸出有助於定位檢查,在調試代碼的時候使用真的是快捷又方便。
進程是系統進行資源分配的獨立單位,在以前的python版本中,進程間的數據交互只能通過Queue、Pipes等方式來實現,數據無法直接共享。
在Python 3.8中,multiprocessing模塊提供了SharedMemory類,可以在不同的Python進程之間創建共享的內存block。目前支持int、float、str、bytes、bool、None、numpy.ndarray等一部分Python對象。
還是舉個例子來進行說明,在下面這段代碼中建立了2個進程,在進程1中對列表中的每個元素執行+10操作,進程1結束後執行進程2,輸出列表內容。
由於進程之間數據無法共享,因此進程2中輸出的列表是沒有進行過+10操作的內容:
現在我們對代碼進行一下小小的修改,nums不是作為一個普通的list,而是作為一個共享內存對象來創建,代碼如下:
由於shared_memory是Python3.8中的新增內容,因此在Python3.6下運行會出錯,我們還是用Python3.8來運行這段代碼(結果如下)可以看到,進程2中輸出的結果與進程1中是一樣的,兩個進程之間通過shared_memory實現了數據共享。
當然,shared_memory在實際應用中肯定不會如此簡單,
Python3.8發布的新特性和新功能還有很多,對一些內置模塊的改進和優化則更多,想嘗鮮的同學可以百度了解更多相關內容。你對Python3.8新特性怎麼看,歡迎吱一聲留言!
—————END—————
喜歡本文的朋友們,歡迎長按下圖關注訂閱號程式設計師小灰,收看更多精彩內容
歡迎掃碼關注公眾號 程式設計師內推圈,優秀的內推機會等著你!