`对于locals()中的x:` - RuntimeError:为什么locals()会改变大小?

时间:2016-09-09 01:53:55

标签: python iteration

我有一个函数,想要传递给它的参数。我尝试通过locals()实现这一目标,但我最终获得了locals()的密钥,而不是价值对:

>>>def print_params(i,j,k):
    for x in locals():
        print(x)
>>>print_params('a','b','c')
j
i
k     #<--I want to get back 'a','b','c'.

我希望从locals()获取值而不是键。

正常的词典,我知道我可以这样做:

>>> m = {
... 'a' : 'b',
... 'y' : 'z'}
>>> m
{'a': 'b', 'y': 'z'}
>>> for x in m:
...     print(x)
...
a      # <--Keys
y
>>> for x in m:
...     print(m[x])
...
b      # <--Values
z

但是,如果我使用locals()尝试此操作,则会创建一个RuntimeError。

>>> def print_args(i,j,k):
...     for x in locals():
...         print(locals()[x])
...
>>> print_args('a','b','c')
b                          # <--Got one!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in y
RuntimeError: dictionary changed size during iteration

有人能解释一下发生了什么吗?从我打印的单个值(在它出错之前),我可以看到当循环尝试迭代到locals()的下一个元素时出现错误;然后,随着dict被修改,它失去了它的位置。但是,我无法理解为什么字典大小应该改变。我推断,在每次通过for循环时,变量x被销毁,然后重新创建以便下一次循环通过循环。然而,这看起来很奇怪,因为我认为保持x&#34;活着&#34;会更有效率。并在每次迭代时将其重新分配给新值。

最终,我想我可以做local_args = locals(),但我仍然很好奇究竟发生了什么阻止我简单地循环locals()。我不确定我是否正确x被摧毁/重建;如果我是,我不得不想知道为什么会这样,如果我不是,那么我对于发生的事情更加好奇。

2 个答案:

答案 0 :(得分:1)

  

有人可以解释发生了什么吗?从我打印的单个值(在它出错之前),我可以看到当循环尝试迭代到locals()的下一个元素时出现错误;然后,随着dict被修改,它失去了它的位置。但是我无法理解为什么字典大小应该改变。我推断,在每次通过for循环时,变量x都被破坏,然后重新创建,以便下一次循环通过循环。然而,这看起来很奇怪,因为我认为保持x“活着”并且在每次迭代时将其重新分配给新值会更有效。

变量x只创建一次,但是在开始迭代locals()之后才会创建它,因为for循环的源迭代在任何事情之前都会被评估分配给循环变量。然而,错误消息是由dict本身(或者更确切地说,它的迭代器)引发的:当某些东西试图迭代它并且自上次迭代以来它已经改变时它会“注意到”。那么会发生什么:

  1. locals()被评估并为您提供词典
  2. for循环在dict上创建一个迭代器并获取第一个元素
  3. 创建新的本地x并将其设置为第一个元素
  4. 在下一个循环迭代中,for循环尝试从dict迭代器中获取另一个元素
  5. dict迭代器注意到自上次迭代以来底层dict有一个新元素(x),因此它引发了错误。
  6. 一个简单的解决方法是在循环之前创建带有一些虚拟值的变量x。然后它会在您访问locals()之前就已存在,并且在循环期间不会创建新变量:

    def print_args(i,j,k):
        x = None
        for x in locals():
            print(locals()[x])
    
    >>> print_args('a', 'b', 'c')
    a
    x
    c
    b
    

    当然,请注意,在此迭代过程中,您自己也会获得x

    正如其他人在评论中提到的那样,为什么你这样做根本就不清楚。 locals()是一个相当滑的野兽,通常,如果你想要完成一些实际任务,有一种更好的方法可以不使用locals()

答案 1 :(得分:0)

当您致电for x in locals()时,会对locals()进行评估,然后将其第一项存储在x中。之前未定义x,因此在第一个循环期间将其添加到locals字典,第二个循环通知更改。

解决此问题的一种方法是预先复制locals字典,如您所述:

def print_args(i,j,k):
    local_args = dict(locals())
    for x in local_args:
        print(local_args[x])

print_args('a','b','c')