How do I obtain the non-local variables for the current scope? The functions vars
, locals
, and globals
exist, but is there a function to get the nonlocals
?
Why aren't the nonlocals listed when calling vars
?
Update
My issue is that there's no way to enumerate the variables available in the current scope, as neither vars
or globals
includes the non-locals AFAICT.
I frequently use vars
in code such as the following:
'{meh[0]}/{meh[3]} {a}{b}{c}'.format(**vars())
Which fails if any of these variables are in the scope of a containing function.
From within running code, you can easily get the names of the nonlocal variables - but retriving their content in a way a call to locals
gets you a dictionary is a bit trickier.
The used nonlocal
variable names are stored in the current running code object, in the co_freevars attribute.
So, getting the nonlocal names is a matter of:
names = inspect.currentframe().f_code.co_freevars
The contents for these variables, however, are stored in the __closure__
attribute (func_closure
, in Python 2), of the function object instead. (Not the code object). The problem is that, without "aid from outside", there is no easy way for a running code to get to the function object it is running on. You can get to the frame object, which links to the code object, but there are no links back to the function object. (For a top level defined function one could explicitly use the function known name, as used in the def
statement` but for an enclosed function, that is returned to a caller, there is no way of knowing its name either).
So, one has to resort to a trick - getting all the objects that link to the current code object, by using the gc module (garbage collector) - there is a gc.get_referrers
call - it will return all the function objects that link to the code object one holds.
So, inside a function with non_local variables one could do:
import inspect, gc
def a(b):
b1 = 2
def c():
nonlocal b1
print (b)
code = inspect.currentframe().f_code
names = code.co_freevars
function = [func for func in gc.get_referrers(code) if isinstance(func, FunctionType)][0]
nonlocals = dict (zip(names, (x.cell_contents for x in function.__closure__ )))
print(nonlocals)
return inspect.currentframe()
return c
c = a(5)
f = c()
And therefore retrieve the names and values of the nonlocals. But this won't work if you have more than one instance of that function around (that is, if the function of interested was created more than once with more than one call to the functin that generates it) - becasue all of those instances will link to the same code object. The example above, assumes there is only one function running with the current code - and would work correctly in this case. Another call to the factrory function would create another function, with potentially other values for the nonlocal variables, but with the same code object - the function =
list genrator above would retrieve all of those, and arbitrarily pick the first of those.
The "correct" function is the one on which the current code is executing - I am trying to think of a way of retrieving this information, but can't get to it. If I can, I will complete this answer, but for now, this can't help you to retrieve the nonlocals values values.
(just found out that trying to use "eval" with a nonlocal variable name won't work as well)
It looks like that the only thing linking the current running frame to the function object where the nonlocal variables values are held is created at run time inside the native side of the Python interpreter. I can't think of a way of getting to it short of using the ctypes module to look at interpreters data structures at runtime, which would, of course, be unsuitable for any actual production code.
The bottom line: you can reliably retrieve the nonlocal variable names. But it looks like you can't get their value given their name as a string (nor rebind then).
You could try opening a feature-request for a "nonlocals" call on Python's bug tracker or on Python-ideas mailing list.
来源:https://stackoverflow.com/questions/8968407/where-is-nonlocals