簡體   English   中英

collections.defaultdict 是如何工作的?

[英]How does collections.defaultdict work?

我已經閱讀了 python 文檔中的示例,但仍然無法弄清楚這種方法的含義。 有人可以幫忙嗎? 以下是 python 文檔中的兩個示例

>>> from collections import defaultdict

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> for k in s:
...     d[k] += 1
...
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

參數intlist是干什么用的?

通常,如果您嘗試使用當前不在字典中的鍵獲取項目,則 Python 字典會拋出KeyError 相比之下, defaultdict只會創建您嘗試訪問的任何項目(當然前提是它們還不存在)。 要創建這樣的“默認”項,它會調用您傳遞給構造函數的函數對象(更准確地說,它是一個任意的“可調用”對象,其中包括函數和類型對象)。 對於第一個示例,默認項是使用int()創建的,它將返回整數對象0 對於第二個示例,默認項是使用list()創建的,它返回一個新的空列表對象。

defaultdict意味着如果在字典中找不到鍵,則不會拋出KeyError ,而是創建一個新條目。 這個新條目的類型由 defaultdict 的參數給出。

例如:

somedict = {}
print(somedict[3]) # KeyError

someddict = defaultdict(int)
print(someddict[3]) # print int(), thus 0

默認字典

“標准字典包括 setdefault() 方法,用於檢索值並在該值不存在時建立默認值。相比之下, defaultdict允許調用者在容器初始化時預先指定默認值(要返回的值)。”

Doug HellmannThe Python Standard Library by Example 中定義

如何使用 defaultdict

導入默認字典

>>> from collections import defaultdict

初始化 defaultdict

通過傳遞初始化它

callable作為它的第一個參數(強制)

>>> d_int = defaultdict(int)
>>> d_list = defaultdict(list)
>>> def foo():
...     return 'default value'
... 
>>> d_foo = defaultdict(foo)
>>> d_int
defaultdict(<type 'int'>, {})
>>> d_list
defaultdict(<type 'list'>, {})
>>> d_foo
defaultdict(<function foo at 0x7f34a0a69578>, {})

**kwargs作為它的第二個參數(可選)

>>> d_int = defaultdict(int, a=10, b=12, c=13)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

或者

>>> kwargs = {'a':10,'b':12,'c':13}
>>> d_int = defaultdict(int, **kwargs)
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12})

它是如何工作的

作為標准字典的子類,它可以執行所有相同的功能。

但是在傳遞未知鍵的情況下,它返回默認值而不是錯誤。 例如:

>>> d_int['a']
10
>>> d_int['d']
0
>>> d_int
defaultdict(<type 'int'>, {'a': 10, 'c': 13, 'b': 12, 'd': 0})

如果您想更改默認值,請覆蓋 default_factory:

>>> d_int.default_factory = lambda: 1
>>> d_int['e']
1
>>> d_int
defaultdict(<function <lambda> at 0x7f34a0a91578>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0})

或者

>>> def foo():
...     return 2
>>> d_int.default_factory = foo
>>> d_int['f']
2
>>> d_int
defaultdict(<function foo at 0x7f34a0a0a140>, {'a': 10, 'c': 13, 'b': 12, 'e': 1, 'd': 0, 'f': 2})

問題中的例子

示例 1

由於 int 已作為 default_factory 傳遞,因此默認情況下任何未知鍵都將返回 0。

現在當字符串在循環中傳遞時,它將增加 d 中這些字母的計數。

>>> s = 'mississippi'
>>> d = defaultdict(int)
>>> d.default_factory
<type 'int'>
>>> for k in s:
...     d[k] += 1
>>> d.items()
[('i', 4), ('p', 2), ('s', 4), ('m', 1)]
>>> d
defaultdict(<type 'int'>, {'i': 4, 'p': 2, 's': 4, 'm': 1})

示例 2

由於列表已作為 default_factory 傳遞,默認情況下,任何未知(不存在)的鍵都將返回 [ ](即列表)。

現在,當元組列表在循環中傳遞時,它會將值附加到 d[color]

>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> d.default_factory
<type 'list'>
>>> for k, v in s:
...     d[k].append(v)
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
>>> d
defaultdict(<type 'list'>, {'blue': [2, 4], 'red': [1], 'yellow': [1, 3]})

字典是一種存儲數據以供以后按名稱(鍵)檢索的便捷方式。 鍵必須是唯一的、不可變的對象,並且通常是字符串。 字典中的值可以是任何東西。 對於許多應用程序,這些值是簡單的類型,例如整數和字符串。

當字典中的值是集合(列表、字典等)時,它會變得更有趣。在這種情況下,必須在第一次使用給定鍵時初始化值(空列表或字典)。 雖然這相對容易手動完成,但 defaultdict 類型可以自動化並簡化這些類型的操作。 defaultdict 的工作方式與普通 dict 完全一樣,但它使用一個函數(“默認工廠”)進行初始化,該函數不接受任何參數並為不存在的鍵提供默認值。

defaultdict 永遠不會引發 KeyError。 任何不存在的鍵都會獲得默認工廠返回的值。

from collections import defaultdict
ice_cream = defaultdict(lambda: 'Vanilla')

ice_cream['Sarah'] = 'Chunky Monkey'
ice_cream['Abdul'] = 'Butter Pecan'

print(ice_cream['Sarah'])
>>>Chunky Monkey

print(ice_cream['Joe'])
>>>Vanilla

這是關於如何使用 defaultdict 的另一個示例,我們可以降低復雜性

from collections import defaultdict
# Time complexity O(n^2)
def delete_nth_naive(array, n):
    ans = []
    for num in array:
        if ans.count(num) < n:
            ans.append(num)
    return ans

# Time Complexity O(n), using hash tables.
def delete_nth(array,n):
    result = []
    counts = defaultdict(int)

    for i in array:
        if counts[i] < n:
            result.append(i)
            counts[i] += 1
    return result


x = [1,2,3,1,2,1,2,3]
print(delete_nth(x, n=2))
print(delete_nth_naive(x, n=2))

總之,無論何時需要字典,並且每個元素的值都應以默認值開頭,請使用 defaultdict。

這里對 defaultdicts 有很好的解釋: http : //ludovf.net/blog/python-collections-defaultdict/

基本上,參數intlist是您傳遞的函數。 請記住,Python 接受函數名稱作為參數。 int默認返回 0, list使用括號調用時返回空列表。

在普通字典中,如果在你的例子中我嘗試調用d[a] ,我會得到一個錯誤(KeyError),因為只有鍵 m、s、i 和 p 存在並且鍵 a 尚未初始化。 但是在 defaultdict 中,它將函數名稱作為參數,當您嘗試使用尚未初始化的鍵時,它只會調用您傳入的函數並將其返回值分配為新鍵的值。

在每次調用中使用dict.setdefault而不是d[key]可以輕松模仿defaultdict的行為。

換句話說,代碼:

from collections import defaultdict

d = defaultdict(list)

print(d['key'])                        # empty list []
d['key'].append(1)                     # adding constant 1 to the list
print(d['key'])                        # list containing the constant [1]

相當於:

d = dict()

print(d.setdefault('key', list()))     # empty list []
d.setdefault('key', list()).append(1)  # adding constant 1 to the list
print(d.setdefault('key', list()))     # list containing the constant [1]

唯一的區別是,使用defaultdict只調用一次列表構造函數,而使用dict.setdefault調用列表構造函數的頻率更高(但如果確實需要,可以dict.setdefault代碼以避免這種情況)。

有些人可能會爭辯說有一個性能考慮,但這個話題是一個雷區。 例如,這篇文章表明使用 defaultdict 並沒有太大的性能提升。

IMO,defaultdict 是一個集合,它增加了代碼的混亂而不是好處。 對我沒用,但其他人可能會有不同的想法。

由於問題是關於“它是如何工作的”,一些讀者可能希望看到更多的細節。 具體來說,所討論的方法是__missing__(key)方法。 請參閱: https : //docs.python.org/2/library/collections.html#defaultdict-objects

更具體地說,這個答案顯示了如何以實用的方式使用__missing__(key)https : __missing__(key)

為了闡明“可調用”的含義,這里有一個交互式會話(來自 2.7.6,但也應該在 v3 中工作):

>>> x = int
>>> x
<type 'int'>
>>> y = int(5)
>>> y
5
>>> z = x(5)
>>> z
5

>>> from collections import defaultdict
>>> dd = defaultdict(int)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd = defaultdict(x)
>>> dd
defaultdict(<type 'int'>, {})
>>> dd['a']
0
>>> dd
defaultdict(<type 'int'>, {'a': 0})

這是 defaultdict 最典型的用法(除了 x 變量的無意義使用)。 您可以使用 0 作為顯式默認值執行相同的操作,但不能使用簡單值:

>>> dd2 = defaultdict(0)

Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    dd2 = defaultdict(0)
TypeError: first argument must be callable

相反,以下有效,因為它傳入了一個簡單的函數(它即時創建了一個不帶參數且始終返回 0 的無名函數):

>>> dd2 = defaultdict(lambda: 0)
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {})
>>> dd2['a']
0
>>> dd2
defaultdict(<function <lambda> at 0x02C4C130>, {'a': 0})
>>> 

並使用不同的默認值:

>>> dd3 = defaultdict(lambda: 1)
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {})
>>> dd3['a']
1
>>> dd3
defaultdict(<function <lambda> at 0x02C4C170>, {'a': 1})
>>> 

我自己的 2¢:你也可以將 defaultdict 子類化:

class MyDict(defaultdict):
    def __missing__(self, key):
        value = [None, None]
        self[key] = value
        return value

這對於非常復雜的情況可能會派上用場。

defaultdict 工具是 Python 集合類中的一個容器。 它類似於通常的字典 (dict) 容器,但有一個區別:值字段的數據類型在初始化時指定。

例如:

from collections import defaultdict

d = defaultdict(list)

d['python'].append("awesome")

d['something-else'].append("not relevant")

d['python'].append("language")

for i in d.items():

    print i

這打印:

('python', ['awesome', 'language'])
('something-else', ['not relevant'])

好吧,defaultdict 也可以在以下情況下引發 keyerror:

    from collections import defaultdict
    d = defaultdict()
    print(d[3]) #raises keyerror

永遠記住像 defaultdict(int) 一樣為 defaultdict 提供參數。

如果沒有defaultdict ,您可能可以為看不見的鍵分配新值,但不能修改它。 例如:

import collections
d = collections.defaultdict(int)
for i in range(10):
  d[i] += i
print(d)
# Output: defaultdict(<class 'int'>, {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9})

import collections
d = {}
for i in range(10):
  d[i] += i
print(d)
# Output: Traceback (most recent call last): File "python", line 4, in <module> KeyError: 0

我認為它最好用來代替 switch case 語句。 想象一下,如果我們有一個 switch case 語句,如下所示:

option = 1

switch(option) {
    case 1: print '1st option'
    case 2: print '2nd option'
    case 3: print '3rd option'
    default: return 'No such option'
}

python 中沒有switch case 語句。 我們可以通過使用defaultdict來實現相同的效果。

from collections import defaultdict

def default_value(): return "Default Value"
dd = defaultdict(default_value)

dd[1] = '1st option'
dd[2] = '2nd option'
dd[3] = '3rd option'

print(dd[4])    
print(dd[5])    
print(dd[3])

它打印:

Default Value
Default Value
3rd option

在上面的代碼片段中, dd沒有鍵 4 或 5,因此它打印出我們在輔助函數中配置的默認值。 這比原始字典要好得多,如果鍵不存在則拋出KeyError 由此可見, defaultdict更像是一個 switch case 語句,我們可以在其中避免復雜的if-elif-elif-else塊。

這個網站給我留下了深刻印象的另一個很好的例子是:

>>> from collections import defaultdict
>>> food_list = 'spam spam spam spam spam spam eggs spam'.split()
>>> food_count = defaultdict(int) # default value of int is 0
>>> for food in food_list:
...     food_count[food] += 1 # increment element's value by 1
...
defaultdict(<type 'int'>, {'eggs': 1, 'spam': 7})
>>>

如果我們嘗試訪問除eggsspam之外的任何項目,我們將得到 0 計數。

標准字典包含方法 setdefault() 用於檢索值並在該值不存在時建立默認值。 相比之下,defaultdict 允許調用者在容器初始化時預先指定默認值。

import collections

def default_factory():
    return 'default value'

d = collections.defaultdict(default_factory, foo='bar')
print 'd:', d
print 'foo =>', d['foo']
print 'bar =>', d['bar']

只要所有鍵都具有相同的默認值,這種方法就可以很好地工作。 如果默認值是用於聚合或累加值的類型,例如列表、集合或什至 int,則它可能特別有用。 標准庫文檔包括幾個以這種方式使用 defaultdict 的示例。

$ python collections_defaultdict.py

d: defaultdict(<function default_factory at 0x100468c80>, {'foo': 'bar'})
foo => bar
bar => default value

簡而言之:

defaultdict(int) - 參數 int 表示值將是 int 類型。

defaultdict(list) - 參數列表表明值將是列表類型。

#dictinary and defaultdict

normaldictionary=dict()
print(type(normaldictionary))
#print(normaldictionary["keynotexisit"])
#Above normal dictionary give an error as key not present

from collections import defaultdict
defaultdict1=defaultdict()
print(type(defaultdict1))
#print(defaultdict1['keynotexisit'])
######################################

from collections import defaultdict
default2=defaultdict(int)
print(default2['keynotexist'])

https://msatutorpy.medium.com/different-between-dictionary-and-defaultdictionary-cb215f682971

文檔和解釋幾乎一目了然:

http://docs.python.org/library/collections.html#collections.defaultdict

作為參數傳遞的類型函數(int/str 等)用於為字典中不存在鍵的任何給定鍵初始化默認值。

暫無
暫無

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

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