关于python:如果值不存在,在字典中设置值?

Set value in dictionary if value not there?

本问题已经有最佳答案,请猛点这里访问。

我用字典来存储一堆计数器,每个计数器都在计算文件类型(.wav,.mp3等)的出现次数。

1
filetypecounter = {}

当我遇到某个文件类型时,我希望能够以Python式的方式增加计数器。所以我想…

1
filetypecounter[filetype] +=1

但是,如果文件类型不在字典中,我想将其实例化为1。所以我的逻辑是,如果有filetype计数器,在计数器值上加1,否则设置为1。

1
2
3
4
if filetype not in filetypecounter:
    filetypecounter[filetype] = 1
else:
    filetypecounter[filetype] +=1

有没有更像Python的方法?


1
2
3
4
from collections import defaultdict

filetypecounter = defaultdict(int)
filetypecounter[filetype] += 1

1
2
3
4
from collections import Counter

filetypecounter = Counter()
filetypecounter.update([filetype])

有关信息,如果您必须使用dict,您的解决方案(检查是否存在密钥)是合理的。也许更"Python"的解决方案是:

1
2
filetypecounter = {}
filetypecounter[filetype] = filetypecounter.get(filetype, 0) + 1

实际上,这和其他建议只是同一主题的变体。我用一下柜台。


在这组答案中,很好地使用collections.Counter,但这可能不是最快的选择。

一个古老的方法是:

1
2
3
4
5
6
>>> d={}
>>> for ext in ('.mp3','.mp3','.m4a','.mp3','.wav','.m4a'):
...    d[ext]=d.setdefault(ext,0)+1
...
>>> d
{'.mp3': 3, '.wav': 1, '.m4a': 2}

这也不是最快的,但比collections.Counter快。

这些方法都有基准,无论是defaultdict、try/except还是您的原始方法都是最快的。

我在这里复制(并扩展)了基准:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import urllib2
import timeit

response = urllib2.urlopen('http://pastebin.com/raw.php?i=7p3uycAz')
hamlet = response.read().replace('

'
,'
'
)
LETTERS = [w for w in hamlet]
WORDS = hamlet.split(' ')
fmt='{:>20}: {:7.4} seconds for {} loops'
n=100
print
t = timeit.Timer(stmt="""
        counter = defaultdict(int)
        for k in LETTERS:
            counter[k] += 1
       """
,
        setup="from collections import defaultdict; from __main__ import LETTERS")

print fmt.format("defaultdict letters",t.timeit(n),n)
t = timeit.Timer(stmt="""
        counter = defaultdict(int)
        for k in WORDS:
            counter[k] += 1
       """
,
        setup="from collections import defaultdict; from __main__ import WORDS")

print fmt.format("defaultdict words",t.timeit(n),n)
print

# setdefault
t = timeit.Timer(stmt="""
        counter = {}
        for k in LETTERS:
            counter[k]=counter.setdefault(k, 0)+1
       """
,
        setup="from __main__ import LETTERS")
print fmt.format("setdefault letters",t.timeit(n),n)
t = timeit.Timer(stmt="""
        counter = {}
        for k in WORDS:
            counter[k]=counter.setdefault(k, 0)+1
       """
,
        setup="from __main__ import WORDS")
print fmt.format("setdefault words",t.timeit(n),n)
print

# Counter
t = timeit.Timer(stmt="c = Counter(LETTERS)",
        setup="from collections import Counter; from __main__ import LETTERS")

print fmt.format("Counter letters",t.timeit(n),n)
t = timeit.Timer(stmt="c = Counter(WORDS)",
        setup="from collections import Counter; from __main__ import WORDS")
print fmt.format("Counter words",t.timeit(n),n)
print

# in
t = timeit.Timer(stmt="""
        counter = {}
        for k in LETTERS:
            if k in counter: counter[k]+=1
            else: counter[k]=1  
       """
,
        setup="from __main__ import LETTERS")
print fmt.format("'in' letters",t.timeit(n),n)
t = timeit.Timer(stmt="""
        counter = {}
        for k in WORDS:
            if k in counter: counter[k]+=1
            else: counter[k]=1  
       """
,
        setup="from __main__ import WORDS")
print fmt.format("'in' words",t.timeit(n),n)
print

# try
t = timeit.Timer(stmt="""
        counter = {}
        for k in LETTERS:
            try:
                counter[k]+=1
            except KeyError:
                counter[k]=1    
       """
,
        setup="from __main__ import LETTERS")
print fmt.format("try letters",t.timeit(n),n)
t = timeit.Timer(stmt="""
        counter = {}
        for k in WORDS:
            try:
                counter[k]+=1
            except KeyError:
                counter[k]=1            """
,
        setup="from __main__ import WORDS")
print fmt.format("try words",t.timeit(n),n)
print"
{:,} letters and {:,} words"
.format(len(list(LETTERS)),len(list(WORDS)))

印刷品:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 defaultdict letters:   3.001 seconds for 100 loops
   defaultdict words:  0.8495 seconds for 100 loops

  setdefault letters:   4.839 seconds for 100 loops
    setdefault words:   0.946 seconds for 100 loops

     Counter letters:   7.335 seconds for 100 loops
       Counter words:   1.298 seconds for 100 loops

        'in' letters:   4.013 seconds for 100 loops
          'in' words:  0.7275 seconds for 100 loops

         try letters:   3.389 seconds for 100 loops
           try words:   1.571 seconds for 100 loops

175,176 letters and 26,630 words

就我个人而言,我很惊讶于tryexcept是实现这一目标最快的方法之一。谁知道…


您想要的是collections.defaultdict或collections.counter for python 2.7及更高版本。


collections.counter类完全满足您的需要。


我想你只需要计数器模块。


另一种方法是try/except子句:

1
2
3
4
try:
    filetypecounter[filetype] += 1
except KeyError:
    filetypecounter[filetype] = 1

如果文件类型比文件少,则此方法更有效,因为您不必不必要地检查filetype是否在filetypecounter中,而是假定是这样,而只在filetypecounter中创建一个新条目,而不是这样。

编辑:根据@delnan的评论增加了EDOCX1[3]。


我想你要的是defaultdict

1
2
3
4
5
from collections import defaultdict

d = defaultdict(lambda: 0)
d['foo'] += 1
# d['foo'] is now 1

另一个想法是使用dict.setdefault

1
2
3
d = {}
d.setdefault('foo', 0)  # won't override if 'foo' is already in d
d['foo'] += 1