On 03/02/2020 05:31 PM, Makoto Kuwata wrote:

Can anyone explain about difference between above two?
Why it is possiable to change local var via `locals()` only in class
definition?

My Stackoverflow answer (https://stackoverflow.com/q/7969949/208880):


Each of `globals()`, `locals()`, and `vars()` 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:

This behavior is CPython specific -- other Pythons may allow the updates to 
make it back to the local namespace automatically.
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.
--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to