字典在迭代期间改变了大小 - 代码在Py2中工作而不在Py3中

时间:2017-08-23 03:23:17

标签: python python-2.7 python-3.x dictionary

我有以下示例代码:

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将用于我脚本中的后续逻辑。

3 个答案:

答案 0 :(得分:2)

在Python 3中,dict.items已更改为视图,之前它返回了一个副本。要再次使用副本并获得交叉compat代码,您可以更改此行:

for k, value in kwargs.items():

对此:

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

答案 1 :(得分:2)

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

在python3.x中,kwargs.items()在字典的键值对中创建视图。由于它是一个视图,因此您无法在不更改视图的情况下更改字典。这就是你在python3.x中出错的原因

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

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

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

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

这会奏效。在我在交互式解释器中进行的非常不科学的实验中,第一个版本比python2.x上的第二个版本快得多。另请注意,python2.x上的这一切都会稍微低效,因为您要创建某个内容的附加副本(listdict,具体取决于您引用的版本)。根据您的其他代码,这看起来不太重要。如果是,您可以使用类似six的内容来兼容:

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

答案 2 :(得分:2)

我不会使用循环,而是设置:

user_id,category_id
4,2
4,3
4,4
...
5,3
5,8
...

在最后一行中,我没有使用# Your setup kwargs = dict(test='test', test1='test1', test2='test2', test3='test3') k_list = ['test', 'test1', 'test3'] # How to extract unwanted keys: common_keys = set(kwargs) & set(k_list) kwargs = { k: kwargs[k] for k in kwargs if k not in common_keys } kwargs.items(),因此该示例在两个版本中都运行良好。但是如果你只针对Python 3,你可能想在生产中使用.iteritems()