collections.defaultdict如何工作?


529

我已经阅读了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])]

参数int和目的list是什么?


15
顺便说一句,BTW取决于您的用例,不要忘default_factory = None在填充defaultdict之后,通过设置defaultdict将其冻结为只读。看到这个问题
Acumenus '16

Answers:


597

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


4
它与使用d.get(key,default_val)在功能上有所不同吗?
Ambareesh

29
@Ambareesh d.get(key, default)永远不会修改您的字典-它只会返回默认字典,而使字典保持不变。defaultdict另一方面,如果尚未在字典中插入密钥,则会将其插入。这是一个很大的差异。查看问题中的示例以了解原因。
斯文·马纳赫

我们如何知道每种类型的默认值是什么?int()的0和list()的[]很直观,但是也可以有更复杂或自定义的类型。
肖恩

1
@Sean defaultdict调用您传入的任何构造函数。如果传入类型T,则将使用构造值T()。并非所有类型都可以构造而不传递任何参数。如果要构造这样的类型,则需要包装函数或类似的东西functools.partial(T, arg1, arg2)
Sven Marnach

223

defaultdict表示如果在字典中找不到键,则KeyError创建新条目而不是引发键。此新条目的类型由defaultdict的参数给出。

例如:

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

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

10
“这对新对的类型由defaultdict的参数给出。” 请注意,参数可以是任何可调用的对象-不仅仅是类型函数。例如,如果foo是返回“ bar”的函数,则foo可用作默认dict的参数,并且如果访问了不存在的键,则其值将设置为“ bar”。
lf215 2013年

13
或者,如果您只想返回“ bar”:somedict = defaultdict(lambda:“ bar”)
Michael Scott Cuthbert

第四行返回0整数,如果是someddict = defaultdict(list)则返回[ ]。0是默认整数吗?还是[]默认列表?
盖德(Gathide)'17

都不行 0是不可变的-在CPython中,从-5to的所有值256都是缓存的单例,但这是实现特定的行为-在两种情况下,每次使用int()或都会“创建”一个新实例list()。这样,d[k].append(v)就可以工作而无需在字典中填充对同一列表的引用,这将使defaultdict几乎无用。如果这是行为,defaultdict则将一个值而不是一个lambda作为参数。(对不起,您的解释很糟糕!)
wizzwizz4

93

defaultdict

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

Doug HellmannPython标准库中通过示例定义

如何使用defaultdict

导入defaultdict

>>> from collections import defaultdict

初始化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})

如何运作

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

但是,如果传递未知密钥,它将返回默认值而不是错误。例如:

>>> 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传递,因此默认情况下,任何未知(不存在)键都将返回[](即list)。

现在,在循环中传递元组列表时,它将在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]})

20

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

当字典中的值是集合(列表,字典等)时,它将变得更加有趣。在这种情况下,必须在首次使用给定键时初始化值(空列表或字典)。尽管这相对容易手动完成,但是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。


18

这里对defaultdicts有很好的解释:http : //ludovf.net/blog/python-collections-defaultdict/

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

在普通词典中,如果在您的示例中尝试调用d[a],则会出现错误(KeyError),因为仅存在键m,s,i和p,而键a尚未初始化。但是在defaultdict中,它将函数名称作为参数,当您尝试使用尚未初始化的键时,它只是调用您传入的函数,并将其返回值分配为新键的值。


7

由于问题是关于“它是如何工作的”,因此一些读者可能希望看到更多的螺母和螺栓。具体地说,所讨论的方法是该__missing__(key)方法。请参阅:https : //docs.python.org/2/library/collections.html#defaultdict-objects

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

为了阐明“ callable”的含义,这是一个交互式会话(来自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})
>>> 

7

我自己的2¢:您还可以将defaultdict子类化:

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

对于非常复杂的情况,这可能会派上用场。


4

的行为defaultdict可以使用dict.setdefault代替轻松地模仿d[key]在每个调用中。

换句话说,代码:

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

有人可能会认为有性能方面的考虑,但是这个话题是一个雷区。 这篇文章显示,例如,使用defaultdict不会带来很大的性能提升。

IMO,defaultdict是一个集合,它给代码增加的混乱多于好处。对我没用,但其他人可能会有所不同。


3

defaultdict工具是Python的collections类中的容器。它与通常的字典(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'])

“值字段的数据类型在初始化时指定”:这是不正确的。提供了元素工厂功能。这list是调用该函数以填充缺失值的函数,而不是要创建的对象的类型。例如,要使用默认值1,则应使用lambda:1明显不是类型的值。
asac

2

我认为最好用它来代替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'
}

switchpython中没有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如果不存在键,则抛出a 。由此可见,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。


2

如果不使用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

2

好吧,在以下情况下,defaultdict也会引发keyerror:

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

始终记得将参数传递给defaultdict,例如defaultdict(int)。


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

0

简而言之:

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.