Modify the local scope inside a function

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sun Feb 26 01:19:52 EST 2006


On Sat, 25 Feb 2006 15:53:08 -0800, Sandra-24 wrote:

> Is there a way in python to add the items of a dictionary to the local
> function scope? i.e. var_foo = dict['var_foo']. I don't know how many
> items are in this dictionary, or what they are until runtime.

Are you sure you need to do this? How do you use those variables later?

That is, I'm thinking that if you do this:

variables = {"var_foo"=0, "var_bar"=1, "var_baz"=3}
somehow_transfer_to_local_scope(variables)
print var_foo  # or whatever...
print var_bar
print var_baz

it sort of looks pointless to me. In other words, if you have to deal with
the variables BY NAME in your code, then just define them by name. And if
you don't deal with them by name in your code, then they don't need names:

variables = {"var_foo"=0, "var_bar"=1, "var_baz"=3}
for item in variables.values():
    print item


Okay, so they are *really* basic (and pointless) examples. But in most
cases that people say they want to turn strings into variables ("if I have
a string 'abc1', how do I turn it into a variable abc1?") it turns out
that doing so is not the best way of solving their problem.



> exec statements are difficult for debuggers to deal with, 

Which is one of the reasons why exec statements should be avoided whenever
possible. The rule of thumb I use is, any time I feel that I absolutely
must use exec, I drink tequila until the urge goes away.

*wink*

> so as a
> workaround I built my code into a function and saved it in a .py file.

That sounds like normal practice to me. Why aren't you doing that in the
first place?


> The I load the .py file as a module and call the function instead. This
> works great, and it has the added advantage of precompiled versions of
> the code being saved as .pyc and .pyo files. (faster repeated
> execution)

.pyc files don't give you faster execution on repeated calls. They give
you faster loading time on import. The benefit is on the first
import, not subsequent calls to the function. There may be other benefits
as well, but execution speed is not one of them.

Here is a test:

$ ls ftest*
ftest.py  ftest.pyc
$ cat ftest.py
def f(): return sum(range(100))
 
(you'll just have to trust me that ftest.pyc is the compiled version of
ftest.py)

$ python /usr/lib/python2.3/timeit.py --setup="def f(): return sum(range(100))" "f()" 
100000 loops, best of 3: 14 usec per loop 

$ python /usr/lib/python2.3/timeit.py --setup="from ftest import f" "f()"
100000 loops, best of 3: 14.1 usec per loop

No appreciable difference in execution speed.


> The only trouble was I execed inside a specially created scope
> dictionary containing various variables and functions that the code
> requires. 

Ah, your exed'ed code is effectively using global variables.

> I can't seem to figure out how to get this same effect inside
> the function. 

Define the names you need in the module.

var_foo = 0
var_bar = 1
var_baz = 3

def function(x):
    return x + var_foo + var_bar + var_baz


> Right now I'm passing the dict as an argument to the
> function, but I can't modify locals() so it doesn't help me.

No, you can't modify locals. Locals returns a copy of the local
environment as a dict, but changing that dict doesn't change the local
variables.

There is an exception to that rule: if you run locals from the
interpreter, outside of a function, locals() is the same as globals():

>>> locals() is globals()
True

This means that this will work from the interpreter:

>>> x = 6
>>> locals()['x'] = -1
>>> x
-1

but not anywhere else. That's a (rare) Python gotcha.



-- 
Steven.




More information about the Python-list mailing list