这可能是基本的,但可以帮助我理解命名空间。 一个很好的解释可能会逐步完成 执行函数定义,然后执行后续操作 当执行函数 object 时。 递归可能使事情变得复杂。
结果对我来说并不明显;我原以为:
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
结果如下:
locals_1: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_2: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
locals_3: {'var': 'var!', 'locals_1': {...}, 'locals_2': {...}}
模式似乎是,(n)调用 locals ,所有的 返回的locals-dicts是相同的,它们都包括在内 第一个(n-1)本地人 - dicts。
有人可以解释一下吗?
更具体地说:
为什么locals_1会包含自己?
为什么locals_1包含locals_2?是什么时候分配locals_1 func 是创建还是执行?
为什么locals_3不包含在任何地方?
“{...}”是否表示“无休止的递归”?有点像 那些镜子面对面的照片?
答案 0 :(得分:5)
让我们运行此代码:
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()
这将在输出中:
44860744 44860744 {'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_1': {...}, 'var': 'var!'}
44860744 44860744 {'locals_2': {...}, 'locals_3': {...}, 'locals_1': {...}, 'var': 'var!'}
locals()
此处按预期增长,但您将引用分配给locals()
,而不是locals()
的值每个变量。
每次分配后locals()
都会更改,但引用不会,因此每个变量都指向同一个对象。在我的输出中,所有对象id
都相等,这就是证据。
更长的解释
这些变量与该对象具有相同的链接(引用)。基本上,Python中的所有变量都是引用(与指针类似的概念)。
locals_1 locals_2 locals_3
\ | /
\ | /
V V V
---------------------------------------------
| single locals() object |
---------------------------------------------
他们完全不知道locals()
有什么价值,他们只知道在需要时(在某处使用变量时)从何处获取它。 locals()
上的更改不会影响这些变量。
在您运行的最后,您将返回三个变量,这就是您打印它们时发生的情况:
print(locals_N) -> 1. Get object referenced in locals_N
2. Return the value of that object
请参阅?因此,这就是为什么它们具有完全相同的值,即locals()
时print
所具有的值。
如果再次更改locals()
(以某种方式)然后运行打印语句,将打印3次?是的,locals()
的新值。
答案 1 :(得分:2)
我喜欢你的问题,非常好。
是的,locals()
有点神奇,但是按照你的方法,你很快就能得到它并且会喜欢它。
In [1]: a = {"alfa": 1, "beta": 2}
In [2]: b = a
In [3]: b
Out[3]: {'alfa': 1, 'beta': 2}
In [4]: b["gama"] = 3
In [5]: b
Out[5]: {'alfa': 1, 'beta': 2, 'gama': 3}
In [6]: a
Out[6]: {'alfa': 1, 'beta': 2, 'gama': 3}
如您所见,a
在b
被修改的时刻间接更改,因为a
和b
都指向内存中的相同数据结构。 / p>
locals()
正在返回包含所有局部变量的字典编辑:澄清此dict何时更新
因此locals()
来电时存在的所有局部变量都存在于此。如果您对locals()
进行后续调用,则此字典会在通话时更新。
因为locals_1
是对所有本地定义变量的字典的引用。只要locals_1
成为本地命名空间的一部分,它就会获得返回的字典locals()
的一部分。
与前一个相同的答案适用。
这是你问题中最困难的部分。经过一些研究后,我找到了关于这个主题的优秀文章:http://nedbatchelder.com/blog/201211/tricky_locals.html
事实是,locals()
返回一个字典,其中包含对所有局部变量的引用。但棘手的部分是,它不是直接的结构,它是一个字典,仅在此刻更新,locals()
被调用。
这解释了结果中缺少locals_3。所有结果都指向同一个字典,但在引入locals_3
变量后,它不会得到更新。
当我在返回之前添加另一个打印locals()
时,我发现它在那里,没有它。
UFF。
我会把它读成"还有更多的东西"。但我认为,你是对的,这是打印递归数据结构的解决方案。没有这样的解决方案,字典就无法在有限的时间内打印出来。
有一个习惯用法,其中locals()正在缩短你的代码,在string.format()
name = "frost"
surname = "national"
print "The guy named {name} {surname} got great question.".format(**locals())
答案 2 :(得分:1)
frostnational和Jan Vlcinsky已经对幕后发生的事情给出了很好的解释。这是实现您最初预期行为的一小部分。您可以使用copy
方法创建locals()
dict的副本。更新locals()
时不会更新该副本,因此它包含"快照"你期望的那样:
In [1]: def func():
...: var = 'var!'
...: locals1 = locals().copy()
...: locals2 = locals().copy()
...: locals3 = locals().copy()
...: return locals1, locals2, locals3
...:
In [2]: locals1, locals2, locals3 = func()
In [3]: locals1
Out[3]: {'var': 'var!'}
In [4]: locals2
Out[4]: {'locals1': {'var': 'var!'}, 'var': 'var!'}
In [5]: locals3
Out[5]:
{'locals1': {'var': 'var!'},
'locals2': {'locals1': {'var': 'var!'}, 'var': 'var!'},
'var': 'var!'}
正如所料,每个副本仅包含在调用locals()
之前定义的变量。
答案 3 :(得分:0)
我原来的问题归结为'只是什么是 locals()
?'
这是我目前的(推测)理解,用Pythonese写的:
+++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
每个本地命名空间都有自己的命名空间表,可以是
使用 locals
内置函数查看总计。
(实际上, namepace-table 就像一个包含“identifier”:object 条目的dict;对于每个项目,键是名称(以字符串形式) )为对象分配(或“绑定”)。)
在非全局级别调用时,locals
返回解释器对当前本地命名空间表的唯一表示形式:“动态”,始终保持最新状态,专门的,类似dict的对象。
它不是一个简单的dict,也不是实际的名称表,但它实际上是“活着的”,并且只要它被引用就会立即从活动表中更新(当跟踪打开时,它会随每个语句一起更新)。
在退出范围时,此对象将消失,并在下次调用locals
时为当前范围重新创建。
(在全局(模块化)级别调用时,locals
会返回 globals()
,Python的全局命名空间表示,可能具有不同的性质)。
因此,L = locals()
将名称L
绑定到本地命名空间表的'stand-in';随后,只要引用L
,就会刷新并返回此对象
并且,locals()
绑定(在同一范围内)的任何其他名称将是此同一对象的别名。
请注意,分配给L
的{{1}}必然会成为“无限递归”对象(显示为dict locals()
),这对您来说可能重要,也可能不重要。但是,您可以随时制作{...}
的简单字典副本
L
的某些属性(例如 locals()
)也会返回简单对象。
要捕获函数中locals()的“原始”快照,请使用不进行任何本地分配的技术;例如,将副本作为参数传递给函数,该函数将其pickle到文件中。
有keys
的细节,以及它的行为方式;它包含来自函数块的自由变量,但不包括类,并且文档警告不要试图改变L
的内容(它可能不再“镜像”名称表)。
也许应该只读(复制等)
(为什么L
被设计为'实时',而不是'快照',是另一个主题。)
总结:
locals()
是一个独特的专业对象(以dict形式);它是Python当前本地命名空间表的 live 表示(不是冻结快照)
+++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
获得我期望的结果的一种方法是在每一步产生locals()
的副本(这里使用dict.copy):
locals()
调用func ,并显示返回值:
# 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
返回的是简单的dict对象,它捕获本地命名空间的增长阶段 这就是我的意图。
复制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!'}}}
(此处为“L”)的其他可能方式有locals()
,dict(L)
和copy.copy(L)
。