如果字典中没有该值,如何设置值?

2 投票
7 回答
1104 浏览
提问于 2025-04-17 16:53

我正在使用一个字典来存储一些计数器,每个计数器用来统计某种文件类型的出现次数,比如 .wav、.mp3 等。

filetypecounter = {}

当我遇到某种文件类型时,我想用一种更符合 Python 风格的方法来增加计数器的值。所以我在考虑...

filetypecounter[filetype] +=1

不过,如果这个文件类型不在字典里,我想把它的计数器初始化为 1。我的逻辑是,如果这个文件类型的计数器已经存在,就把它的值加 1;如果不存在,就把它设为 1。

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

有没有更符合 Python 风格的方法呢?

7 个回答

2

看起来你想要的是 collections.defaultdict,或者是 collections.Counter,这两个都是适用于Python 2.7及以上版本的工具。

2

在这个问题的回答中,大家都提到了使用 collections.Counter,但这可能不是最快的选择。

还有一种比较老的方法是这样的:

>>> 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 或者你最初的方法是最快的。

我在这里复现并扩展了这个基准测试:

import urllib2
import timeit

response = urllib2.urlopen('http://pastebin.com/raw.php?i=7p3uycAz')
hamlet = response.read().replace('\r\n','\n')
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 "\n{:,} letters and {:,} words".format(len(list(LETTERS)),len(list(WORDS)))

输出结果是:

 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 是实现这个功能最快的方法之一。谁能想到呢……

3
from collections import defaultdict

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

或者

from collections import Counter

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

如果你必须使用一个 dict(字典),那么你检查键是否存在的做法是合理的。也许一种更“符合Python风格”的解决方案可以是:

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

其实,这个和其他建议都是同一个思路的不同变体。我会使用Counter。

撰写回答