【问题标题】:`For x in locals():` -- RuntimeError: Why does locals() change size?`For x in locals():` -- RuntimeError: 为什么 locals() 会改变大小?
【发布时间】:2016-09-09 01:53:55
【问题描述】:

我有一个函数,想要获取传递给它的参数。我尝试通过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“活动”并在每次迭代时将其重新分配给新值会更有效。

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

【问题讨论】:

  • 因为x是一个新变量:因此它是局部变量字典中的一个新条目。
  • "但是,如果我用 locals() 试试这个" 不,你已经尝试了一些微妙的不同。
  • 你不应该使用locals()。为什么你的函数不能是def print_params(*i):,然后是for x in i: print(x),或者只是print(*i)?当你传入参数时,它甚至会保留参数的原始顺序。
  • 另外,你可能想要遍历locals().values(),因为你想要的是值而不是键。或者甚至可能是locals().items() 两者兼得。
  • 没想过使用.values()。这是一个很好的解决方案。使用*i也是如此;我不得不查看那个,因为我以前从未见过它,但我认为这是该语言的一个非常酷的特性。老实说,不知道它存在,但现在我知道了,我会使用它!

标签: python iteration


【解决方案1】:

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

变量x 仅创建一次,但直到您开始迭代locals() 之后才会创建它,因为for 循环的源可迭代对象在任何内容分配给循环变量之前进行评估。然而,错误消息是由 dict 本身(或者更确切地说,它的迭代器)引发的:它“注意到”什么时候试图迭代它并且它自上次迭代以来已经改变。那么会发生什么:

  1. locals() 被评估并给你一个字典
  2. for 循环在 dict 上创建一个迭代器并获取第一个元素
  3. 一个新的本地 x 被创建并设置为第一个元素
  4. 在下一次循环迭代中,for 循环尝试从 dict 迭代器中获取另一个元素
  5. dict 迭代器注意到底层 dict 自上次迭代以来有一个新元素 (x),因此它会引发错误。

一个简单的解决方法是在循环之前使用一些虚拟值创建变量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 本身。

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

【讨论】:

  • 我现在意识到,基于其他人的说法。我刚刚发现 locals() 存在,因为我试图取回函数的参数,并且有人建议(...某处)。我认为最好的解决方案是使用function(*i),考虑到我只会得到论据,这似乎要好得多。
【解决方案2】:

当您调用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')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-18
    • 2011-12-19
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 2022-07-18
    • 2013-11-12
    • 1970-01-01
    相关资源
    最近更新 更多