本文介绍了字典在迭代过程中更改了大小-代码在Py2中有效,而在Py3中无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下示例代码:

k_list = ['test', 'test1', 'test3']

def test(*args, **kwargs):
    for k, value in kwargs.items():
        if k in k_list:
            print("Popping k = ", k)
            kwargs.pop(k, None)
    print("Remaining KWARGS:", kwargs.items())

test(test='test', test1='test1', test2='test2', test3='test3')

在Python 2.7.13中,它完全打印出我期望的样子,并且在kwargs中仍然有一个项目:

In Python 2.7.13 this prints exactly what I expect and still has an item left in the kwargs:

('Popping k = ', 'test')
('Popping k = ', 'test1')
('Popping k = ', 'test3')
('Remaining KWARGS:', [('test2', 'test2')])

但是,在Python 3.6.1中,此操作失败:

In Python 3.6.1, however, this fails:

Popping k =  test
Traceback (most recent call last):
  File "test1.py", line 11, in <module>
    test(test='test', test1='test1', test2='test2', test3='test3')
  File "test1.py", line 5, in test
    for k, value in kwargs.items():
RuntimeError: dictionary changed size during iteration

我需要进行哪些调整以保持Python 2的兼容性,但在Python 3.6中可以正常工作?其余的kwargs将用于我的脚本中的后续逻辑.

What do I need to adjust to maintain the Python 2 compatibility but work correctly in Python 3.6? The remaining kwargs will be used for later logic in my script.

推荐答案

它在python2.x中工作的原因是kwargs.items()创建了一个列表-您可以将其视为字典键值的快照对.由于它是快照,因此您可以更改字典而无需修改要遍历的快照,一切正常.

The reason that it works in python2.x is because kwargs.items() creates a list -- You can think of it as a snapshot of the dictionary's key-value pairs. Since it is a snapshot, you can change the dictionary without modifying the snapshot that you're iterating over and everything is OK.

在python3.x中,kwargs.items()在字典的键/值对中创建一个 view .由于它是视图,因此您不能再更改视图而不更改视图.这就是为什么您在python3.x中得到错误的原因

In python3.x, kwargs.items() creates a view into the dictionary's key-value pairs. Since it is a view, you can no longer change the dictionary without also changing the view. This is why you get an error in python3.x

同时适用于python2.x和python3.x的一种解决方案是始终使用内置的list创建快照:

One resolution which will work on both python2.x and python3.x is to always create a snapshot using the list builtin:

for k, value in list(kwargs.items()):
    ...

或者,通过复制字典来创建快照:

Or, alternatively, create a snapshot by copying the dict:

for k, value in kwargs.copy().items():
    ...

这将起作用.在我在交互式解释器中所做的非常科学的尝试中,第一个版本比python2.x上的第二个版本快很多.还要注意,在python2.x上,这整个过程的效率会稍有降低,因为您将创建某些内容的附加副本(listdict,具体取决于您引用的版本).根据您的其他代码,这似乎不是一个太多的问题.如果是这样,可以使用类似 six 之类的兼容性:

This will work. In a very unscientific experiement that I did in my interactive interpreter, the first version is a fair amount faster than the second on python2.x. Also note that this whole thing will be slightly inefficient on python2.x because you'll be creating an addition copy of something (either a list or dict depending on which version you reference). Based on your other code, that doesn't look like too much of a concern. If it is, you can use something like six for compatibility:

for k, value in list(six.iteritems(kwargs)):
    ...

这篇关于字典在迭代过程中更改了大小-代码在Py2中有效,而在Py3中无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-18 19:09