【问题标题】:Is it bad practice to iterate through a dict or its keys and change values?遍历 dict 或其键并更改值是不好的做法吗?
【发布时间】:2019-06-19 04:33:18
【问题描述】:

我在 python 2 中使用过:

d = {'a': 1, 'b': 2, 'c': 3}
for k in d.keys():
    d[k] = d[k] * 2
print(d)
# {'a': 2, 'b': 4, 'c': 6}

在 python 3 中我使用:

d = {'a': 1, 'b': 2, 'c': 3}
for k in d:
    d[k] = d[k] * 2
print(d)
# {'a': 2, 'b': 4, 'c': 6}

这种新语法让我感觉我正在迭代某些东西(字典)并对其进行修改,这很糟糕。但实际上我只是在遍历键,所以这应该不会带来任何麻烦,对吧?

【问题讨论】:

  • 但是.. 在第一个示例中,您还修改了该字典。
  • 由于您只是在迭代密钥,我认为这在某种程度上相当于for i in range(len(lst)): lst[i] = lst[i] * 2。所以这应该不是问题,因为键(和键的散列)保持不变。
  • @matiit 是的,除了它的编写方式没有任何改变,这让我想知道我是否违反了“不要修改你正在通过规则迭代的东西”
  • “for key in dict”语法在 Python 2 中同样有效(它已经存在多年了,真的),并且是迭代 dict 键的推荐方式...
  • @brunodesthuilliers 确实!我在使用 Python 2 时似乎不知道它:)

标签: python dictionary iteration


【解决方案1】:

遍历字典并修改值非常好。每次调用 dict.__getitem__ / dict.__setitem__ 或分别调用其语法糖 dict[] / dict[] = ... 时,都会检索键的值。您可以在迭代项目时覆盖键的值,因为更改值不会更改键哈希,因此不会影响迭代器。

很好的是在您遍历字典视图时添加或删除键。这是有问题的原因是given in the docs

dict.keys()dict.values()dict.items() 返回的对象 是视图对象。它们提供字典的动态视图 条目,这意味着当字典改变时,视图 反映了这些变化。

【讨论】:

  • 我认为引用与问题无关。 OP已经知道了。对于第一部分,您是否有明确的证据,即为现有键分配新值永远不会弄乱迭代顺序?
  • @tobias_k,我不一定会假设,否则不会问这个问题!但我添加了一些信息。
  • @tobias_k 在迭代期间更新 dict 的 不会影响迭代器(因为它会迭代键,我们在这里不涉及),所以它是安全的。问题是当您更改某些确实会影响迭代器本身的内容时。
  • @tobias_k 很难证明涉及全称量词的陈述。另一方面,没有充分的理由假设更改字典的值(不更改键)会在迭代键方面引发任何问题。
  • @All 我完全同意这很可能不是问题(我自己从未遇到过问题),但我可以想象人们可能会怀疑它是否真的安全。难道不是在调用__set__ 时,当前活动的迭代器被重置或类似的东西吗?另外,请注意,当答案字面意思是“不,那很好”并且没有其他解释时,我添加了该评论。
【解决方案2】:

如果您不更改字典的键集,我看不出它应该是不好的做法的原因。您的示例没有这样做,所以它们很好。

如果您的更改涉及添加或删除元素,事情就会开始变得混乱:

d = {}
k = d.keys()
i = iter(k)
d[42] = 23
next(i)

这将引发异常:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

通过迭代项目而不是单独的键,您的示例可能会有所改进:

d = dict(a=1, b=2, c=3)
for k, v in d.items():
    d[k] = v * 2

或者在你的“只是加倍”的特殊情况下:

for k in d.keys():
    d[k] *= 2

但我猜你的实际用例可能更复杂。

编辑:请注意,如果您仍在使用 Python2,则应使用 .iteritems().iterkeys() 而不是 .items().keys()

【讨论】:

  • @brunodesthuilliers 我更喜欢明确的d.keys()。 (我有几秒钟的错误,也许你指的是那个;如果你删除你的评论,我会删除我的,以减少喋喋不休。)
  • 我已经看到了 sn-p using .items() 的建议,但这是否意味着我确实会修改我正在迭代的内容?文档中 Jpp 的引用似乎暗示 .items() 返回的视图将因修改 dict 而被修改,对吧?
  • @Alfe dict.keys() - 至少在 python2 中 - 创建一个字典键的列表,所以它没有充分的理由有更高的成本,而且字典本身就是它们键上的迭代器这一事实是有据可查,所以 for k in dict 是明确的。
  • @brunodesthuilliers 你是对的,在 Python2 中。切换到 Python3 并不难,自从我上次偶然发现一个尚未移植到 Python3 的库以来已经有好几年了,新版本的优势是巨大的。所以我只是建议切换到当前的 Python 风格(为了保持答案简洁,我不再提及所有旧版本的 Python)。但是,如果有人需要坚持使用 Python2,他们应该使用 .iteritems().iterkeys() 而不是 .items().keys()。使用for i in d:显式的(根据“显式”的定义)。
  • 不是每个人都可以如此轻松地切换到 Python3 - 我们很多人都必须维护遗留代码。我坚持认为for key in dict:for item in list 一样明确,因为dicts 被明确记录为它们自己的键上的迭代器。 “显式”并不意味着“冗长”(当然需要您了解您的语言功能)。或者你会写if (len(somelist) == 0) == True而不是if not somelist?-)
猜你喜欢
  • 2021-12-15
  • 1970-01-01
  • 2022-01-12
  • 2019-10-17
  • 1970-01-01
  • 1970-01-01
  • 2013-04-05
  • 1970-01-01
  • 2021-12-13
相关资源
最近更新 更多