首先
我阅读了许多与创建动态命名变量类似的主题,但是它们大多与Python 2有关,或者假定您正在使用类。是的,我阅读了Python 2和Python 3中exec函数的行为。
我还知道在99%的时间内创建动态命名的变量是一个坏主意,而字典是获得的方法,但是我只想知道是否仍然可能以及exec和locals在python 3中的工作方式。
我想显示一些示例代码来说明我的问题(斐波那契计算斐波那契数字,ListOfLetters提供[“ A”,“ B”,...]):
因此,至少就我目前的理解而言,的行为存在一些不一致之处locals(),因为它包含由所添加的变量名,exec()但该变量在函数中不可用。
如果有人可以解释一下并告诉您这是设计使然还是语言中确实存在不一致之处,我将不胜感激。是的,我知道locals不应该修改它,但是我没有修改它,我在打电话给exec()...
解决方案
当您不确定某个东西为什么能在Python中正常工作时,它通常可以帮助将您困惑的行为放入函数中,然后使用该dis模块将其从Python字节码中分解出来。
让我们从一个简单的代码版本开始:
def foo(): exec("K = 89") print(K)
如果运行foo(),您将得到与更复杂的函数相同的异常:
让我们分解一下,看看为什么:
您需要注意的操作是标为“ 13”的操作。这是编译器处理K在函数(print(K))的最后一行中查找的地方。它使用的LOAD_GLOBAL操作码失败,因为“ K”不是全局变量名称,而是它在我们的locals()字典中的值(由exec调用添加)。
如果我们说服编译器将其K视为局部变量(通过在运行之前为其提供值exec),那么它将知道不寻找不存在的全局变量怎么办?
def bar(): K = None exec("K = 89") print(K)
如果运行它,此函数不会给您带来错误,但不会输出期望值:
>>> bar()None
注意在“ 3”和“ 19”处使用的操作码。Python编译器使用STORE_FAST和LOAD_FAST将局部变量的值K放入插槽0,然后将其取回。使用编号的插槽比从像这样的字典中插入和获取值的速度要快得多locals(),这就是Python编译器为函数中的所有局部变量访问而这样做的原因。您无法通过修改返回的字典来覆盖插槽中的局部变量locals()(exec如果您不将其传递给它的命名空间的字典,也一样)。
确实,让我们尝试函数的第三个版本,locals当我们将K其定义为常规局部变量时,可以再次浏览:
def baz(): K = None exec("K = 89") print(locals())
89这次您也不会在输出中看到!
>>> baz(){"K": None}
函数文档中解释了您看到旧K值的原因:locals()
更新并返回代表当前本地符号表的字典。
该语句K未更改存储本地变量值的插槽,该exec语句仅修改locals()字典。locals()再次调用时,Python使用插槽中的值“更新”字典,用替换存储在其中的值exec。
这就是为什么文档继续说:
注意:此字典的内容不应修改;更改可能不会影响解释器使用的局部变量和自由变量的值。
您的exec呼叫正在修改locals()字典,并且您发现以后的代码并不总是看到其更改。
最后多说一句,小编我是一名python开发工程师,这里有我创建的裙,聊里面大佬很多,有相关问题都可以帮之解决,并且还有相关学习资料,想要这些资料和交流问题的可以关注小编,并在后台私信小编:“01”。