在函数中调用locals()不直观?

tfj*_*tfj 6 python locals

这可能是基本的,但可以帮助我理解命名空间.一个很好的解释可能会逐步执行函数定义执行时发生的事情,然后执行函数对象后发生的事情.递归可能使事情变得复杂.

结果对我来说并不明显; 我原以为:

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不包含在任何地方?

"{...}"是否表示"无休止的递归"?有点像镜子彼此面对的照片?

vau*_*tah 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()
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().


tfj*_*tfj 0

我最初的问题归结为“到底是什么 locals()?” 这是我目前的(推测性)理解,用 Python 语言编写:

++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++

Python 的 locals() 的本质是什么?

每个本地命名空间都有自己的命名空间表,可以使用内置函数查看整个命名空间表。locals名称空间表实际上就像一个保存“标识符”的字典:对象条目;对于每个项目,键是分配(或“绑定”)给对象的名称(字符串形式)。)

当在非全局级别调用时,locals返回解释器当前本地命名空间表的唯一表示:一个“动态”、始终最新的、专门的、类似字典的对象。

它不是一个简单的字典,也不是实际的名称表,但它实际上是“活动的”,并且在任何时候被引用时都会立即从活动表中更新(当跟踪打开时,它会随每个语句更新)。退出作用域时,该对象消失,并在下次调用
时为当前作用域重新创建。locals

(当在全局(模块化)级别调用时,locals返回globals()Python 的全局命名空间表示形式,它可能具有不同的性质)。

因此,L = locals()将名称绑定L到本地名称空间表的“替代”;随后,任何时候L被引用,该对象都会被刷新并返回。
并且,绑定(在同一范围内)的任何其他名称都将locals()是该同一对象的别名。

请注意L,被分配给locals(),必然会成为“无限递归”对象(显示为 dict {...}),这对您可能重要也可能不重要。不过,您可以随时制作简单的字典副本。 的某些属性(例如)也返回简单对象。L
locals()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)