collections.defaultdict如何运作?

我已经阅读了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用于什么?

703670 次浏览

defaultdict表示如果在字典中找不到键,则不会抛出KeyError,而是创建一个新条目。这个新条目的类型由default的参数给出。

例如:

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


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

通常,如果您尝试获取当前不在字典中的键的项,Python字典会抛出KeyError。相比之下,defaultdict将简单地创建您尝试访问的任何项(当然前提是它们还不存在)。要创建这样的“默认”项,它会调用您传递给构造函数的函数对象(更准确地说,它是一个任意的“可调用”对象,其中包括函数和类型对象)。对于第一个示例,默认项是使用int()创建的,它将返回整数对象0。对于第二个示例,默认项是使用list()创建的,它返回一个新的空列表对象。

留档和解释几乎是不言自明的:

作为参数传递的type函数(int/str等)用于初始化任何给定键的默认值,其中键不存在于字典中。

这里有一个关于默认的很好的解释:http://ludovf.net/blog/python-collections-defaultdict/

基本上,参数int列表是您传递的函数。请记住,Python接受函数名称作为参数。int默认返回0,列表在使用括号调用时返回空列表。

在普通字典中,如果在你的例子中我尝试调用d[a],我会得到一个错误(KeyError),因为只有键m、s、i和p存在,键a还没有初始化。但是在默认字典中,它接受一个函数名作为参数,当你尝试使用一个尚未初始化的键时,它只是调用你传入的函数并将其返回值分配为新键的值。

由于问题是关于“它是如何工作的”,一些读者可能希望看到更多的具体细节。具体来说,有问题的方法是__missing__(key)方法。参见:https://docs.python.org/2/library/collections.html#defaultdict-objects

更具体地说,这个答案展示了如何以实际的方式使用__missing__(key)https://stackoverflow.com/a/17956989/1593924

为了澄清“可调用”的含义,这里有一个交互式会话(从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})

这是默认值最典型的用法(除了无意义地使用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})
>>>

标准字典包含setdefault()方法,用于检索值并在值不存在时建立默认值。相比之下,default的允许调用者在容器初始化时预先指定默认值。

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,则特别有用。标准库留档包括以这种方式使用default的几个示例。

$ python collections_defaultdict.py


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

默认

标准字典包含setdefault()方法,用于检索值并在值不存在时建立默认值。相比之下,defaultdict允许调用者在容器初始化时预先指定默认值(要返回的值)。

Python标准库示例中的Doug Hellmann定义

如何使用默认值

进口违约

>>> from collections import defaultdict

初始化默认值

通过传递来初始化它

可调用作为其第一个参数(强制性)

>>> 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})

它是如何工作的

作为标准字典的子类,它可以执行所有相同的功能。

但是在传递未知键的情况下,它会返回默认值而不是错误。对于ex:

>>> 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[颜色]中附加值

>>> 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]})

我自己的2美分:你也可以子类默认:

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

这对于非常复杂的案件可能会派上用场。

我认为它最好用来代替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的计数。

如果没有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

默认字典工具是Python集合类中的一个容器。它类似于通常的字典容器,但它有一个区别:值字段的数据类型是在初始化时指定的。

例如:

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'])

字典是一种方便的方式来存储数据,以便以后按名称(键)检索。键必须是唯一的、不可变的对象,并且通常是字符串。字典中的值可以是任何东西。对于许多应用程序,值是简单的类型,例如整数和字符串。

当字典中的值是集合(列表、字典等)时,它会变得更有趣。在这种情况下,值(空列表或字典)必须在第一次使用给定的键时初始化。虽然这相对容易手动完成,但default的类型自动化并简化了这些类型的操作。 默认值的工作方式与普通的字典完全相同,但它使用一个函数(“默认工厂”)进行初始化,该函数不接受参数并为不存在的键提供默认值。

默认值永远不会引发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

这是另一个关于如何使用默认值的例子,我们可以降低复杂性

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))

总之,每当您需要字典并且每个元素的值都应以默认值开头时,请使用默认值。

好吧,default的也可以在以下情况下引发keyerror:

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

永远记住要给违约的论点,比如

d = defaultdict(int)

简而言之:

defaultdict(int)-参数int表示值将是int类型。

defaultdict(list)-参数列表表明值将是列表类型。

在每次调用中,可以使用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,列表构造函数被更频繁地调用(但如果确实需要,可以重写代码以避免这种情况)。

有些人可能会争辩说有性能考虑,但这个话题是一个雷区。

在我看来,defaultdit是一个给代码增加混乱而不是好处的集合。对我来说没用,但其他人可能会有不同的想法。

#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