簡體   English   中英

為什么 setdefault 在字典理解中不起作用?

[英]Why doesn't setdefault work inside a dictionary comprehension?

為什么 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

字典理解中還沒有字典 您正在構建一個全新的字典,替換之前綁定的任何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()對象

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

實際上,如果您在干凈的名稱空間(沒有事先定義b的名稱空間)中嘗試,您的第二個片段會引發NameError

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}然后將結果綁定到名稱b

如果在表達式被 eval 時b沒有定義,你會得到上面的異常(當然)。 如果它已定義並綁定到 dict(或任何具有setdefault(x, y)方法的 FWIW),則您將在此時綁定b的任何內容上調用setdefault()的結果。

這不起作用,因為在字典理解完成之前沒有定義b 通常,你應該得到一個NameError 如果不是,那么因為您之前已經定義了b ,但這將是一個不同的字典。

話雖如此:看來您可以為此使用collections.Counter

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

暫無
暫無

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

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