如果字典尚不存在,则将条目添加到字典
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]==2
、x[1]==1
、x[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
你会意识到Counter
、defaultdict
或try:
/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
,只是您可以指定所有缺失键都具有的默认值。使用Counter
或defaultdict
,您只需要result[p+1] += result[p]/10
。
但我使用了不同的替代方法:dict
上的 get
方法,它允许您指定默认值。稍后我会解释原因,但请记住,一般来说,当您发现自己使用get
时,您可能需要defaultdict
或Counter
。
所以,现在它可以运行并工作,对于 2 的幂。但它不适用于其他幂,原因有两个:
首先,我们以随机顺序进行进位。对于 2 的幂,您最多可以携带 1,并且 1 不会影响下一个数字是否携带。 (8 不会携带,8+1 也不会,现在有办法得到 9。)但对于任何其他力量,这不是真的。
在简单的测试中,您可能不会注意到这一点,因为当您从一个空的 dict
开始并添加少量小键(实际上,带有小散列值,但小整数散列到自己)按排序顺序,并且不删除任何内容,dict
通常会按顺序迭代(并打印)。但总的来说,顺序是不可预测的,你不能依赖它。
该问题的解决方案是使用collections.OrderedDict
,它按照添加顺序对键进行迭代。这就是为什么我不想使用defaultdict
或Counter
:因为如果你必须切换到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)
【讨论】:
非常感谢您的详尽回答!这里有很多概念要经过...以上是关于如果字典尚不存在,则将条目添加到字典的主要内容,如果未能解决你的问题,请参考以下文章