Python 3.10 的首個 PEP 誕生,內置類型 zip() 迎來新特性

来源:https://www.cnblogs.com/pythonista/archive/2020/07/02/13227932.html
-Advertisement-
Play Games

譯者前言:相信凡是用過 zip() 內置函數的人,都會贊同它很有用,但是,它的最大問題是可能會產生出非預期的結果。PEP-618 提出給它增加一個參數,可以有效地解決大家的痛點。 這是 Python 3.10 版本正式採納的第一個 PEP,「Python貓」一直有跟進社區最新動態的習慣,所以翻譯了出 ...


譯者前言:相信凡是用過 zip() 內置函數的人,都會贊同它很有用,但是,它的最大問題是可能會產生出非預期的結果。PEP-618 提出給它增加一個參數,可以有效地解決大家的痛點。

這是 Python 3.10 版本正式採納的第一個 PEP,「Python貓」一直有跟進社區最新動態的習慣,所以翻譯了出來給大家嘗鮮,強烈推薦一讀。(PS:嚴格來說,zip() 是一個內置類(built-in type),而不是一個內置函數(built-in function),但我們一般都稱它為一個內置函數。)

PEP原文 : https://www.python.org/dev/peps/pep-0618/

PEP標題: Add Optional Length-Checking To zip

PEP作者: Brandt Bucher

創建日期: 2020-05-01

合入版本: 3.10

譯者豌豆花下貓 @Python貓公眾號

PEP翻譯計劃https://github.com/chinesehuazhou/peps-cn

摘要

本 PEP 建議給內置的 zip 添加一個可選的 strict 布爾關鍵字參數。當啟用時,如果其中一個參數先被用盡了,則會引發 ValueError 。

動機

從作者的個人經驗和一份對標準庫的調查 來看,明顯有很多(如果不是絕大多數)zip 用例要求可迭代對象必須是等長的。有時候,周圍代碼的上下文可以保證這點,但是要 zip 處理的數據通常是由調用者傳入的、單獨提供的或者以某種方式生成的。在這些情況下,zip 的預設行為意味著錯誤的重構或邏輯錯誤,很容易悄悄地導致數據丟失。這些 bug 不僅難以定位,甚至難以被覺察到。

很容易想到造成這種問題的簡單案例。例如,以下代碼在 items 為一個序列(sequence)時可以良好地運行,但是如果調用者將 item 重構為一個可消耗的迭代器,則代碼會悄悄地產生縮短的、不匹配的結果:

def apply_calculations(items):
    transformed = transform(items)
    for i, t in zip(items, transformed):
        yield calculate(i, t)

zip 還有幾種常見用法。慣用的技巧性用法特別容易出問題,因為它們經常被不完全瞭解代碼工作方式的用戶使用。下麵是一個示例,解包到 zip 中以轉化成嵌套的可迭代對象:

>>> x = [[1, 2, 3], ["one" "two" "three"]]
>>> xt = list(zip(*x))

另一個例子是將數據“分塊”成大小相等的組:

>>> n = 3
>>> x = range(n ** 2),
>>> xn = list(zip(*[iter(x)] * n))

在第一個例子中,非矩形數據通常會導致邏輯錯誤。在第二個例子中,長度不是 n 的倍數的數據通常也是錯誤。因為這兩個習慣用法都會悄悄地忽略不匹配的尾部元素。

最有說服力的例子來自使用了 zip 的標準庫ast ,它在 literal_eval 里產生過一個 bug,會直接丟棄不匹配的節點

>>> from ast import Constant, Dict, literal_eval
>>> nasty_dict = Dict(keys=[Constant(None)], values=[])
>>> literal_eval(nasty_dict)  # Like eval("{None: }")
{}

實際上,筆者已經在 Python 的標準庫和工具中找出了許多調用點, 立即在這些位置啟用此新特性是恰當的。

基本原理

一些評論者聲稱:布爾開關常量是一種“代碼壞氣味(code-smell)”,或者與 Python 的設計哲學背道而馳。

但是,Python 當前在內置函數上有幾個布爾關鍵字參數的用法,它們通常使用編譯期常量來調用:

  • compile(..., dont_inherit=True)
  • open(..., closefd=False)
  • print(..., flush=True)
  • sorted(..., reverse=True)

標準庫中還有許多類似用法。

這個新參數的想法和名稱最初是由 Ram Rachum 提出的。該議題收到了 100 多個回覆,而候選的“equal”也獲得了相近的支持數。

筆者對它們沒有很強烈的偏好,儘管“equal equals” 讀起來有點尷尬。它還可能(錯誤地)暗示了 zip 的對象是相等的:

>>> z = zip([2.0, 4.0, 6.0], [2, 4, 8], equal=True)

規範

當用關鍵字參數 strict=True 調用內置類 zip 時,如果參數的長度不同,則生成的迭代器會引發 ValueError。這個異常就發生在迭代器正常停止迭代的地方。

向上相容

此項更改是完全向上相容的。當前的 zip 不接受關鍵字參數,預設省略 strict 的“非嚴格”用法會保持不變。

參考實現

筆者設計了一個 C 實現

用 Python 大致翻譯如下:

def zip(*iterables, strict=False):
    if not iterables:
        return
    iterators = tuple(iter(iterable) for iterable in iterables)
    try:
        while True:
            items = []
            for iterator in iterators:
                items.append(next(iterator))
            yield tuple(items)
    except StopIteration:
        if not strict:
            return
    if items:
        i = len(items)
        plural = " " if i == 1 else "s 1-"
        msg = f"zip() argument {i+1} is shorter than argument{plural}{i}"
        raise ValueError(msg)
    sentinel = object()
    for i, iterator in enumerate(iterators[1:], 1):
        if next(iterator, sentinel) is not sentinel:
            plural = " " if i == 1 else "s 1-"
            msg = f"zip() argument {i+1} is longer than argument{plural}{i}"
            raise ValueError(msg)

被拒絕的意見

(1)添加 itertools.zip_strict

這是 Python-Ideas 郵件列表上獲得最多支持的替代方案,因此值得在此處加以討論。它沒有任何嚴重的缺陷,如果本 PEP 被否絕,它是一個很好的替代。

雖然考慮到這一點,但是在 zip 中添加可選參數可以用較小的更改而更好地解決誘發此 PEP 的問題。

(2)依照先例

itertools 中有一個 zip_longest,這似乎讓人很有動機再添加一個 zip_strict。但是,zip_longest 在許多方面是一個更加複雜且特定的程式:它負責填寫缺失的值,但其它函數都不需要操心這種事。

如果 zip 和 zip_longest 同時放在 itertools 中,或者都作為內置函數,那麼在相同的地方添加 zip_strict 就確實是一個更有效的論點。然而,新的“strict”用法在介面和行為方面,相比起 zip_longest,更接近於 zip 的概念,但又不足以成為內置對象。考慮到這個原因,令 zip 就地擴展出一個新的選項,似乎是最自然的選擇。

(3)易用性

如果 zip 能夠防止此類 bug,那麼用戶在調用的地方啟動檢查,就會變得非常簡單。與其編寫一套繁重的邏輯來處理,不如用這個新特性來直接檢查。

有人還認為,在標準庫中放一個新的函數,相比在一個內置函數上加關鍵字參數,更“容易發現(discoverable)”。筆者不同意這一論斷。

(4)維護成本

儘管在提升易用性時,具體的實現是個次要問題,但重要的是要認識到,添加新的程式比修改原有程式複雜得多。與此 PEP 一起提供的 CPython 實現非常簡單,並且對 zip 的預設行為沒有顯著的性能影響,而在 itertools 中添加一個全新的程式將需要:

  • 複製 zip 的許多現有邏輯,zip_longest 就是這麼乾的。
  • 大刀闊斧地重構 zip 或 zip_longest 或這兩者,以便共用一個公共的或者繼承性的實現(這可能會影響性能)。

(5)添加多個“模式”以供切換

如果預期有三個或更多模式(mode),這個建議才會比二元標誌更有意義。最顯而易見的三種模式是:“最短的”(當前 zip 的行為),“嚴格的”(本 PEP 提議的行為)和“最長的”(itertools.zip_longest 的行為)。

但是,除了當前的預設值以及本提案的“strict”模式,似乎不需要再添加其它模式。最可能的是添加一個“最長的”模式,但這需要一個新的 fillvalue 參數(它對於前兩種模式都沒有意義),另外,itertools.zip_longest 已經完美地處理了這種模式,若在 zip 中添加該模式,將會造成重覆。目前尚不清楚哪一個是“顯而易見的”選擇:內置 zip 上的 mode 參數,還是已經長期存在於 itertools 中的 zip_longest。

(6)給 zip 添加方法或者構造函數

考慮以下兩個被提出來的做法:

>>> zm = zip(*iters).strict()
>>> zd = zip.strict(*iters)

尚不清楚哪個更好,或者哪個更差。如果 zip.strict 作為一個方法來實現,則 zm 沒問題,但是 zd 會出現幾種令人困惑的情況:

  • 返回不包裝在元組中的結果(如果 iters 僅包含一個元素,一個 zip 迭代器)。
  • 參數類型錯誤時拋出 TypeError(如果 iters 只包含一個元素,不是一個 zip 迭代器)。
  • 否則,參數數量不對時拋出 TypeError。

如果 zip.strict 是作為 classmethod 或 staticmethod 實現,則 zd 將成功執行,而 zm 將不產生任何結果(這正是我們最初要避免的問題)。

本提案還面臨著更為複雜的問題,因為 CPython 中 zip 內置類的實現細節是未文檔化的。這意味著若選擇以上的某種行為,當前的實現就會被“鎖定”(或至少要求對其進行模擬)。

(7)變更 zip 的預設行為

zip 的預設行為沒有什麼“錯” ,因為在許多情況下,這確實是正確處理大小不等的輸入的方法。例如,在處理無限迭代器時,它非常有用。

itertools.zip_longest 已經用在仍然需要“額外”尾端數據的情況。

(8)使用回調來處理剩餘對象

儘管基本上可以執行用戶需要的任何操作,但此解決方案在處理常見問題時(例如捨棄不匹配的長度),變得不必要的複雜且不直觀。

(9)引發一個 AssertionError

沒有內置函數或內置類的 API 會引發 AssertionError。此外,官方文檔 這麼寫的(它的全部):

Raised when an assert statement fails.

由於此功能與 Python 的 assert 語句無關,因此不應該引發 AssertionError。用戶若希望在優化模式下禁用檢查(像一個 assert 語句),可以改用 strict = __debug__。

(10)在 map 上添加類似的特性

本 PEP 不建議對 map 作任何更改,因為很少使用帶有多個可迭代參數的map。但是,本 PEP 的裁定可作為將來討論類似特性的先例(應該出現)。

如果本 PEP 被拒絕,則 map 的那種特性實際上也不值得追求。如果通過了,則對 map 的更改不需要新的 PEP(儘管像所有提案一樣,都應仔細考慮其有用性)。為了保持一致性,它應遵循此處討論的跟 zip 相同的 API 和語義。

(11)什麼也不做

此建議可能最沒有吸引力。

悄悄地將數據截斷是一種特別令人討厭的 bug,而手寫一個健壯的解決方案卻並非易事。Python 自己的標準庫(前文提到的 ast)是有現實意義的反例,很容易就陷入本 PEP 試圖避免的那種陷阱。

推薦閱讀:

1、PEP中文翻譯計劃 (https://github.com/chinesehuazhou/peps-cn)

2、學習 Python,怎能不懂點PEP呢? (https://mp.weixin.qq.com/s/oRoBxZ2-IyuPOf_MWyKZyw)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 不知道大家有沒有註意過對象中的一些通用方法,例如所有所有的對象都有 toString、constructor 等等一些方法。 當然如果要仔細看的話,大家可以: var a = {}; console.log(a); 我們可以清晰的看到他有很多的內置方法。當然,也可以看到最下麵有兩個比較怪的方法 ge ...
  • 應屆生:阿姨,我不想努力了在學校用React + antd做過後臺管理系統,熟悉React技術棧。兩年前端:公司技術棧是React,都用了一年了,我React賊六。五年前端:帶團隊把公司的糞坑項目用React重構了。React對我來說就跟呼吸一樣容易。:要不學學React源碼吧。......%……& ...
  • 1.HTML的學習內容 1.HTML的概念 2.互聯網的三大基石 3.HTML的頭標簽 4.HTML的主體標簽 5.HTML的圖片標簽 6.HTML的超鏈接標簽7.HTML的表格標簽 8.HTML的內嵌和框架標簽 9.HTML的表單 2.HTML的概念 HTML:超文本標記語言 作用:需要將java ...
  • 在前面隨筆《循序漸進VUE+Element 前端應用開發(12)--- 整合ABP框架的前端登錄處理》簡單的介紹了一個結合ABP後端的登陸介面實現前端系統登陸的功能,本篇隨筆繼續深化這一主題,著重介紹基於ABP後端介面信息,實現對前端界面的開發工作。 ABP(ASP.NET Boilerplate)... ...
  • 以下麵試題來自騰訊、阿裡、網易、餓了麽、美團、拼多多、百度等等大廠綜合起來常考的題目。 如何寫一個漂亮的簡歷 簡歷不是一份記流水賬的東西,而是讓用人方瞭解你的亮點的。平時有在做一些修改簡歷的收費服務,也算看過蠻多簡歷了。很多簡歷都有如下特征 喜歡說自己的特長、優點,用人方真的不關註你的性格是否陽光等 ...
  • 從webpack打包結構中我們知道,vue中有一個存放外部資源的文件夾static,它裡面的文件是不會被打包編譯的,所以我們就可以利用外部引入js的方式將我們的想要的數據在index.html中以js文件的方式引入,然後就可以全局使用。 具體的方法如下: 1.在項目中找到static文件夾,在裡面創 ...
  • 單例模式 線程安全的Singleton 會破壞Singleton的情況 線程級Singleton 單例模式是幾個創建型模式中最獨立的一個,它的主要目標不是根據客戶程式調用生成一個新的實例,而是控制某個類型的實例數量只有一個。 GOF對單例的描述為: Ensure a class only has o ...
  • 創建型模式 簡單工廠模式 工廠方法模式 IOC與工廠方法模式的結合 泛型工廠 委托工廠 創建型模式 創建型模式可以隔離客戶程式對需要實例化類型的依賴關係,這類模式一般通過將實例化具體對象的職責委托給第三方對象的方式,使得客戶程式或者外部系統在獲得所需的具體類型實例的同時,而不必對其發生直接的引用。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...