如果字典尚不存在,则将条目添加到字典

Posted

技术标签:

【中文标题】如果字典尚不存在,则将条目添加到字典【英文标题】:adding entries to a dictionary if it doesn't exist already 【发布时间】:2012-09-20 22:04:28 【问题描述】:

我正在尝试编写一些代码来计算 2 的幂,但将它们存储在以 10 的幂作为键的字典中,例如,2^9 将存储为

0:2, 1:1, 2:5

你有 5*10^2 + 1*10^1 + 2*10^0。

所以目前,我有类似的东西

def foo():
    result=0:2
    for i in range(2,10):
        for p in result.keys():
            result[p]*=2
        for p in result.keys():
            if result[p] / 10 != 0:
                result.setdefault(p+1,result[p]/10)
                result[p] = result[p] % 10

问题是

result.setdefault(p+1,result[p]/10)

它会覆盖result[p+1] 中的所有内容。我知道可以只检查字典,然后在需要时“初始化”新密钥,但是有没有更优雅的方式来做呢?我可以将结果初始化为足够长,但由于我不一定知道“足够长”有多长,所以在我看来,动态扩展它更有意义。

基本上我想要一些类似的东西

for p in result.keys():
    if result[p] / 10 != 0:
        if result[p+1] exists:
            result[p+1]+=result[p]/10
        else:
            create result[p+1] and set its value to result[p]/10

非常感谢任何帮助!

【问题讨论】:

您发布的代码设置为result[p],而不是result[p+1] 快速提问:为什么使用字典0:2, 1:1, 2:5 而不是列表[2, 1, 5]?无论哪种方式,x[0]==2x[1]==1x[2]==5 @Amber:抱歉,我不太明白,我的目标是将 0:16 设置为 0:6, 1:1,所以我必须同时更改... @abarnert: 只是想学习/练习如何使用字典的基础知识:) 好的。您在这里谈论的不仅仅是基础知识,但我认为“学习如何使用字典的中间体”与学习基础知识一样是让自己更难的一个好理由。 :) 我只是想指出您正在使用 dict 作为从非稀疏初始自然数集到值的映射,而这正是 list 是开箱即用的。跨度> 【参考方案1】:

您可以使用in 语法检查密钥是否存在:

for p in result.keys()[:]:
    r_p = result[p] / 10

    if r_p != 0:
        if p + 1 in result:
            result[p + 1] += r_p
        else:
            result[p + 1] = r_p

或者,您可以使用Counter,它会自动执行此操作:

from collections import Counter

result = Counter()

for p in result.keys()[:]:
    r_p = result[p] / 10

    if r_p != 0:
        result[p + 1] += r_p

另外,你的情况有点奇怪。 result[p] / 10 == 0 仅适用于 Python 2 上的 result[p] < 10 或 Python 3 上的 result[p] == 0

【讨论】:

+1 表示Counter。但是第一个版本会简单得多:result[p + 1] = result.get(p + 1, 0) + r_p 而不是 if:/else: 循环。 @abarnert:谢谢。我应该开始更频繁地使用.get() 好吧,有一半的时间你到达get 你会意识到Counterdefaultdicttry:/d[key]…/except: 无论如何是一个更好的答案......跨度> Blender,@abarnert:我尝试了您的解决方案,但它们都抛出了“迭代期间字典更改大小”错误。有什么我想念的吗?我需要使用临时字典来使用您的解决方案吗? 对,对不起,这会发生,因为每次添加新值时,都会更改字典的键集,从而更改迭代顺序,从而使迭代无效。 (注意:这不会发生在列表中……)无论如何,如果你知道 dict 会非常小(而且它确实是,除非你处理的是非常大的数字),一个非常简单的解决方法是复制初始键列表:for p in result.keys()[:]。 (它实际上对你的算法是正确的,还是你需要进位语义?我没有仔细阅读它......)【参考方案2】:

因此,您每次通过主循环所做的是:将每个数字加倍,然后将额外的十位带入下一个数字。问题是,对于最高位,下一位不存在。

所以这是一个解决方案——尽管它不适用于除 2 之外的任何幂,原因我将在下面讨论。

def foo():
    result=0:2
    for i in range(2,10):
        for p in result.keys():
            result[p]*=2
        for p in result.keys()[:]:
            if result[p] / 10 != 0:
                result[p+1] = result.get(p+1, 0) + result[p] / 10
                result[p] = result[p] % 10

您的原始代码有两个关键更改。

首先,我们必须将[:] 添加到第二个result.keys() 的末尾,以便在循环之前遍历字典的一组键的副本,而不是其当前键。原因是如果最后一位数字大于 5,我们将在字典中添加一个新键,并且在迭代它时不允许这样做。 (为什么?有几个原因,但最简单的一个是字典是任意顺序的,每次添加一个键,整个顺序都会改变。这在以后也很重要。)

其次,您最初的问题是:如何避免检查if p+1 in result 来决定是存储r_p 还是将r_p 添加到现有值?

当你处理计数时,你使用collection.Counter,它类似于dict,只是它只存储整数,并且任何缺失的键的值都是0。否则,你通常使用collection.defaultdict,这类似于dict,只是您可以指定所有缺失键都具有的默认值。使用Counterdefaultdict,您只需要result[p+1] += result[p]/10

但我使用了不同的替代方法:dict 上的 get 方法,它允许您指定默认值。稍后我会解释原因,但请记住,一般来说,当您发现自己使用get 时,您可能需要defaultdictCounter

所以,现在它可以运行并工作,对于 2 的幂。但它不适用于其他幂,原因有两个:

首先,我们以随机顺序进行进位。对于 2 的幂,您最多可以携带 1,并且 1 不会影响下一个数字是否携带。 (8 不会携带,8+1 也不会,现在有办法得到 9。)但对于任何其他力量,这不是真的。

在简单的测试中,您可能不会注意到这一点,因为当您从一个空的 dict 开始并添加少量小键(实际上,带有小散列值,但小整数散列到自己)按排序顺序,并且不删除任何内容,dict 通常会按顺序迭代(并打印)。但总的来说,顺序是不可预测的,你不能依赖它。

该问题的解决方案是使用collections.OrderedDict,它按照添加顺序对键进行迭代。这就是为什么我不想使用defaultdictCounter:因为如果你必须切换到OrderedDict,你唯一的选择是get

其次,一旦您的指数超过 10,您可能需要进位 100。这意味着您必须进位 10,这将导致下一个数字即使是 0 也会进位。这意味着我们不能只复制提前拿到钥匙。例如,假设我们正在计算 75 的幂。从 0:5, 1:7 开始。将每个数字乘以 75 即可得到 0:375, 1:525。现在携带 37:0:5, 1:562。现在携带 56:0:5, 1:2, 2:56。因为我们只是迭代原始密钥的副本——即[0, 1]——我们没有机会携带 5。

我会留给你解决这个问题。

但是,您可能需要考虑的一件事是创建一个新字典,而不是修改旧字典。然后你就可以解决所有这些问题。 (作为一般规则,使用纯函数而不是改变状态会解决很多问题,但当然是以额外复制为代价的。)

def double(d):
    result = Counter()
    for p in d:
        result[p] += d[p] % 10
        result[p+1] += d[p] / 10
    return result

digits=0:2
for i in range(2,10):
    for p in digits:
        digits[p]*=2
    digits = double(digits)

【讨论】:

非常感谢您的详尽回答!这里有很多概念要经过...

以上是关于如果字典尚不存在,则将条目添加到字典的主要内容,如果未能解决你的问题,请参考以下文章

python 18:字典的赋值

如何将值附加到尚不存在的键?

AVL树字典

在字典中添加新列表或附加到列表(如果存在)是不是有一个很好的习语?

如何根据密钥是否已存在更新我的字典? [重复]

如何在 NSMutableDictionary 中保留部分分数