检查给定键是否已存在于字典中并递增


294

给定字典,我如何找出该字典中的给定键是否已设置为非值?

即,我想这样做:

my_dict = {}

if (my_dict[key] != None):
  my_dict[key] = 1
else:
  my_dict[key] += 1

即,如果要已有一个,我想增加该值,否则,请将该值设置为1。


11
小代码nitpick:如果代码已存在,则代码将my_dict [key]设置为1,否则将代码递增。我认为您想要==,而不是!=。
QuantumFool

Answers:


331

您正在寻找collections.defaultdict(适用于Python 2.5+)。这个

from collections import defaultdict

my_dict = defaultdict(int)
my_dict[key] += 1

会做你想要的。

对于常规Python而言dict,如果给定键没有值,则访问dict时不会获得结果None- KeyError将会引发a。因此,如果您想使用Regular dict而不是代码,则可以使用

if key in my_dict:
    my_dict[key] += 1
else:
    my_dict[key] = 1

8
根据他的示例,设置“ defaultdict(lambda:0)”并跳过整个“ if”子句就足够了。
Deestan

这可行,但会混淆键和值(使读取变得有些奇怪)。“ some_value”应为“
some_key

@nailer:固定,谢谢。我最初使用了“ some_value”,因为这是问题中的变量名,但是我同意现在更清楚了。
dF。

20
...或对于常规dicts,您可以这样做my_dict[key] = my_dict.get(key, 0) + 1
minmaxavg

如何将其扩展到嵌套词典?dict [key1] [key2] + = 1?
Pablo Ruiz Ruiz

300

我更喜欢用一行代码来做到这一点。

my_dict = {}

my_dict [some_key] = my_dict.get(some_key,0)+ 1

字典具有一个函数get,该函数带有两个参数-所需的键和默认值(如果不存在)。我更喜欢这种方法作为defaultdict,因为您只想处理在这一行代码中不存在该键,而不是在所有地方都不存在该键的情况。


1
@AndrewWilkinson,我不好。没有像我本应的那样彻底阅读您的答案。
masaers

59

我个人喜欢使用 setdefault()

my_dict = {}

my_dict.setdefault(some_key, 0)
my_dict[some_key] += 1

setdefault太棒了。如果已经为设置了值,则不会更改该值some_key。例如,d={1:2}; d.setdefault(1, 0)不会打扰的值d[1]
wsaleem

49

您需要这样的key in dict成语。

if key in my_dict and not (my_dict[key] is None):
  # do something
else:
  # do something else

但是,您可能应该考虑使用defaultdict(按dF建议)。


1
请注意,至少在2.6中已使用has_key()来代替d中的key。我认为2.5也是如此。
大卫·洛克

请注意,可以写的my_dict[key] is not None内容更清晰(至少恕我直言)
brandizzi 2012年

@brandizzi-同意,if key in my_dict and my_dict[key]:
罗布·格兰特

18

要回答“ 我如何找出该字典中的给定索引是否已设置为非值 ”的问题,我希望这样做:

try:
  nonNone = my_dict[key] is not None
except KeyError:
  nonNone = False

这符合已被引用的EAFP概念(更容易先请求宽恕然后再允许)。它也避免了字典中重复的键查找,因为key in my_dict and my_dict[key] is not None如果查找很昂贵,那会很有趣。

对于您提出的实际问题,即增加一个int(如果存在),或者将其设置为默认值,我也建议

my_dict[key] = my_dict.get(key, default) + 1

就像安德鲁·威尔金森(Andrew Wilkinson)的回答一样。

如果要在字典中存储可修改的对象,则有第三种解决方案。一个常见的示例是multimap,您可以在其中存储键的元素列表。在这种情况下,您可以使用:

my_dict.setdefault(key, []).append(item)

如果字典中不存在key的值,则setdefault方法会将其设置为setdefault的第二个参数。它的行为就像标准的my_dict [key]一样,返回键的值(可能是新设置的值)。


(对于像我这样的局外人来说)看起来像是Pythonic的是,任何问题至少都有3个有效答案:)
davka 2011年

@davka:嗯,这三个用例几乎相同,但是有所不同:a)查找字典中是否存在非None元素b)从字典中检索值,或者如果该值不存在,则使用默认值c)从字典中检索一个值,如果该值尚不存在,则存储默认值。
nd。

我知道:)这不是批评,我只是被这个事实逗乐了
davka 2011年

在评论@ryeguy的答案时,Stuart Woodward建议“在语言中进行异常处理时的开销总是比确定该项是否存在于字典中的哈希表查找要大一个数量级”,同时您在说“还避免了字典中的重复键查找...如果查找昂贵,”-是否有人可以测量异常处理比双键查找快还是慢?
Michael Firth

1
@MichaelFirth我粗略地搜索了Python的异常开销:stackoverflow.com/questions/2522005/…它的速度较慢,但​​速度并不慢。请记住,抛出异常的高级概念在不同语言中的处理方式非常不同,因此不能一概而论。因此,尽管“异常的开销是Java的10倍”可能是正确的,但对于Python(或Swift或其他)却不是。
nd。

13

同意cgoldberg。我是怎么做的:

try:
    dict[key] += 1
except KeyError:
    dict[key] = 1

因此,要么如上所述,要么使用其他人建议的默认字典。不要使用if语句。那不是Pythonic。


8
如果语句不是Pythonic怎么办?
亚当·帕金

2
我认为这是Python的EAFP并非最佳方法的一种情况。上面的示例中有重复的代码;如果有一天,我们想+=2还是-=1?您必须记住要同时更改这两行。现在看来似乎是一件微不足道的事情,但是那是那种愚蠢的小“琐碎”错误,它们可能会再次咬住你。
Cam Jackson

3
这看起来不错并且可以正常工作,但是我通常避免这样做,因为我认为语言中的Exception处理的开销总是比确定该项是否存在于字典中的哈希表查找大一个数量级。
斯图尔特·伍德沃德

11

从许多答案中可以看出,有几种解决方案。has_key()方法尚未提及LBYL的一个实例(三步前进)。

my_dict = {}

def add (key):
    if my_dict.has_key(key):
        my_dict[key] += 1
    else:
        my_dict[key] = 1

if __name__ == '__main__':
    add("foo")
    add("bar")
    add("foo")
    print my_dict

6
has_key()比“ in”运算符慢,可读性差。
Abgan

9
...它已被弃用,在Python 2.6和在Python 3去除
蒂姆Pietzcker

7

您尝试执行此操作的方法称为LBYL(跳前先查看),因为您在尝试尝试增加值之前正在检查条件。

另一种方法称为EAFP(更容易先请求宽恕然后再允许)。在这种情况下,您只需尝试操作(增加值)。如果失败,则捕获该异常并将其值设置为1。这是使用Python的方式稍多一些(IMO)。

http://mail.python.org/pipermail/python-list/2003-May/205182.html


5

有点晚了,但这应该可行。

my_dict = {}
my_dict[key] = my_dict[key] + 1 if key in my_dict else 1

哇,作为Java程序员,这是一个非常疯狂的结构。看起来像是奇数三元运算符?
forresthopkinsa

5

这不是直接回答问题,但对我来说,您似乎可能需要collections.Counter的功能。

from collections import Counter

to_count = ["foo", "foo", "bar", "baz", "foo", "bar"]

count = Counter(to_count)

print(count)

print("acts just like the desired dictionary:")
print("bar occurs {} times".format(count["bar"]))

print("any item that does not occur in the list is set to 0:")
print("dog occurs {} times".format(count["dog"]))

print("can iterate over items from most frequent to least:")
for item, times in count.most_common():
    print("{} occurs {} times".format(item, times))

这导致输出

Counter({'foo': 3, 'bar': 2, 'baz': 1})
acts just like the desired dictionary:
bar occurs 2 times
any item that does not occur in the list is set to 0:
dog occurs 0 times
can iterate over items from most frequent to least:
foo occurs 3 times
bar occurs 2 times
baz occurs 1 times

计数器的工作方式defaultdict(int)与某些其他功能一样,因此在专门处理整数时它可以完美工作,但您不会表现出任何相关行为。
塔德格·麦当劳-詹森


0

我一直在寻找它,没有在网上找到它,然后尝试使用Try / Error运气并找到了它

my_dict = {}

if my_dict.__contains__(some_key):
  my_dict[some_key] += 1
else:
  my_dict[some_key] = 1

1
您不应该__contains__在生产代码中使用。顺便说一句 __contains__与使用相同is
user1767754'1

1
my_dict.__contains__(some_key)对等some_key in my_dict,对in操作员而言不是重载is
Tadhg McDonald-Jensen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.