Python 3.10有哪些有用的新特性或者功能?
這是一篇補作業的文章,本文以一個資深Python開發的角度分析這個版本添加了那些有用的新功能和特性。
新的上下文管理器語法
其實就是可以使用with把多個上下文管理器(Context Manager)用括號括起來。在之前的版本,有時候我們想要在一個with裡面放多個上下文,放在一行很長,一個常見的方式是使用反斜槓,可以把open對齊:
In:withopen('input',mode='rb')asinput,\...:open('output',mode='rb')asoutput:...:......:
但是這樣寫,會讓pep8、black等代碼檢查工具報錯: 它們都認為不應該使用反斜槓顯式續行。
現在可以給這些上下文管理器加個括號就完美了(代碼潔癖者的福音):
In:with(...:open('input',mode='rb')asinput,...:open('output',mode='rb')asoutput...:):...:......:更好的錯誤提示
在官方中列舉了一些之前版本中不夠明顯友好的錯誤信息例子(延伸閱讀鏈接1)。,這個版本集中的處理了它們。本文就列出2個我感受最明顯的,其他的不挨個展示了。
對於初學者,第一次寫hello world時就可能這麼寫:
In:print("Hello,World!)File"<ipython-input-7-bc0808c61f64>",line1print("Hello,World!)^SyntaxError:EOLwhilescanningstringliteral
而新的版本提示如下:
In:print("Hello,World!)InputIn[]print("Hello,World!)^SyntaxError:unterminatedstringliteral(detectedatline1)
同樣是SyntaxError,是不是新版本的提示更容易理解呢?
再看判斷時誤用了單個等號(正確語法是雙等號==):
In:ifb=1:passFile"<ipython-input-8-e4cfea4b3624>",line1ifb=1:pass^SyntaxError:invalidsyntax
而新的版本:
In:ifb=1:passInputIn[]ifb=1:pass^SyntaxError:invalidsyntax.Maybeyoumeant'=='or':='insteadof'='?
不僅提示錯誤類型,還給了提示。
當然大家也沒必要具體了解每個錯誤提示的改進,反正知道從Python 3.10開始錯誤提示更友好了就可以了。
PEP 634: Structural Pattern Matching
這是這個版本最大的也是最重要的新Match-Case語法,具體的可以看我之前寫的文章:Python 3.10裡面的Match-Case語法詳解
類型系統
具體的可以看我之前寫的文章:Python 3.10新加入的四個和類型系統相關的新特性
zip 函數的參數
相信每個工作經驗多的Python開發都經歷過這個坑:
In:list(zip(['a','b','c'],[1,2]))Out:[('a',1),('b',2)]
當然參數內的元素長度不同時,長度長的那部分會被忽略,在沒有任何提示的情況下。其實本來它的文檔中提到了:
This continues until the shortest argument is exhausted.
但是對於大部分開發者來說沒人真的能注意這個文檔說明,這個問題是很隱性的,需要開發者了解zip的問題。而新的版本在參數內元素長度不同時,使用strict=True會報錯:
In:list(zip(['a','b','c'],[1,2],strict=True))---------------------------------------------------------------------------ValueErrorTraceback(mostrecentcalllast)InputIn,in<cellline:1>()---->1list(zip(['a','b','c'],[1,2],strict=True))ValueError:zip()argument2isshorterthanargument1模塊新增特性
這小節介紹一些模塊新增的特性,分析其用途和用法。
itertools.pairwise
itertools.pairwise是把一個可迭代對象中的元素按照順序兩個挨着的元素放一組的形式返回迭代器。
在之前的版本中可以使用more-itertools這個包,現在已經把它加入到標準庫了。
這麼用:
In:list(pairwise([1,2,3,4,5]))Out:[(1,2),(2,3),(3,4),(4,5)]In:list(pairwise('ABCDE'))Out:[('A','B'),('B','C'),('C','D'),('D','E')]
如果還不理解可以看函數文檔:
Return an iterator of overlapping pairs taken from the input iterator. s -> (s0,s1), (s1,s2), (s2, s3), ...
contextlib.aclosing
在之前已經一個標準的contextlib.closing裝飾器,而contextlib.aclosing其實就是async的版本罷了。
我們先了解下contextlib.closing的作用。如官方介紹,它其實的作用是實現下面的邏輯:
f=<module>.open(<arguments>)try:<block>finally:f.close()
使用Python讀寫文件一個好的習慣是操作完成後應該要關閉文件句柄,內置的open配合with是一個很好地實踐:
In:withopen('new.txt','w')asf:...:f.write('A\n')...:
它的原理是在塊代碼執行完成後,會通過調用ff.__exit__自動關閉文件句柄:
In:ff=open('new.txt','w')In:ff.closedOut:FalseIn:ff.__exit__()#with就會調用它In:ff.closedOut:True
但是這裡要注意,不是所有的open方法都原生支持with(或者說提供__exit__以及__enter__這個方法),當然還有其他類型的操作也需要被確保關閉(即便發生了異常)。
事實上,一般標準庫或者知名項目都會支持with語法,但是當自己寫或者公司項目里就可能沒有了, 例如我這樣定義一個類:
In:classDatabase:...:defclose(self):...:print('Closed')...:In:withDatabase()asd:...:......:---------------------------------------------------------------------------AttributeErrorTraceback(mostrecentcalllast)<ipython-input-23-f0ea085f7488>in<module>---->1withDatabase()asd:2...3AttributeError:__enter__
這樣就拋錯了。所以可以使用contextlib.closing包裝一下:
In:importcontextlibIn:withcontextlib.closing(Database())asd:...:......:Closed
而contextlib.aclosing其實就是asyncio的版本:
fromcontextlibimportaclosingasyncwithaclosing(my_generator())asvalues:asyncforvalueinvalues:ifvalue==42:breakcontextlib.AsyncContextDecorator
在Python3.2時加入了contextlib.ContextDecorator,它是contextlib.contextmanager的基礎,如類名的表達,它的作用讓上下文管理器能用作裝飾器。舉個例子就能理解了,下面是一個最基本的、不帶參數的上下文管理器:
classmycontext:def__enter__(self):print('Starting')returnselfdef__exit__(self,*exc):print('Finishing')returnFalse
所以可以使用with語法這麼用:
In:withmycontext():...:print('Inner')...:StartingInnerFinishing
如果你想把這個上下文的管理改成裝飾器怎麼辦呢? 只需要讓mycontext繼承contextlib.ContextDecorator這個基類就可以了:
In:classmycontext(contextlib.ContextDecorator):#注意這裡繼承...:def__enter__(self):...:print('Starting')...:returnself...:...:def__exit__(self,*exc):...:print('Finishing')...:returnFalse...:In:@mycontext()...:defp():...:print('Inner')...:In:p()StartingInnerFinishing
是不是很方便?所以contextlib.AsyncContextDecorator其實就是asyncio的版本:
classmycontext(AsyncContextDecorator):asyncdef__aenter__(self):print('Starting')returnselfasyncdef__aexit__(self,*exc):print('Finishing')returnFalse延伸閱讀