为什么不在字典理解中设置默认工作?

时间:2015-09-28 10:08:27

标签: python python-2.7 dictionary-comprehension setdefault

为什么setdefault在字典理解中a中的每次出现都不会递增1,但它是在循环中做的?这里发生了什么?

替代解决方案很棒。我最感兴趣的是理解为什么这不起作用。

具有setdefault的循环

a = [1,1,2,2,2,3,3]

b = {}

for x in a:
    b[x] = b.setdefault(x, 0) + 1

b

Out[4]: {1: 2, 2: 3, 3: 2}

使用setdefault进行字典理解不起作用

b = {k: b.setdefault(k, 0) + 1 for k in a}

b

Out[7]: {1: 1, 2: 1, 3: 1}

更新

感谢您的回答,我想尝试解决方案。

def using_get(a):
    b = {}
    for x in a:
        b[x] = b.get(x, 0) + 1
    return b


def using_setdefault(a):
    b = {}
    for x in a:
        b[x] = b.setdefault(x, 0) + 1
    return b


timeit.timeit(lambda: Counter(a), number=1000000)
Out[3]: 15.19974103783569

timeit.timeit(lambda: using_get(a), number=1000000)
Out[4]: 3.1597984457950474

timeit.timeit(lambda: using_setdefault(a), number=1000000)
Out[5]: 3.231248461129759

3 个答案:

答案 0 :(得分:3)

dict理解中还有还没有字典。您正在构建一个全新的词典,替换之前绑定的b

换句话说,在词典理解中,b.setdefault()是一个完全不同的词典,它与理解构建的对象无关。

实际上,只有在运行表达式之前b绑定到具有.setdefault()方法的对象时,才能使用字典理解。如果尚未定义b,或者未使用此类方法绑定到对象,则它只会失败并出现异常:

>>> a = [1,1,2,2,2,3,3]
>>> b = {k: b.setdefault(k, 0) + 1 for k in a}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
NameError: global name 'b' is not defined
>>> b = 42
>>> b = {k: b.setdefault(k, 0) + 1 for k in a}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
AttributeError: 'int' object has no attribute 'setdefault'

除非您对数字进行分组,否则您无法通过词典理解来执行您想要的操作,这需要排序并itertools.groupby();这不是一种有效的方法(需要O(NlogN)步骤而不是O(N)):

>>> from itertools import groupby
>>> {k: sum(1 for _ in group) for k, group in groupby(sorted(a))}
{1: 2, 2: 3, 3: 2}

请注意,标准库已经附带了一个计数工具;请参阅collections.Counter() object

>>> from collections import Counter
>>> Counter(a)
Counter({2: 3, 1: 2, 3: 2})

答案 1 :(得分:2)

实际上,如果您在一个干净的命名空间中尝试使用NameError,那么您的第二个代码段会引发b

bruno@bigb:~/Work/playground$ python
Python 2.7.3 (default, Jun 22 2015, 19:33:41) 
>>> a = [1,1,2,2,2,3,3]
>>> b = {k: b.setdefault(k, 0) + 1 for k in a}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <dictcomp>
NameError: global name 'b' is not defined

哪个应该给你一个错误的提示。

声明:

b = {k: b.setdefault(k, 0) + 1 for k in a}

首先评估(好吧,实际上尝试...)右侧表达式{k: b.setdefault(k, 0) + 1 for k in a}然后将结果绑定到名称{{ 1}}。

如果表达式为eval&d时未定义b,则会得到上述异常(当然)。如果它被定义并绑定到一个字典(或任何具有b方法FWIW的东西),那么就会得到setdefault(x, y)setdefault()被绑定的结果。

答案 2 :(得分:2)

这不起作用,因为在字典理解完成之前未定义b。通常,你应该得到NameError;如果没有,那么因为你之前已经定义了b,但这将是一个不同的字典。

话虽如此:您似乎只能使用collections.Counter

>>> a = [1,1,2,2,2,3,3]
>>> collections.Counter(a)
Counter({2: 3, 1: 2, 3: 2})