这可能是基本的,但可以帮助我理解命名空间.一个很好的解释可能会逐步执行函数定义执行时发生的事情,然后执行函数对象后发生的事情.递归可能使事情变得复杂.
结果对我来说并不明显; 我原以为:
locals_1将包含var; locals_2将包含var和locals_1; 和locals_3将包含var,locals_1和locals_2
# A function calls locals() several times, and returns them ...
def func():
var = 'var!'
locals_1 = locals()
locals_2 = locals()
locals_3 = locals()
return locals_1, locals_2, locals_3
# func is called ...
locals_1, locals_2, locals_3 = func()
# display results ...
print 'locals_1:', locals_1
print 'locals_2:', locals_2
print 'locals_3:', locals_3
Run Code Online (Sandbox Code Playgroud)
结果如下:
locals_1: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_2: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_3: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
Run Code Online (Sandbox Code Playgroud)
模式似乎是,(n)调用本地人,所有
返回的locals-dicts都是相同的,并且它们都包含第一个(n-1)locals-dicts.
有人可以解释一下吗?
进一步来说:
为什么locals_1包含自己?
为什么locals_1包含locals_2?在创建或执行func时是否分配了locals_1 ?
为什么locals_3不包含在任何地方?
"{...}"是否表示"无休止的递归"?有点像镜子彼此面对的照片?
我们运行这段代码:
def func():
var = 'var!'
locals_1 = locals()
print(id(locals_1), id(locals()), locals())
locals_2 = locals()
print(id(locals_2), id(locals()), locals())
locals_3 = locals()
print(id(locals_3), id(locals()), locals())
return locals_1, locals_2, locals_3
func()
Run Code Online (Sandbox Code Playgroud)
这将在输出中:
44860744 44860744 {'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_3': {...}, 'locals_1': {...}, 'var': 'var!'}
Run Code Online (Sandbox Code Playgroud)
locals()这里的增长预期,但是你分配基准来locals(),而不是价值的locals()各个变量.
每个赋值locals() 改变后,但引用不会,因此每个变量都指向同一个对象.在我的输出中,所有对象id都是相同的,这就是证据.
更长的解释
这些变量与该对象具有相同的链接(引用).基本上,Python中的所有变量都是引用(与指针类似的概念).
locals_1 locals_2 locals_3
\ | /
\ | /
V V V
---------------------------------------------
| single locals() object |
---------------------------------------------
Run Code Online (Sandbox Code Playgroud)
他们完全不知道具有什么价值locals(),他们只知道何时需要它(当变量在某处使用时).更改locals()不会影响这些变量.
在你运行的最后,你将返回三个变量,这就是你打印它们时发生的事情:
print(locals_N) -> 1. Get object referenced in locals_N
2. Return the value of that object
Run Code Online (Sandbox Code Playgroud)
看到?所以,这就是为什么他们有完全相同的值时,一个locals()具有在此刻print.
如果您locals()再次(以某种方式)更改然后运行打印语句,将打印3次?是的,新的价值locals().
我最初的问题归结为“到底是什么 locals()?” 这是我目前的(推测性)理解,用 Python 语言编写:
++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++
每个本地命名空间都有自己的命名空间表,可以使用内置函数查看整个命名空间表。locals(名称空间表实际上就像一个保存“标识符”的字典:对象条目;对于每个项目,键是分配(或“绑定”)给对象的名称(字符串形式)。)
当在非全局级别调用时,locals返回解释器当前本地命名空间表的唯一表示:一个“动态”、始终最新的、专门的、类似字典的对象。
它不是一个简单的字典,也不是实际的名称表,但它实际上是“活动的”,并且在任何时候被引用时都会立即从活动表中更新(当跟踪打开时,它会随每个语句更新)。退出作用域时,该对象消失,并在下次调用
时为当前作用域重新创建。locals
(当在全局(模块化)级别调用时,locals返回globals()Python 的全局命名空间表示形式,它可能具有不同的性质)。
因此,L = locals()将名称绑定L到本地名称空间表的“替代”;随后,任何时候L被引用,该对象都会被刷新并返回。
并且,绑定(在同一范围内)的任何其他名称都将locals()是该同一对象的别名。
请注意L,被分配给locals(),必然会成为“无限递归”对象(显示为 dict {...}),这对您可能重要也可能不重要。不过,您可以随时制作简单的字典副本。
的某些属性(例如)也返回简单对象。Llocals()keys
要捕获函数内 locals() 的“原始”快照,请使用不进行任何本地分配的技术;例如,将副本作为参数传递给函数,该函数将其腌制到文件中。
有关于 的细节L,以及它的行为方式;它包含来自函数块的自由变量,但不包含类,并且文档警告不要尝试更改 的L内容(它可能不再“镜像”名称表)。
也许只应该阅读(复制等)
(为什么locals()被设计为“实时”,而不是“快照”,是另一个话题)。
总之:
locals()是一个独特的、专门的对象(字典形式);它是当前本地名称空间表的 Python实时表示(不是冻结快照)
++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++
那么,获得我预期结果的一种方法是在每一步生成副本(locals()此处使用 dict.copy):
# A function copies locals() several times, and returns each result ...
def func():
var = 'var!'
locals_1 = locals().copy()
locals_2 = locals().copy()
locals_3 = locals().copy()
return locals_1, locals_2, locals_3
Run Code Online (Sandbox Code Playgroud)
func被调用,并显示返回结果:
locals_1: {'var': 'var!'}
locals_2: {'var': 'var!', 'locals_1': {'var': 'var!'}}
locals_3: {'var': 'var!', 'locals_1': {'var': 'var!'}, 'locals_2':{'var':'var!','locals_1': {'var': 'var!'}}}
Run Code Online (Sandbox Code Playgroud)
返回的是简单的 dict 对象,它捕获本地命名空间的成长阶段。
这就是我的意图。
其他可能的复制方式locals()(此处为“L”)是dict(L)、copy.copy(L)和copy.deepcopy(L)。