我有以下示例代码:
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
将用于我脚本中的后续逻辑。
答案 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上的这一切都会稍微低效,因为您要创建某个内容的附加副本(list
或dict
,具体取决于您引用的版本)。根据您的其他代码,这看起来不太重要。如果是,您可以使用类似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()
。