我有以下示例代码:

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中仍然有一个项目:
('Popping k = ', 'test')
('Popping k = ', 'test1')
('Popping k = ', 'test3')
('Remaining KWARGS:', [('test2', 'test2')])

但是,在Python 3.6.1中,此操作失败:
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将用于我的脚本中的后续逻辑。

最佳答案

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

在python3.x中,kwargs.items()创建一个对字典的键-值对的 View 。由于它是 View ,因此您无法再更改 View 而又不能更改字典。这就是为什么您在python3.x中收到错误的原因

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

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

或者,通过复制字典来创建快照:
for k, value in kwargs.copy().items():
    ...

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

07-24 09:15