Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is nonlocals()?

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.

like image 869
Matt Joiner Avatar asked Jan 23 '12 07:01

Matt Joiner


People also ask

Where is the nonlocal keyword used in Python?

The nonlocal keyword is used to work with variables inside nested functions, where the variable should not belong to the inner function. Use the keyword nonlocal to declare that the variable is not local.

How do you access a variable outside a function in Python?

Using the global statement If you want to assign a value to a name defined outside the function, then you have to tell Python that the name is not local, but it is global. We do this using the global statement. It is impossible to assign a value to a variable defined outside a function without the global statement.

What is a nonlocal variable Python?

In python, nonlocal variables refer to all those variables that are declared within nested functions. The local scope of a nonlocal variable is not defined. This essentially means that the variable exists neither in the local scope nor in the global scope.

How do you access a variable inside a function in Python?

The variables that are defined inside the methods can be accessed within that method only by simply using the variable name. Example – var_name. If you want to use that variable outside the method or class, you have to declared that variable as a global.


1 Answers

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

from types import FunctionType

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.

like image 74
jsbueno Avatar answered Sep 27 '22 23:09

jsbueno