dict和collections.defaultdict的区别是什么?

66

我在查看Peter Norvig的代码,学习如何编写简单的拼写检查器。一开始,他使用这段代码将单词插入字典中。

def train(features):
    model = collections.defaultdict(lambda: 1)
    for f in features:
        model[f] += 1
    return model

Python中的字典(dict)和这里使用的那个有什么区别?此外,lambda是什么意思?我查看了API文档,它说defaultdict实际上是继承自dict的,但人们如何决定使用哪一个呢?


1
如果您尝试使用“model = {}”(即普通字典)来运行上述代码,会发生什么? - Greg Hewgill
2
defaultdict 允许您指定一个函数,如果字典中不存在某个键,则生成默认值。 - Jeff Mercado
1
@Greg Hwegill:是的。它会生成一个键错误,但我可以通过使用setdefault来解决它,对吗?如果我错了,请纠正我。另外,你能告诉我lambda被用来做什么吗? - Legend
1
dictcollections.defaultdict在文档中都有非常完整的定义。您对实际文档中使用的具体单词有什么问题?对我们来说似乎很清楚。您能提供一些关于您不清楚的内容的提示吗? - S.Lott
4个回答

67
区别在于 defaultdict 如果键尚未设置,则会“默认”一个值。如果您没有使用 defaultdict,则必须检查该键是否存在,如果不存在,则将其设置为所需的值。 lambda 函数定义了默认值的工厂函数。每次需要默认值时都会调用该函数。您可以假设有一个更复杂的默认函数。
Help on class defaultdict in module collections:

class defaultdict(__builtin__.dict)
 |  defaultdict(default_factory) --> dict with default factory
 |  
 |  The default factory is called without arguments to produce
 |  a new value when a key is not present, in __getitem__ only.
 |  A defaultdict compares equal to a dict with the same items.
 |  

(来自help(type(collections.defaultdict())))

{}.setdefaultcollections.defaultdict 相似,但是它接收的是一个值而不是工厂函数。它用于在键不存在时设置对应的值...这有点不同,不过。


25

感谢:- https://shirishweb.wordpress.com/2017/05/06/python-defaultdict-versus-dict-get/

使用普通字典(dict)

d={}
d['Apple']=50
d['Orange']=20
print(d['Apple'])
print(d['Grapes'])# This gives Key Error

我们可以在普通字典中使用默认值来避免这个KeyError,让我们看看如何做到这一点。

d={}
d['Apple']=50
d['Orange']=20
print(d['Apple'])
print(d.get('Apple'))
print(d.get('Grapes',0)) # DEFAULTING

使用默认字典

from collections import defaultdict
d = defaultdict(int) ## inside parenthesis we say what should be the default value.
d['Apple']=50
d['Orange']=20
print(d['Apple'])
print(d['Grapes']) ##→ This gives Will not give error

使用用户自定义函数将值默认为

from collections import defaultdict
def mydefault():
        return 0

d = defaultdict(mydefault)
d['Apple']=50
d['Orange']=20
print(d['Apple'])
print(d['Grapes'])

摘要

  1. 默认情况下,普通字典的默认值是基于不同情况而定的。而在defaultdict中,我们可以以一般方式提供默认值。

  2. 使用defaultdict进行默认值设置的效率比使用普通字典高出两倍。您可以参考下面的链接了解更多关于这个性能测试的信息 https://shirishweb.wordpress.com/2017/05/06/python-defaultdict-versus-dict-get/


12

让我们深入了解Python字典和Python defaultdict()

Python字典

字典是Python中可用的数据结构之一,它允许以键值对的形式存储数据。

例如:

d = {'a': 2, 'b': 5, 'c': 6}

字典问题

字典通常很好用,除非遇到缺少键的情况。假设你正在寻找一个键-值对,但在字典中没有该值,那么可能会遇到 KeyError 的问题。就像这样:

d = {'a': 2, 'b': 5, 'c': 6}
d['z']  # z is not present in dict so it will throw a error

您将看到类似于以下内容:
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
    d['z'] 
KeyError: 'z'

解决上述问题的方法

我们可以采用不同的方法来解决上述问题:

使用内置函数

setdefault

如果字典中存在 key,则返回其值。否则,插入一个具有值为default的键并返回default。默认情况下,defaultNone:

>>> d = {'a' :2, 'b': 5, 'c': 6}
>>> d.setdefault('z', 0)
0  # returns 0 
>>> print(d)  # add z to the dictionary
{'a': 2, 'b': 5, 'c': 6, 'z': 0}

get

如果字典中存在键 key,则返回该键的值;否则返回default的值。如果没有指定默认值default,则默认为None,因此该方法永远不会引发KeyError异常:

>>> d = {'a': 2, 'b': 5, 'c': 6}
>>> d.get('z', 0)
0  # returns 0 
>>> print(d)  # Doesn't add z to the dictionary unlike setdefault
{'a': 2, 'b': 5, 'c': 6}

以上两种方法是我们问题的解决方案。它们不会引发"KeyError"异常。除了以上两种方法,Python还有一个名为collections的模块可以处理这个问题。让我们深入探讨一下位于collections模块中的defaultdict: defaultdict位于Python的collections模块中。您可以使用以下方式使用它:
from collections import defaultdict

d = defaultdict(int)

defaultdict 构造函数接受一个名为 default_factory 的可调用对象作为参数。这个可调用对象可以是以下之一:

  • int: 默认值将会是整数 0

  • str: 默认值将会是空字符串 ""

  • list: 默认值将会是空列表 []

代码:

from collections import defaultdict

d = defaultdict(list)
d['a']  # access a missing key and returns an empty list
d['b'] = 1 # add a key-value pair to dict
print(d)

输出结果将是defaultdict(<class 'list'>, {'b': 1, 'a': []})

defaultdictget()setdefault() 方法的作用相同,那么什么时候使用它们?

什么时候使用 get()

如果你需要返回特定的键值对而不出现 KeyError,并且还不能在字典中更新它 - 那么选用 dict.get 是正确的选择。它会返回你指定的默认值,但不会修改字典。

什么时候使用 setdefault()

如果你需要使用默认的键值对修改原始字典 - 那么选用 setdefault 是正确的选择。

什么时候使用 defaultdict

使用 defaultdict 可以实现 setdefault 方法,但我们可以在 defaultdict 中一次性提供默认值,而不是每次都提供。此外,setdefault 可以为键提供不同的默认值选择。两者各有优劣,具体取决于使用情况。

就效率而言:

defaultdict > setdefault()get()

defaultdictget() 快两倍!

你可以在这里查看结果。


2
非常好的详细回答!一个问题在于你给出的defaultdict示例中。你创建了一个defaultdict(list),但是接着却执行了d['b'] = 1。这失去了使用defaultdict的意义。更典型的用法是当我们想要将某些内容附加到列表中而不检查键是否已经存在时。因此,我会将该示例更改为d['b'].append(1)并展示它变成了一个列表[1] - Tomerikoo
@Tomerikoo 这是一个非常好的例子,很适合上面的场景,感谢您的编辑。 - Manu Manoj

11

如果你有一些有意义的默认值来处理缺失的键并且不想显式地处理它们,则可以使用defaultdict。

defaultdict构造函数接受一个函数作为参数,并使用该函数构造一个值。

lambda: 1
与不带参数的函数f执行相同的函数。
def f():
 return 1

我忘了为什么API是这样设计的,而不是将一个值作为参数传递。如果我设计defaultdict接口,它会稍微复杂一些,缺失值创建函数将采用缺失的键作为参数。


1
如果将值作为参数传递,您必须小心可变的值。例如,defaultdict([])会设置相同(可变)列表作为每个缺失元素的值,而defaultdict(list)则总是创建一个新列表。 - Ismail Badawi
2
我认为defaultdict的工厂函数不需要参数的原因是它可以与那些__init__()构造函数不需要任何参数的类型一起使用,例如intlistdict。当然,你可以轻松地从defaultdict派生一个子类,其__missing__()方法将键传递给工厂函数。请参见有没有聪明的方法将键传递给defaultdict的default_factory?的答案。 - martineau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接