Python 3.X簡史——記錄3.0之后的重要更新


Python 3.0在2008年12月3日正式發布,在之后又經歷了多個小版本(3.1,3.2,3.3……),本文梳理Python 3.0之后的新特性。已更新到3.6版,會持續更新下去。

 

其實每個版本都有大量更新,都寫出來要幾百頁,這里只寫主要的更新,以及我個人認為重要的。

因此難免有失偏頗,望見諒,可以點擊小標題查看每個版本的完整What's New。

 

Python 3.1   2009年6月27日發布

▲用C語言實現io模塊。在3.0,io模塊是用Python語言實現的,性能很慢,現在比3.0快了2~20倍。

▲新增OrderedDict類,能記住元素添加順序的字典。舉個用例:讀取一個.ini文件,處理之后還能按原始順序輸出。

▲嵌套的with語句可以寫在同一行:

with A() as a, B() as b:
    suite
# 等同於以前的
with A() as a:
    with B() as b:
        suite

▲更易懂的浮點數表示。由於浮點數在CPU內部的存在形式,repr(1.1)會顯示為'1.1000000000000001'。現在顯示為'1.1',盡量確保eval(repr(f)) == f。

▲新增collections.Counter類,用於計數元素在序列中出現的次數:

>>> Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
Counter({'blue': 3, 'red': 2, 'green': 1})

▲str.format()支持自動編號:

"{} is {} {}".format('A', 'B', 'C')
# 相當於
"{1} is {2} {3}".format('A', 'B', 'C')

▲format()和str.format()支持千分位分隔符:

>>> format(1234567, ',')
'1,234,567'
>>> format(1234567.837, ',.2f')
'1,234,567.84'

新增tkinter.ttk模塊。使tkinter控件呈現操作系統本地化風格,在windows下像windows,在mac下像mac,在linux下像linux。tkinter.ttk還包含了6個以前缺失的常用控件:Combobox, Notebook, Progressbar, Separator, Sizegrip, Treeview。

round(x, n),如果x是整數,則返回一個整數。之前返回的是一個浮點數。

itertools新增兩個函數:

# 一個排列組合函數 combinations_with_replacement
>>> [p+q for p,q in combinations_with_replacement('LOVE', 2)]
['LL', 'LO', 'LV', 'LE', 'OO', 'OV', 'OE', 'VV', 'VE', 'EE']

# 一個按true進行選擇的函數 compress
>>> list(compress(data=[0,1,2,3,4,5,6,7,8,9], selectors=[0,0,1,1,0,1,0,1,0,0]))
[2, 3, 5, 7]

如果一個目錄或一個zip文件里有__main__.py文件,把目錄名或zip文件名傳給Python解釋器就可以啟動程序。

▲新增importlib模塊。把import的功能做成模塊,並提供一些相關API。

64位版的int快了27%~55%以前32位版、64位版int的計算單元都是15比特,現在64位版是30個比特

UTF-8、UTF-16、LATIN-1編碼的decode速度是以前的2~4倍。

▲json模塊用C語言擴展,性能更快。

 

Python 3.2   2011年2月20日發布

▲新增argparse模塊,解析命令行參數。用於替代功能有限的optparse模塊。

▲新增concurrent.futures模塊。定義了異步運行callable對象的接口(Future類),並提供了兩個異步運行管理器:線程池、進程池。

▲標准庫進行了大量改進,主要的幾個改進:

  1. email、mailbox、nntplib這3個模塊能更好的處理bytes/str混合的情況。
  2. ssl模塊加入許多新東西,更好的SSL連接和安全認證。
  3. 有更多的類實現了上下文管理器,可以用於with語句。

▲增加html模塊。在Python 3.2里,只有escape一個函數,用於轉義html字符:

>>> html.escape('x > 2 && x < 7')
'x &gt; 2 &amp;&amp; x &lt; 7'

▲增加functools.lru_cache()裝飾器,用於緩存函數的參數和返回值。以相同的參數再次調用函數時,不必再執行函數,直接返回緩存的返回值。

▲itertools增加accumulate函數。累加(也可進行累減、累乘、累X等操作):

>>> from itertools import accumulate
>>> list(accumulate([8, 2, 50]))
[8, 10, 60]

▲字節串decode時支持'strict'和'ignore'錯誤處理,字符串encode時支持'strict'和'replace'錯誤處理。

▲優化表達式 x in {1, 2, 3}的性能。如果集合的元素都是常量,在編譯時就會被放到一個frozenset里。

if extension in {'xml', 'html', 'xhtml', 'css'}: do(something)

▲優化pickle模塊。性能快了數倍。

▲優化list.sort()和sorted()。當使用key參數時,比以前快15%~40%。

▲logging模塊新增基於dict的配置方式,配置方式更靈活一些。

▲WSGI版本更新到1.0.1。WSGI是Python程序和Web服務器之間的一種通用接口(詳細介紹),1.0.1僅為Python3做了少量修改。

▲定義了一套穩定的ABI(二進制接口)。以前,每發布一個版本的Python,就要把第三方擴展重新編譯一遍;現在提供了一套有限但穩定的ABI,很多情況下不必重新編譯了。見PEP 384。

▲改進.pyc文件的命名方式,以便區分不同版本Python生成的.pyc文件。

▲更新Unicode數據到6.0。

 

Python 3.3   2012年9月29日發布

▲新增yield from語法,見PEP 380

yield from g
# 等同於以前的
for v in g:
    yield v

此外,yield from在外部調用者和內部生成器之間建立一個透明通道,簡化中間環節的一些處理,見參考1參考2

▲重新組織了操作系統相關異常的體系。更簡化、有更好的粒度,並且基本上不必處理errno了。見PEP 3151

▲新增lzma壓縮模塊,就是7-zip使用的壓縮算法。

▲新增ipaddress模塊,用於處理IPv4、IPv6的IP地址,詳細介紹

▲新增venv模塊。用於創建虛擬環境,每個虛擬環境都能安裝一套獨立的第三方模塊,可以更靈活的部署多個項目。

▲新增faulthandler模塊。用於調試,能找出crash、超時、死鎖等問題發生的位置。

▲新增unittest.mock模塊。用於測試,可以模擬某個對象(類、實例、函數)的行為,從而方便測試。(詳細介紹

▲memoryview類的實現做了大量改進。memoryview可直接訪問一個對象的內部數據(要求該對象支持Buffer Protocol協議),從而避免復制數據,當數據非常大時很有用。

▲用C語言重寫了decimal模塊,比以前快12倍(數據庫操作)~120倍(高密度計算)。

▲隨機hash。用一個隨機數參與hash值的計算,這個隨機數在啟動python時生成。防止hash性能攻擊。

▲字符串的內部存儲更靈活。以前每個Unicode字符都占4字節,現在根據整個字符串的需要占1、2、4字節(日常使用的漢字基本上就用2個字節)。在一項Django測試中,內存使用比以前少了2~3倍。見PEP 393

▲新增Windows下的啟動器,py.exe和pyw.exe。

如果同時安裝了多個版本的Python,比如python 2.7、3.2、3.3(32位版、64位版),可以指定啟動的版本:

# 啟動Python 3.2
py -3.2
# 用Python 2.7運行a.py py -2.7 a.py # 用Python 3的最高版本運行a.py py -3 a.py
# 用Python 3.3 32位版運行a.py py -3.3-32 a.py

另外,在.py文件的第一行像這樣寫上Python版本,用py a.py命令運行就會使用指定的版本:(類似linux的shebang line

#! python2.7
#! python3

▲xml.etree.ElementTree默認使用C語言加速,以提高性能。

▲utf-8編碼快了2~4倍,utf-16編碼的encode最多快了10倍。

▲增加可以共享key的字典。同一個類的多個實例,它們儲存屬性的字典現在可以共享key和hash了,當有大量實例時能節省內存。見PEP 412

▲增加python 2的字符串u修飾符:u'this is a string'。這個在python 3里沒意義,僅僅是為了python 2程序遷移更方便。

▲更新Unicode數據到6.1。

 

Python 3.4   2014年3月16日發布

▲集成了pip。用於安裝、更新、卸載pypi上的第三方模塊。

▲新增asyncio模塊,關於協程的模塊。這個模塊提供了一個協程的事件循環器,以及一些具體工具。(線程、進程是互爭資源,而協程是互讓資源,主要用於異步處理並發I/O。協程是用生成器實現的。)

▲新增enum模塊,提供枚舉。好多人抱怨python沒有枚舉,現在有了,雖然是以模塊的方式實現的。

▲新增pathlib模塊,用面向對象的方式表示磁盤路徑。簡化路徑操作,可以在Windows上處理linux路徑、或反之。

from pathlib import Path

p = Path('/etc')
q = p / 'init.d' / 'reboot'
q.is_dir()

for one in q.iterdir():
    with one.open() as f:
        f.read()

▲新增statistics模塊。提供了基本的統計功能,比如平均值、中位數、方差、標准差等。

▲新增selectors模塊。基於已有的select模塊,提供了一個更高級的接口。這兩個模塊用於IO復用模型(select、poll、epoll等)。

▲新增tracemalloc模塊。一個調試工具,用於追蹤、統計python的內存分配。

▲html模塊新增unescape函數。用於反轉義html字符。

>>> html.unescape('x &gt; 2 &amp;&amp; x &lt; 7')
'x > 2 && x < 7'

▲functools模塊新增singledispatch裝飾器。用於定義單分派函數——傳入不同類型的參數,函數可以有不同行為:

>>> @singledispatch
... def fun(arg):
...     print('default', arg)
...
>>> @fun.register(int)
... def _(arg):
...     print('int', arg)
...
>>> fun('hello')
default hello
>>> fun(123)
int 123

▲pickle新增第4版協議。支持嵌套的類、巨大的對象、更多的類型,以及其它一些改進。但是直到python 3.6,pickle的默認協議還是第3版。

▲默認情況下,新創建文件的文件描述符不再允許被新進程(如子進程)使用。

▲python解釋器的啟動快了30%。

▲改進str和bytes的hash算法,更能抵御DOS攻擊。並且允許在編譯CPython時更換別的hash算法。

▲允許在編譯CPython時更換內存分配器。

▲啟用Argument Clinic。在C語言寫的底層函數被Python代碼調用時,須要把參數解析成C語言的形式,使用Argument Clinic能更簡便的生成參數解析代碼。見PEP 436

▲更新Unicode數據到6.3。

 

Python 3.5   2015年9月13日發布

▲為協程新增async和await語句,見PEP 492(中文版)

1、把協程的概念從生成器獨立出來,並為之添加了async/await語句。

async def read_data(db):
    data = await db.fetch('SELECT ...')
    ...

注:在CPython的內部實現,協程仍然是一個生成器。

2、增加“異步迭代器”,異步迭代器的__aiter__、__anext__函數是協程,可以將程序掛起。

async for data in cursor:
    ...

3、增加“異步上下文管理器”,異步上下文管理器的__aenter__、__aexit__函數是協程,可以將程序掛起。

async with lock:
    ...

▲新增矩陣乘法運算符@,如a @ b。

▲解包(unpacking)。*用於可迭代對象,**用於字典,見PEP 448

>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4, *(5, 6, 7)}
{0, 1, 2, 3, 4, 5, 6, 7}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}

▲新增zipapp模塊。把Python程序用Zip打包到一個可執行的.pyz文件,見PEP 441

# 生成.pyz可執行壓縮包
python -m zipapp myapp -m "myapp:main"
# 以后就可以這樣運行myapp了,也可以鼠標雙擊.pyz文件運行
python myapp.pyz

▲允許bytes類型使用%格式化:

>>> b'Hello %b!' % b'World'
b'Hello World!'
>>> b'x=%i y=%f' % (1, 2.5)
b'x=1 y=2.500000'

▲新增Type Hints和typing模塊。方便(IDE等工具)靜態分析代碼,程序在實際運行時會忽略掉這些東西。見PEP 484

# 使用Type Hints聲明:參數name的類型是str,函數返回值的類型是int
def namelength(name: str) -> int:
    return len(name)

其實在Python 3.0就增加了函數注釋(Function annotation,PEP 3107),但是函數注釋可以很隨意:

def namelength(name: '參數是一個字符串') -> '返回值是一個整數':
    return len(name)

這次的Type Hints采用了函數注釋的語法,結合一定規范、typing模塊,可以精確定義參數、返回值的類型。

▲bytes、bytearray、memoryview新增.hex()函數:

>>> b'\xf0\xf1\xf2'.hex()
'f0f1f2'

▲math模塊新增math.isclose()函數,判斷兩個數值是否相近,可以忽略浮點表示法帶來的誤差:

>>> math.isclose(1.1, 1.1000000000000001)
True

另增加math.gcd()函數,計算最大公約數。

▲新增os.scandir()函數,更快、更省內存的遍歷文件夾。在POSIX系統上比以前快3~5倍,在Windows系統上快7~20倍。os.walk()目前也在使用此函數。

▲用C語言重寫了OrderedDict,快了4~100倍。

▲functools.lru_cache()的大部分改用C語言實現,快了10~25倍。

▲Windows版由VC2015編譯,並且要求所有擴展也用VC2015編譯。

▲Python 3.5不再支持Windows XP及之前的系統,Python 3.4成了XP能用的最高版本。

▲提供Windows下的免安裝綠色版,僅適合打包發布、不適合用於開發。在官網下載:Windows x86/x86-64 embeddable zip file。

▲不再使用.pyo文件名。-O和-OO選項不再生成xxx.pyo文件,而是分別生成xxx.opt-1.pyc和xxx.opt-2.pyc。

▲更新Unicode數據到8.0。

 

Python 3.6   2016年12月23日發布

▲在f""修飾的字符串里直接使用變量,見PEP 498

>>> name = "Fred"
>>> f"He said his name is {name}."
'He said his name is Fred.'
>>> age = 50
>>> f"My age next year is {age+1}."
'My age next year is 51.'

▲在代碼中,可以用下划線增加數值的可讀性。見PEP 515

# 下划線可以出現在數字之間、進制指示符之后。
# 不能出現在首、尾,也不能出現連續的。
amount = 10_000_000.0 addr = 0xCAFE_F00D flags = 0b_0011_1111_0100_1110 # 也能作用於字符串轉換 flags = int('0b_1111_0000', 2)

▲新增“異步生成器”。現在某些“異步迭代器”可以簡化成“異步生成器”了,見PEP 525

async def ticker(delay, to):
    """每隔delay秒,yield一個從0到to的數字"""
    for i in range(to):
        yield i
        await asyncio.sleep(delay)

▲新增“異步生成式”。在list、set、dict的生成式中,可以(通過async for)使用異步迭代器,也可以(通過await)調用協程。見PEP 530

# 可以用async for語句使用異步迭代器
result = [i async for i in aiter() if i % 2]
# 也可以用await語句調用協程
result = {await fun() for fun in funcs}
# 同時使用await和async for語句
result = {fun: await fun() async for fun in funcs if await something}

▲新增secrets模塊,生成強隨機數。以前的random模塊只能生成偽隨機數,官方推薦在涉及安全問題時不再使用random模塊。

▲hashlib模塊新增SHA-3算法。這個算法包括4個hash函數:SHA3-224、SHA3-256、SHA3-384、SHA3-512,輸出是末尾數字指定的比特數;還包括2個擴展hash函數:SHAKE128、SHAKE256,輸出是不大於128或256的任意比特數。

▲新增變量注釋(Variable Annotations)。和Type Hints類似,用於(IDE等工具)靜態分析代碼。見PEP 526

# 聲明primes是列表,其元素的類型為int。並給primes賦值一個空列表。
primes: List[int] = []
# 聲明captain的類型是str,注意這里無初始值
captain: str 
# 聲明stats是類變量,類型是字典(key的類型為str、value的類型為int)。並給stats賦值一個空字典。
class Starship:
    stats: ClassVar[Dict[str, int]] = {}

▲更簡便地自定義類的創建。有些情況下不必使用元類了,見PEP 487

class Philosopher:
    # 增加__init_subclass__方法
    # 在創建子類時,會調用父類的__init_subclass__,cls參數是子類
    def __init_subclass__(cls, default_name, **kwargs):
        super().__init_subclass__(**kwargs)
        cls.default_name = default_name

class AustralianPhilosopher(Philosopher, default_name="Bruce"):
    pass

PEP 487另給descriptor增加__set_name__方法。在創建descriptor所屬的類時被調用,讓descriptor可以感知被賦予的變量名。

普通的字典現在能記住元素的添加順序。

這不是Python的改動,僅是CPython的改動,因此官方並不鼓勵用戶依賴它的順序。

▲得益於字典的順序,3.6有兩個改動:

1,函數的參數**kwargs可以保留順序,見PEP 468

def save(node, **kwargs):
    ....
# 現在能記住參數id、href、title出現的順序了
save(mynode, id=i, href=url, title=s)

2,能記住“類屬性”的定義順序,見PEP 520。在以前,需要定義一個元類才能知道a、b、c的定義順序。

class MyClass:
    a = 1
    b = 2
    c = 3

▲現在,global和nolocal語句必須出現在實際使用該變量之前。不這樣的話,在以前僅會給一個SyntaxWarning。

▲新增(文件系統)路徑協議:如果一個對象有.__fspath__(self)函數、並且返回值是str或bytes類型,則說明這個對象能表示路徑。同時增加os.fspath函數,與__fspath__配套使用。

主要目的:(1)防止不相關的對象在str(obj)后被誤當作路徑使用,(2)統一各模塊表示路徑的接口。見PEP 519

▲給datetime.time和datetime.datetime的對象增加.fold屬性。用於區分兩個相同(但UTC不同)的當地時間,常見於夏令時結束的那天。如圖,橫軸是UTC時間,縱軸是當地時間:

▲在Windows操作系統上,CPython與控制台的通訊使用Unicode編碼(CPython <-> utf-8 <-> utf-16-le <-> Windows操作系統),而不再使用當前的Code Page。見PEP 528

好處是可以print、input所有Unicode字符(前提是字體也支持)。

# 一段代碼,print一個印度字符
print('')

# 在Python 3.5上,ള被encode成當前Code Page發往控制台,會出現異常:
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    print('\u0d33')
UnicodeEncodeError: 'gbk' codec can't encode character '\u0d33' in position 0: illegal multibyte sequence

# 在Python 3.6上,會encode成unicode(先utf-8、再utf-16-le)發往控制台,可以打印。
# (由於控制台字體的原因,可能會顯示成方塊):

與此類似,CPython與Windows文件系統的通訊編碼也從當前的Code Page改成Unicode了,見PEP 529

▲用C語言實現asyncio.Future類和asyncio.Task類,asyncio程序的性能快了25%~30%。

▲glob模塊的glob()函數和iglob()函數現在使用os.scandir()函數。快了3~6倍。

▲pathlib.Path模塊的glob()函數現在使用os.scandir()函數。快了1.5~4倍。

▲增加一個frame evaluation的C語言API,為第三方給CPython實現函數級JIT引擎提供了可能,IDE也可以用這個API實現性能更快的調試功能,見PEP 523。

▲更新Unicode數據到9.0。Unicode 9.0支持西夏文字。

 

Python 3.7   預計2018年6月15日發布

▲新增內置函數breakpoint(),進入(Python自帶或IDE提供的)調試模式。
環境變量設置PYTHONBREAKPOINT=0會忽略此函數。

foo()
breakpoint()  # 在執行foo()后、執行bar()前,進入調試模式
bar()

▲對Annotations延緩求值。於是Annotations可以向前引用了,見B類:

class C:
def validate_b(self, obj: B) -> bool: ... class B: ...

這項改動向后不兼容,需要以如下方法啟用。在Python 4.0會成為默認行為。

from __future__ import annotations

新增dataclasses模塊,PEP 526
Data Class可被視為:可有默認值的、元素可變的NamedTuple。
允許使用類語法:繼承、元類、docstrings、定義方法,等等。

@dataclass class Point:
    x: float  # 這里的float沒有強制性,只是增加代碼可讀性
    y: float
    z: float = 0.0

p = Point(1.5, 2.5)
print(p)   # 輸出 "Point(x=1.5, y=2.5, z=0.0)"

▲新增contextvars模塊,針對異步任務提供上下文變量,見PEP 567
decimal模塊已使用此模塊,在async/await代碼中可以保持Context。

▲time模塊新增6個可以訪問納秒的函數,如time.time_ns()、time.perf_counter_ns()等等。

▲新增-X dev選項,啟用開發模式。在程序運行時進行一些開銷昂貴的檢測,見文檔

▲__main__中的代碼會顯示棄用警告(DeprecationWarning)。

▲新增UTF-8模式,見PEP 540
在Linux/Unix系統,將忽略系統的locale,使用UTF-8作為默認編碼。(還會把本進程、子進程的locale設置為UTF-8,以便C語言擴展與Python保持一致,見PEP 538
在非Linux/Unix系統,需要使用-X utf8選項啟用UTF-8模式

允許模塊定義__getattr__、__dir__函數,為棄用警告延遲import子模塊等提供便利,見PEP 562

新的線程本地存儲C語言API,見PEP 539

▲更新Unicode數據到11.0。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM