[英]Why doesn't setdefault work inside a dictionary comprehension?
為什么 setdefault 不會在字典理解中a
每次出現時增加 1,但它會在循環中增加? 這里發生了什么?
替代解決方案很棒。 我最感興趣的是了解為什么這不起作用。
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}
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.