91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python中怎么實現字典合并操作

發布時間:2021-07-05 17:39:40 來源:億速云 閱讀:255 作者:Leah 欄目:編程語言

本篇文章給大家分享的是有關Python中怎么實現字典合并操作,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

2.1 dict.update

d1.update(d2) 確實能合并兩個字典,但它是在修改d1的基礎上進行。如果我們想要合并成一個新的字典,沒有一個直接使用表達式的方式,而需要借助臨時變量進行:

e = d1.copy()  e.update(d2)

2.2 {**d1, **d2}

字典解包可以將兩個字典合并為一個新的字典,但看起來有些丑陋,并且不能讓人顯而易見地看出這是在合并字典。

{**d1, **d2} 還會忽略映射類型,并始終返回字典類型。

2.3 collections.ChainMap

ChainMap 很少有人知道,它也可以用作合并字典。但和前面合并方式相反,在合并兩個字典時,第一個字典的鍵會覆蓋第二個字典的相同鍵。

此外,由于 ChainMap 是對入參字典的封裝,這意味著寫入 ChainMap 會修改原始字典:

>>> from collections import ChainMap  >>> d1 = {'a':1}  >>> d2 = {'a':2}  >>> merged = ChainMap(d1, d2)  >>> merged['a']     # d1['a'] 會覆蓋 d2['a']  1  >>> merged['a'] = 3 # 實際等同于 d1['a'] = 3  >>> d1  {'a': 3}

2.4 dict(d1, **d2)

這是一種鮮為人知的合并字典的“巧妙方法”,但如果字典的鍵不是字符串,它就不能有效工作了:

>>> d1 = {'a': 1}  >>> d2 = {2: 2}  >>> dict(d1, **d2)  Traceback (most recent call last):    ...  TypeError: keywords must be strings

三、原理

新操作符同 dict.update 方法的關系,就和列表連接(+)、擴展(+=)操作符同 list.extend 方法的關系一樣。需要注意的是,這和集合中 |/|= 操作符同 set.update 的關系稍有不同。作者明確了允許就地運算符接受更廣泛的類型(就像 list 那樣)是一種更有用的設計,并且限制二進制操作符的操作數類型(就像 list 那樣)將有助于避免由復雜的隱式類型轉換引起的錯誤被吞掉。

>>> l1 = [1, 2]  >>> l1 + (3,) # 限制操作數的類型,不是列表就報錯  Traceback (most recent call last)  ...  TypeError: can only concatenate list (not "tuple") to list  >>> l1 += (3,) # 允許就地運算符接受更廣泛的類型(如元組)  >>> l1  [1, 2, 3]

當合并字典發生鍵沖突時,以最右邊的值為準。這和現存的字典類似操作相符,比如:

{'a': 1, 'a': 2} # 2 覆蓋 1  {**d, **e}       # e覆蓋d中相同鍵所對應的值  d.update(e)      # e覆蓋d中相同鍵所對應的值  d[k] = v         # v 覆蓋原有值  {k: v for x in (d, e) for (k, v) in x.items()} # e覆蓋d中相同鍵所對應的值

四、規范

字典合并會返回一個新字典,該字典由左操作數與右操作數合并而成,每個操作數必須是 dict(或 dict 子類的實例)。如果兩個操作數中都出現一個鍵,則最后出現的值(即來自右側操作數的值)將會覆蓋:

>>> d = {'spam': 1, 'eggs': 2, 'cheese': 3}  >>> e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}  >>> d | e  {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}  >>> e | d # 不符合交換律,左右互換操作數會得到不同的結果  {'aardvark': 'Ethel', 'spam': 1, 'eggs': 2, 'cheese': 3}

擴展賦值版本的就地操作:

>>> d |= e # 將 e 更新到 d 中  >>> d  {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

擴展賦值的行為和字典的 update 方法完全一樣,它還支持任何實現了映射協議(更確切地說是實現了 keys 和 __getitem__ 方法)或鍵值對迭代對象。所以:

>>> d | [('spam', 999)]   # “原理”章節中提到限制操作數的類型,不是字典或字典子類就報錯  Traceback (most recent call last):    ...  TypeError: can only merge dict (not "list") to dict   >>> d |= [('spam', 999)]  # “原理”章節中提到允許就地運算符接受更廣泛的類型,其行為和 update 一樣,接受鍵值對迭代對象  >>> d  {'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel', 'spam': 999}

五、主流觀點

5.1 字典合并不符合交換律

合并是符合交換律的,但是字典聯合卻沒有(d | e != e | d)。

回應

Python 中有過不符合交換律的合并先例:

>>> {0} | {False}  {0}  >>> {False} | {0}  {False}

上述結果雖然是相等的,但是本質是不同的。通常來說,a | b 和 b | a 并不相同。

5.2 字典合并并不高效

類似管道寫法使用多次字典合并并不高效,比如 d | e | f | g | h 會創建和銷毀三個臨時映射。

回應

這種問題在序列級聯時同樣會出現。

序列級聯的每一次合并都會使序列中的元素總數增加,最終會帶來 O(N^2) 的性能開銷。而字典合并有可能會有重復鍵,因此臨時映射的大小并不會如此快速地增長。

正如我們很少將大量的列表或元組連接在一起一樣,PEP的作者任務合并大量的字典也是少見情況。若是確實有這樣的訴求,那么最好使用顯式的循環和就地合并:

new = {}  for d in many_dicts:      new |= d

5.3 字典合并是有損的

字典合并可能會丟失數據(相同鍵的值可能消失),其他形式的合并并不會。

回應

作者并不覺得這種有損是一個問題。此外,dict.update 也會發生這種情況,但并不會丟棄鍵,這其實是符合預期的。只不過是現在使用的不是 update 而是 |。

如果從不可逆的角度考慮,其他類型的合并也是有損的。假設 a | b 的結果是365,那么 a 和 b 是多少卻不得而知。

5.4 只有一種方法達到目的

字典合并不符合“Only One Way”的禪宗。

回應

其實并沒有這樣的禪宗。“Only One Way”起源于很早之前Perl社區對Python的誹謗。

5.5 超過一種方法達到目的

好吧,禪宗并沒有說“Only One Way To Do It”。但是它明確禁止“超過一種方法達到目的”。

回應

并沒有這樣的禁止。Python 之禪僅表達了對“僅一種顯而易見的方式”的偏愛。

There should be one-- and preferably only one --obvious way to do  it.

它的重點是應該有一種明顯的方式達到目的。對于字典更新操作來說,我們可能希望至少執行兩個不同的操作:

  •  就地更新字典:顯而易見的方式是使用 update() 方法。如果此提案被接受,|= 擴展賦值操作符也將等效,但這是擴展賦值如何定義的副作用。選擇哪種取決于使用者口味。

  •  合并兩個現存的字典到新字典中:此提案中顯而易見的方法是使用 | 合并操作符。

實際上,Python 里經常違反對“僅一種方式”的偏愛。例如,每個 for 循環都可以重寫為 while 循環;每個 if 塊都可以寫為 if/else 塊。列表、集合和字典推導都可以用生成器表達式代替。列表提供了不少于五種方法來實現級聯:

  •  級聯操作符:a + b

  •  就地級聯操作符:a + = b

  •  切片分配:a[len(a):] = b

  •  序列解壓縮:[*a, *b]

  •  擴展方法:a.extend(b)

我們不能太教條主義,不能因為它違反了“僅一種方式”就非常嚴格的拒絕有用的功能。

5.6 字典合并讓代碼更難理解

字典合并讓人們更難理解代碼的含義。為了解釋該異議,而不是具體引用任何人的話:“在看到 spam | eggs,如果不知道 spam 和 eggs 是什么,根本就不知道這個表達式的作用”。

回應

這確實如此,即使沒有該提案,| 操作符的現狀也是如此:

  •  對于 int/bool 是按位或

  •  對于 set/forzenset 是并集

  •  還可能是任何其他的重載操作

添加字典合并看起來并不會讓理解代碼變得更困難。確定 spam 和 eggs 是映射類型并不比確定是集合還是整數要花更多的工作。其實良好的命名約定將會有助于改善情況:

flags |= WRITEABLE  # 可能就是數字的按位或  DO_NOT_RUN = WEEKENDS | HOLIDAYS  # 可能就是集合合并  settings = DEFAULT_SETTINGS | user_settings | workspace_settings  # 可能就是字典合并

5.7 參考下完整的集合API

字典和集合很相似,應該要支持集合所支持的操作符:|、&、^ 和 -。

回應

也許后續會有PEP來專門說明這些操作符如何用于字典。簡單來說:

把集合的對稱差集(^)操作用在字典上面是顯而易見且自然。比如:

>>> d1 = {"spam": 1, "eggs": 2}  >>> d2 = {"ham": 3, "eggs": 4}

對于 d1 和 d2 對稱差集,我們期望 d1 ^ d2 應該是 {"spam": 1, "ham": 3}

把集合的差集(-)操作用在字典上面也是顯而易見和自然的。比如 d1 和 d2 的差集,我們期望:

  •  d1 - d2 為 {"spam": 1}

  •  d2 - d1 為 {"ham": 3}

把集合的交集(&)操作用在字典上面就有些問題了。雖然很容易確定兩個字典中鍵的交集,但是如何處理鍵所對應的值就比較模糊。不難看出 d1 和 d2 的共同鍵是 eggs,如果我們遵循“后者勝出”的一致性原則,那么值就是 4。

六、已拒絕的觀點

PEP 584 提案中羅列了很多已拒絕的觀點,比如使用 + 來合并字典;在合并字典時也合并值類型為列表的值等等。這些觀點都非常有意思,被拒絕的理由也同樣有說服力。限于篇幅的原因不再進一步展開,感興趣的可以閱讀 https://www.python.org/dev/peps/pep-0584/#id34。

七、實現

7.1 純 Python 實現

def __or__(self, other):      if not isinstance(other, dict):          return NotImplemented      new = dict(self)      new.update(other)      return new  def __ror__(self, other):      if not isinstance(other, dict):          return NotImplemented      new = dict(other)      new.update(self)      return new  def __ior__(self, other):      dict.update(self, other)      return self

純 Python 實現并不復雜,我們只需讓 dict 實現幾個魔法方法:

  __or__ 和 __ror__ 魔法方法對應于 | 操作符,__or__ 表示對象在操作符左側,__ror__ 表示對象在操作符右側。實現就是根據左側操作數生成一個新字典,再把右側操作數更新到新字典中,并返回新字典。

  __ior__ 魔法方法對應于 |= 操作符,將右側操作數更新到自身即可。

7.2 CPython 實現

CPython 中字典合并的詳細實現可見此 PR:https://github.com/python/cpython/pull/12088/files 。

最核心的實現如下:

// 實現字典合并生成新字典的邏輯,對應于 | 操作符  static PyObject *  dict_or(PyObject *self, PyObject *other)  {      if (!PyDict_Check(self) || !PyDict_Check(other)) {          Py_RETURN_NOTIMPLEMENTED;      }      PyObject *new = PyDict_Copy(self);      if (new == NULL) {          return NULL;      }      if (dict_update_arg(new, other)) {          Py_DECREF(new); // 減少引用計數          return NULL;      }      return new;  }  // 實現字典就地合并邏輯,對應于 |= 操作符  static PyObject *  dict_ior(PyObject *self, PyObject *other)  {      if (dict_update_arg(self, other)) {          return NULL;      }      Py_INCREF(self); // 增加引用計數      return self;  }

以上就是Python中怎么實現字典合并操作,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

五常市| 广汉市| 武义县| 奇台县| 昌吉市| 南华县| 驻马店市| 海盐县| 克山县| 苗栗县| 钟山县| 胶州市| 尉犁县| 新巴尔虎右旗| 静海县| 东兰县| 拉萨市| 湟中县| 白城市| 田阳县| 靖边县| 石阡县| 秦安县| 财经| 泾川县| 綦江县| 崇左市| 新干县| 江陵县| 丽水市| 阜康市| 营山县| 五常市| 陇西县| 穆棱市| 察雅县| 余干县| 江孜县| 沈阳市| 沅陵县| 教育|