globals(),locals()和vars()有什么区别?

问题:globals(),locals()和vars()有什么区别?

是什么区别globals()locals()vars()?他们返回什么?更新结果有用吗?

What is the difference between globals(), locals(), and vars()? What do they return? Are updates to the results useful?


回答 0

这些都返回一个字典:

  • globals() 总是返回模块命名空间的字典
  • locals() 总是返回当前命名空间字典
  • vars()返回当前命名空间字典(如果不带参数调用)或参数字典。

localsvars可以使用更多解释。如果locals()在函数内部调用,它将使用当前局部变量命名空间(加上任何闭包变量)的值更新dict并返回它。locals()每次对同一堆栈框架的多次调用都会返回相同的字典-它作为其f_locals属性附加到堆栈框架对象。dict的内容在每次locals()调用和每个f_locals属性访问时更新,但在此类调用或属性访问时更新。分配变量时,它不会自动更新,并且在dict中分配条目不会分配相应的局部变量:

import inspect

def f():
    x = 1
    l = locals()
    print(l)
    locals()
    print(l)
    x = 2
    print(x, l['x'])
    l['x'] = 3
    print(x, l['x'])
    inspect.currentframe().f_locals
    print(x, l['x'])

f()

给我们:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

第一个print(l)只显示一个'x'条目,因为分配要llocals()调用之后进行。再次print(l)调用locals()后,第二个显示一个l条目,即使我们没有保存返回值。第三和第四print步表明分配变量不会更新l,反之亦然,但是在我们访问之后f_locals,局部变量会locals()再次复制到其中。

两个注意事项:

  1. 此行为是CPython特有的-其他Python可能允许更新使它自动返回到本地命名空间。
  2. 在CPython 2.x中,可以通过exec "pass"在函数中放置一行来使其工作。这会将函数切换到较旧的执行速度较慢的模式,该模式使用locals()dict作为局部变量的规范表示。

如果locals()被称为函数返回实际的字典,它是当前的命名空间。命名空间进一步的变化反映在字典中,并到词典中的变化反映在命名空间:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

给我们:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

到目前为止,我所说的所有事情locals()也适用于vars()…这里是不同的: vars()接受单个对象作为其参数,如果给它一个对象,它将返回该__dict__对象的。对于典型的对象,__dict__大多数属性数据都存储在该对象中。这包括类变量和模块全局变量:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

这给了我们:

three

请注意,函数__dict__是其属性命名空间,而不是局部变量。对于一个函数来说__dict__,存储局部变量是没有意义的,因为递归和多线程意味着一个函数可以同时有多个调用,每个调用都有自己的局部变量:

def f(outer):
    if outer:
        f(False)
        print('Outer call locals:', locals())
        print('f.__dict__:', f.__dict__)
    else:
        print('Inner call locals:', locals())
        print('f.__dict__:', f.__dict__)

f.x = 3

f(True)

这给了我们:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

在这里,f递归调用本身,因此内部和外部调用重叠。每个调用时都会看到自己的局部变量locals(),但是两个调用都看到相同的f.__dict__,并且其中f.__dict__没有任何局部变量。

Each of these return a dictionary:

  • globals() always returns the dictionary of the module namespace
  • locals() always returns a dictionary of the current namespace
  • vars() returns either a dictionary of the current namespace (if called with no argument) or the dictionary of the argument.

locals and vars could use some more explanation. If locals() is called inside a function, it updates a dict with the values of the current local variable namespace (plus any closure variables) as of that moment and returns it. Multiple calls to locals() in the same stack frame return the same dict each time – it’s attached to the stack frame object as its f_locals attribute. The dict’s contents are updated on each locals() call and each f_locals attribute access, but only on such calls or attribute accesses. It does not automatically update when variables are assigned, and assigning entries in the dict will not assign the corresponding local variables:

import inspect

def f():
    x = 1
    l = locals()
    print(l)
    locals()
    print(l)
    x = 2
    print(x, l['x'])
    l['x'] = 3
    print(x, l['x'])
    inspect.currentframe().f_locals
    print(x, l['x'])

f()

gives us:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

The first print(l) only shows an 'x' entry, because the assignment to l happens after the locals() call. The second print(l), after calling locals() again, shows an l entry, even though we didn’t save the return value. The third and fourth prints show that assigning variables doesn’t update l and vice versa, but after we access f_locals, local variables are copied into locals() again.

Two notes:

  1. This behavior is CPython specific — other Pythons may allow the updates to make it back to the local namespace automatically.
  2. In CPython 2.x it is possible to make this work by putting an exec "pass" line in the function. This switches the function to an older, slower execution mode that uses the locals() dict as the canonical representation of local variables.

If locals() is called outside a function it returns the actual dictionary that is the current namespace. Further changes to the namespace are reflected in the dictionary, and changes to the dictionary are reflected in the namespace:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

gives us:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

So far, everything I’ve said about locals() is also true for vars()… here’s the difference: vars() accepts a single object as its argument, and if you give it an object it returns the __dict__ of that object. For a typical object, its __dict__ is where most of its attribute data is stored. This includes class variables and module globals:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

which gives us:

three

Note that a function’s __dict__ is its attribute namespace, not local variables. It wouldn’t make sense for a function’s __dict__ to store local variables, since recursion and multithreading mean there can be multiple calls to a function at the same time, each with their own locals:

def f(outer):
    if outer:
        f(False)
        print('Outer call locals:', locals())
        print('f.__dict__:', f.__dict__)
    else:
        print('Inner call locals:', locals())
        print('f.__dict__:', f.__dict__)

f.x = 3

f(True)

which gives us:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

Here, f calls itself recursively, so the inner and outer calls overlap. Each one sees its own local variables when it calls locals(), but both calls see the same f.__dict__, and f.__dict__ doesn’t have any local variables in it.