簡體   English   中英

為什么 dict.get(key) 而不是 dict[key]?

[英]Why dict.get(key) instead of dict[key]?

今天,我遇到了dict方法get ,它給定字典中的鍵,返回關聯的值。

這個功能有什么用? 如果我想在字典中查找與鍵關聯的值,我可以執行dict[key] ,它返回相同的內容:

dictionary = {"Name": "Harry", "Age": 17}
dictionary["Name"]
dictionary.get("Name")

如果缺少鍵,它允許您提供默認值:

dictionary.get("bogus", default_value)

返回default_value (無論您選擇什么),而

dictionary["bogus"]

會引發KeyError

如果省略,則default_valueNone ,這樣

dictionary.get("bogus")  # <-- No default specified -- defaults to None

返回None就像

dictionary.get("bogus", None)

將。

什么是dict.get()方法?

如前所述, get方法包含一個指示缺失值的附加參數。 從文檔

get(key[, default])

如果鍵在字典中,則返回鍵的值,否則返回默認值。 如果未給出默認值,則默認為無,因此此方法永遠不會引發KeyError

一個例子可以是

>>> d = {1:2,2:3}
>>> d[1]
2
>>> d.get(1)
2
>>> d.get(3)
>>> repr(d.get(3))
'None'
>>> d.get(3,1)
1

任何地方都有速度改進嗎?

正如這里提到的,

似乎所有三種方法現在都表現出相似的性能(彼此相差約 10%),或多或少獨立於單詞列表的屬性。

早期的get速度要慢得多,但是現在速度幾乎可以與返回默認值的額外優勢相媲美。 但是為了清除我們所有的查詢,我們可以在一個相當大的列表上進行測試(注意,測試只包括查找所有有效的鍵)

def getway(d):
    for i in range(100):
        s = d.get(i)

def lookup(d):
    for i in range(100):
        s = d[i]

現在使用timeit對這兩個函數進行計時

>>> import timeit
>>> print(timeit.timeit("getway({i:i for i in range(100)})","from __main__ import getway"))
20.2124660015
>>> print(timeit.timeit("lookup({i:i for i in range(100)})","from __main__ import lookup"))
16.16223979

正如我們所見,查找比獲取更快,因為沒有函數查找。 這可以通過dis看到

>>> def lookup(d,val):
...     return d[val]
... 
>>> def getway(d,val):
...     return d.get(val)
... 
>>> dis.dis(getway)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_ATTR                0 (get)
              6 LOAD_FAST                1 (val)
              9 CALL_FUNCTION            1
             12 RETURN_VALUE        
>>> dis.dis(lookup)
  2           0 LOAD_FAST                0 (d)
              3 LOAD_FAST                1 (val)
              6 BINARY_SUBSCR       
              7 RETURN_VALUE  

它將在哪里有用?

每當您要在查找字典時提供默認值時,它都會很有用。 這減少了

 if key in dic:
      val = dic[key]
 else:
      val = def_val

對於單行, val = dic.get(key,def_val)

它在哪里沒有用處?

每當您想返回KeyError說明特定鍵不可用時。 返回默認值也會帶來特定默認值也可能是鍵的風險!

是否有可能在dict['key']get類似的功能?

是的! 我們需要在 dict 子類中實現__missing__

一個示例程序可以是

class MyDict(dict):
    def __missing__(self, key):
        return None

一個小示范可以

>>> my_d = MyDict({1:2,2:3})
>>> my_d[1]
2
>>> my_d[3]
>>> repr(my_d[3])
'None'

get采用第二個可選值。 如果您的字典中不存在指定的鍵,則將返回此值。

dictionary = {"Name": "Harry", "Age": 17}
dictionary.get('Year', 'No available data')
>> 'No available data'

如果不給第二個參數,則返回None

如果您在dictionary['Year']中使用索引,則不存在的鍵將引發KeyError

["

{"bids":{"id":16210506,"submitdate":"2011-10-16 15:53:25","submitdate_f":"10\/16\/2011 at 21:53 CEST","submitdate_f2":"p\u0159ed 2 lety","submitdate_ts":1318794805,"users_id":"2674360","project_id":"1250499"}}

使用.get()時要注意的問題:

如果字典包含調用.get()時使用的鍵並且其值為None ,則.get()方法將返回None即使提供了默認值。

例如,以下返回None ,而不是預期'alt_value'

d = {'key': None}
assert None is d.get('key', 'alt_value')

.get()的第二個值僅在提供的鍵不在字典中時返回,而不是在該調用的返回值為None時返回。

目的是如果沒有找到key可以給一個默認值,非常有用

dictionary.get("Name",'harry')

這個功能有什么用?

一種特殊的用法是用字典計數。 假設您要計算給定列表中每個元素的出現次數。 這樣做的常用方法是制作一個字典,其中鍵是元素,值是出現次數。

fruits = ['apple', 'banana', 'peach', 'apple', 'pear']
d = {}
for fruit in fruits:
    if fruit not in d:
        d[fruit] = 0
    d[fruit] += 1

使用.get()方法,您可以使這段代碼更加緊湊和清晰:

for fruit in fruits:
    d[fruit] = d.get(fruit, 0) + 1

其他答案已經清楚地解釋了 dict 括號鍵控和.get之間的區別,並提到了一個相當無害的陷阱,當None或默認值也是有效鍵時。

鑒於此信息,可能很容易得出結論,即.get在某種程度上比括號索引更安全和更好,並且應該始終使用而不是括號查找,如Stop Using Square Bracket Notation to Get a Dictionary's Value in Python中所述,即使在常見的情況下也是如此他們期望查找成功的情況(即永遠不會引發KeyError )。

博客文章的作者認為.get “保護您的代碼”:

請注意嘗試引用不存在的術語如何導致KeyError 這可能會讓人頭疼,尤其是在處理不可預測的業務數據時。

雖然我們可以將我們的語句包裝在try / exceptif語句中,但對字典術語的這種關注很快就會堆積起來。

確實,在不常見的情況下null ( None )-合並或以其他方式填充缺失值以處理不可預測的動態數據,明智部署的.get是一個有用且 Pythonic 的速記工具,用於笨拙if key in dct:try / except作為程序行為規范的一部分可能缺少鍵時僅用於設置默認值的塊。

但是,用.get替換所有括號 dict 查找,包括您斷言必須成功的那些查找是另一回事。 這種做法有效地將一類運行時錯誤降級,這些錯誤有助於將錯誤揭示為更難以識別和調試的靜默非法狀態場景。

程序員之間的一個常見錯誤是認為異常會引起頭痛並試圖抑制它們,使用諸如將代碼包裝在try ... except: pass blocks中的技術。 他們后來意識到,真正令人頭疼的事情是永遠不會在故障點看到應用程序邏輯被破壞並部署損壞的應用程序。 更好的編程實踐是包含所有程序不變量的斷言,例如必須在字典中的鍵。

錯誤安全的層次結構大致是:

錯誤類別 調試相對容易
編譯時錯誤 簡單的; 去生產線解決問題
運行時異常 中等的; 控制需要流向錯誤,這可能是由於意外的邊緣情況或難以重現的狀態(如線程之間的競爭條件),但至少當它發生時我們會得到明確的錯誤消息和堆棧跟蹤。
沉默的邏輯錯誤 難的; 我們甚至可能不知道它的存在,當我們這樣做時,由於缺乏局部性和潛在的多個斷言違規,追蹤導致它的狀態可能非常具有挑戰性。

當編程語言設計者談論程序安全時,一個主要目標是通過將運行時錯誤提升為編譯時錯誤並將靜默邏輯錯誤提升為運行時異常或(理想情況下)編譯時錯誤來顯示而不是抑制真正的錯誤。

Python 在設計上是一種解釋性語言,它嚴重依賴運行時異常而不是編譯器錯誤。 默認情況下,缺少方法或屬性、非法類型操作(如1 + "a"和超出范圍或缺少索引或鍵)。

某些語言,如 JS、Java、Rust 和 Go,默認情況下使用其映射的回退行為(在許多情況下,不提供 throw/raise 替代方案),但 Python 和其他語言(如 C#)默認情況下會拋出。 Perl/PHP 發出未初始化值警告。

.get不加選擇地應用於所有 dict 訪問,即使是那些預計不會失敗並且沒有后備處理None (或使用任何默認值)在代碼中亂跑的人,幾乎拋棄了 Python 的運行時異常安全網這類錯誤,沉默或增加潛在錯誤的間接性。

更喜歡括號查找的其他支持原因(偶爾,放置良好的.get預期默認值):

  • 更喜歡使用該語言提供的工具編寫標准的慣用代碼。 由於上面給出的異常安全原因,Python 程序員通常(正確地)更喜歡括號,因為它是 Python dicts 的默認行為。
  • 當您希望提供與您斷言必須成功的查找無法區分的默認None值時,始終使用.get放棄意圖。
  • 測試的復雜性與.get允許的新“合法”程序路徑成正比。 實際上,每個查找現在都是一個可以成功或失敗的分支——這兩種情況都必須經過測試以建立覆蓋范圍,即使默認路徑實際上無法通過規范到達(具有諷刺意味的是, if val is not None:try所有未來檢索到的值的用途;對於本來不應該是None的東西來說是不必要的和令人困惑的)。
  • .get有點慢
  • .get更難輸入,也更難讀(將 Java 附加的ArrayList語法與原生 C# Lists或 C++ 矢量代碼進行比較)。 次要的。

一些語言,如 C++ 和 Ruby 提供替代方法(分別為atfetch )來選擇在錯誤訪問時拋出錯誤,而 C# 提供類似於 Python 的get的選擇后備值TryGetValue

由於 JS、Java、Ruby、Go 和 Rust 默認將.get的后備方法烘焙到所有哈希查找中,所以它不會那么糟糕,人們可能會想。 誠然,這不是語言設計者面臨的最大問題,並且無拋出訪問版本有很多用例,因此跨語言沒有達成共識也就不足為奇了。

但正如我所論證的,Python(連同 C#)通過將 assert 選項設為默認值,比這些語言做得更好。 通過不加選擇地全面使用.get來選擇不使用它在故障點報告合同違規行為是一種安全性和表達能力的損失。

為什么 dict.get(key) 而不是 dict[key]?

0. 總結

dict[key]相比, dict.get在查找鍵時提供了一個備用值。

一、定義

get(key[, default]) 4. 內置類型 — Python 3.6.4rc1 文檔

如果鍵在字典中,則返回鍵的值,否則返回默認值。 如果未給出默認值,則默認為無,因此此方法永遠不會引發 KeyError。

d = {"Name": "Harry", "Age": 17}
In [4]: d['gender']
KeyError: 'gender'
In [5]: d.get('gender', 'Not specified, please add it')
Out[5]: 'Not specified, please add it'

2.它解決的問題。

如果沒有default value ,則必須編寫繁瑣的代碼來處理此類異常。

def get_harry_info(key):
    try:
        return "{}".format(d[key])
    except KeyError:
        return 'Not specified, please add it'
In [9]: get_harry_info('Name')
Out[9]: 'Harry'
In [10]: get_harry_info('Gender')
Out[10]: 'Not specified, please add it'

作為一種方便的解決方案, dict.get引入了一個可選的默認值,避免了上面那些笨拙的代碼。

三、結論

如果字典中沒有鍵, dict.get有一個額外的默認值選項來處理異常

一個可能是優勢的區別是,如果我們正在尋找一個不存在的鍵,我們將得到 None,不像我們使用方括號表示法時那樣,在這種情況下我們會拋出一個錯誤:

print(dictionary.get("address")) # None
print(dictionary["address"]) # throws KeyError: 'address'

get 方法的最后一件很酷的事情是,它接收一個額外的默認值的可選參數,也就是說,如果我們試圖獲取學生的分數值,但學生沒有我們可以獲得的分數鍵一個 0 代替。

因此,不要這樣做(或類似的事情):

score = None
try:
    score = dictionary["score"]
except KeyError:
    score = 0

我們做得到:

score = dictionary.get("score", 0)
# score = 0

我沒有看到的另一個用例是作為sortedmaxmin等函數的key參數。 get方法允許根據鍵的值返回鍵。

>>> ages = {"Harry": 17, "Lucy": 16, "Charlie": 18}
>>> print(sorted(ages, key=ages.get))
['Lucy', 'Harry', 'Charlie']
>>> print(max(ages, key=ages.get))
Charlie
>>> print(min(ages, key=ages.get))
Lucy

感謝提供此用例的另一個問題的答案

它允許您提供默認值,而不是在找不到該值時出錯。 像這樣說服代碼:

class dictionary():
    def get(self,key,default):
         if self[key] is not found : 
               return default
         else:
               return self[key]

簡短的回答

方括號用於條件查找,當缺少鍵時,可能會因KeyError而失敗。

get()方法用於無條件查找,因為提供了默認值,因此永遠不會失敗。

基本方法和輔助方法

方括號調用__getitem__方法,該方法是 dicts 等映射的基礎。

get()方法是分層在該功能之上的幫助程序。 它是通用編碼模式的捷徑:

try:
    v = d[k]
except KeyError:
    v = default_value  

根據用法應該使用這個get方法。

示例 1

In [14]: user_dict = {'type': False}

In [15]: user_dict.get('type', '')

Out[15]: False

In [16]: user_dict.get('type') or ''

Out[16]: ''

示例 2

In [17]: user_dict = {'type': "lead"}

In [18]: user_dict.get('type') or ''

Out[18]: 'lead'

In [19]: user_dict.get('type', '')

Out[19]: 'lead'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM