updating local()

F

Flavio

Hi,

I heard time and again that you are not _supposed_ to update the
locals dictionary.

Can anyone tell me why, if the following code works, I should not do
this?

#
# Extending Local namespace
#

def fun(a=1,b=2,**args):

print 'locals:',locals()
locals().update(args)
print locals()

e = {'s':3,'e':4}
fun(k=10,v=32,**e)


thanks,

Flávio
 
S

Steve Holden

Flavio said:
Hi,

I heard time and again that you are not _supposed_ to update the
locals dictionary.

Can anyone tell me why, if the following code works, I should not do
this?

#
# Extending Local namespace
#

def fun(a=1,b=2,**args):

print 'locals:',locals()
locals().update(args)
print locals()

e = {'s':3,'e':4}
fun(k=10,v=32,**e)
Because it depends on the current implementation and isn't guaranteeed
to work in the future.

regards
Steve
 
S

Sybren Stuvel

Flavio enlightened us with:
Can anyone tell me why, if the following code works, I should not do
this?

def fun(a=1,b=2,**args):

print 'locals:',locals()
locals().update(args)
print locals()

Because it's very, very, very insecure. What would happen if someone
found a way to call that function? It could replace any name in the
locals dictionary, including functions from __builtins__. In other
words: probably the whole program could be taken over by other code by
just one call to that function.

Sybren
 
F

Fredrik Lundh

Flavio said:
Can anyone tell me why, if the following code works, I should not do
this?

because it doesn't work:

#
# Extending Local namespace, now with Local namespace
#

def fun(a=1,b=2,**args):
k="K"
v="V"
print 'locals:',locals()
locals().update(args)
print locals()
print k
print v

e = {'s':3,'e':4}
fun(k=10,v=32,**e)


</F>
 
D

Duncan Booth

Flavio said:
Can anyone tell me why, if the following code works, I should not do
this?

#
# Extending Local namespace
#

def fun(a=1,b=2,**args):

print 'locals:',locals()
locals().update(args)
print locals()

e = {'s':3,'e':4}
fun(k=10,v=32,**e)
Because if all you want is to update a dictionary you might as well use
another dictionary which isn't locals(). If what you want is to update the
local variables, that won't work:
s = 1
print 'locals:',locals()
locals().update(args)
print locals()
print s, e

locals: {'a': 1, 's': 1, 'args': {'s': 3, 'e': 4, 'k': 10, 'v': 32}, 'b':
2}
{'a': 1, 'b': 2, 'e': 4, 'k': 10, 'args': {'s': 3, 'e': 4, 'k': 10, 'v':
32}, 's': 1, 'v': 32}
1 {'s': 3, 'e': 4}
Note that k, v, and e are added to the locals dictionary, but real local
variables such as 's' are not updated. Also you cannot access k, v, and e
as local variables within the function, if you try you get the global
variables (if they exist).

Also, this behaviour is undefined. Different versions of Python will behave
differently, and even minor code changes can change the behaviour. With
this version, 's' still doesn't update, but we can now access the 'e' value
as a variable:
s = 1
print 'locals:',locals()
locals().update(args)
print locals()
exec "print 'hi'"
print s,e

locals: {'a': 1, 's': 1, 'args': {'s': 3, 'e': 4, 'k': 10, 'v': 32}, 'b':
2}
{'a': 1, 'b': 2, 'e': 4, 'k': 10, 'args': {'s': 3, 'e': 4, 'k': 10, 'v':
32}, 's': 1, 'v': 32}
hi
1 4
Short answer: don't even think of trying to update locals().
 
J

jepler

I'm surprised you found any example of 'locals().update' that worked.
Here's one that doesn't work:
def f(y):
locals().update({'x': y})
return x

print f(3) # prints 3?

Jeff

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFDRBXeJd01MZaTXX0RAru9AKCLn7jtxwCFBIn6+ztalyhoN1vT9wCfR6yZ
1HxA4j8rhl/RlsF4mDmPlI8=
=rjtz
-----END PGP SIGNATURE-----
 
S

Sybren Stuvel

Jp Calderone enlightened us with:
If I can call functions in your process space, I've already taken
over your whole program.

That's true for standalone programs, but not for things like web
applications, RPC calls etc.

Sybren
 
F

Flavio

Ok,

I got it!

Its vey insecure, and it is not guaranteed to work. Fine.

Now what would you do if you wanted to pass a lot of variables (like a
thousand) to a function and did not wanted the declare them in the
function header?

Flávio
 
R

Richard Brodie

Now what would you do if you wanted to pass a lot of variables (like a
thousand) to a function and did not wanted the declare them in the
function header?

I'd lie down until I felt better.
 
S

Steve Holden

Richard said:
I'd lie down until I felt better.
Or alternatively put them in a 1,000-element list. Just as a matter of
interest, what on *earth* is the use case for a function with a thousand
arguments?

regards
Steve
 
S

Simon Brunning

Now what would you do if you wanted to pass a lot of variables (like a
thousand) to a function and did not wanted the declare them in the
function header?

I'd think twice. If on reflection I decided I really wanted to do it,
I'd pass them all in a dictionary.
 
D

Diez B. Roggisch

Flavio said:
Ok,

I got it!

Its vey insecure, and it is not guaranteed to work. Fine.

Now what would you do if you wanted to pass a lot of variables (like a
thousand) to a function and did not wanted the declare them in the
function header?

use a dict or list? This is almost certainly a design smell - and a
pretty strong one, too.. Nobody is using so many variables and actually
typing them - so there certainly is some naming scheme that could be
used to access the values from a dict.

Or, to put it differently: show us the code that uses thousands of
variables, and we show you how to improve that.

Diez
 
F

Flavio

I wish all my problems involved just a couple of variables, but
unfortunately the real interesting problems tend to be complex...

As a last resort this problem could be solved by something like this:

def fun(**kw):
a = 100
for k,v in kw.items():
exec('%s = %s'%(k,v))
print locals()

{'a': 1, 'k': 'b', 'b': 2, 'kw': {'a': 1, 'b': 2}, 'v': 2}

But that would be utterly stupid! So much for not being able to write
to locals()....

any better Ideas?
 
F

Flavio

Ok, its not thousands, but more like dozens of variables...
I am reading a large form from the web which returns a lot of values.
(I am Using cherrypy)

I know I could pass these variables around as:

def some_function(**variables):
...

some_function(**variables)

but its a pain in the neck to have to refer to them as
variables['whatever']...

dont you think?

Flavio
 
R

Robert Kern

Flavio said:
Ok, its not thousands, but more like dozens of variables...
I am reading a large form from the web which returns a lot of values.
(I am Using cherrypy)

I know I could pass these variables around as:

def some_function(**variables):
...

some_function(**variables)

but its a pain in the neck to have to refer to them as
variables['whatever']...

dont you think?

Use a Bunch.

class Bunch(dict):
def __init__(self, *args, **kwds):
dict.__init__(self, *args, **kwds)
self.__dict__ = self

--
Robert Kern
(e-mail address removed)

"In the fields of hell where the grass grows high
Are the graves of dreams allowed to die."
-- Richard Harter
 
E

El Pitonero

Flavio said:
I wish all my problems involved just a couple of variables, but
unfortunately the real interesting problems tend to be complex...

def fun(**kw):
a = 100
for k,v in kw.items():
exec('%s = %s'%(k,v))
print locals()


{'a': 1, 'k': 'b', 'b': 2, 'kw': {'a': 1, 'b': 2}, 'v': 2}

any better Ideas?

Actually, your solution is not bad. Some potential problems are: (1)
unintentional name collisions with other variables, including
globals/builtins, (2) it's easy to unpack variables into locals(), but
not easy to pack them back, since locals() are often contaminated with
extra auxiliary variables.

Your problem happens often in the field of math formulas/equations.

I remember similar problem happens in C++, too. When one has a function
with a long list of parameters, in C++ one may find oneself updating
the funtion header/prototype all the time, which is very tiresome and
error-prone.

When you have complicated list of arguments to pass, it's better to put
them into a structure/object. This way, the function header/prototype
will remain the same, and you only need to change the declaration of
the object.

The parameter object(s) could be called:

- request and response, if input and output are separated
- param
- workspace, session, etc.

so, your function call would look like

class Param: pass
....
def f(p):
result = p.x + p.y
return result
....
p=Param()
p.x = 3
p.y = 4
result = f(p)

Now, you may not like the extra dots in the line:

result = p.x + p.y

My experience is: it's not that bad to have names with extra dots. I
know it's annoying, but not a total disaster. Plus, once you start to
use OOP, it makes your code more organized. It has its benefits. For
instance, very often your models/formulas have several versions. Using
OOP's class hierarchy inheritance mechanism allows you to try out
different versions or different ideas much more easily, and you can
roll back the changes more easily, too (instead of commenting out code
lines all over places.) If you do decide to go the route of OOP, the
lines:

p.x = 3
p.y = 4
p.z = 5

can be replaced by something like:

calculation_engine.set(x=3, y=4)
calculation_engine.set(z=5)

----------------------

The longer answer is: if you need complex formula evaluations, Python
is probably not the language to use. For speed and memory usage issues,
C++ is probably what you need. You can hookup your C++ program to
Python in various ways, but packing/unpacking variables seems
unavoidable. And even in C++ (where object attributes don't have the
dots inside the object's own methods), I still often end up using a lot
of dotted names for attributes from other objects.

If the problem is complex, then it's complex. I know you have your
equations, but you have to decide for yourself: is it better to do all
the packing/unpacking, or is it better to change your equations to use
the dotted names? There is no right or wrong answer, it all depends on
your particular situation.
 
G

Grant Edwards

Ok,

I got it!

Its vey insecure, and it is not guaranteed to work. Fine.

Now what would you do if you wanted to pass a lot of variables (like a
thousand) to a function and did not wanted the declare them in the
function header?

Pass them in a dictionary or tuple or list or object with
attributes.
 
B

Bengt Richter

Flavio said:
Ok, its not thousands, but more like dozens of variables...
I am reading a large form from the web which returns a lot of values.
(I am Using cherrypy)

I know I could pass these variables around as:

def some_function(**variables):
...

some_function(**variables)

but its a pain in the neck to have to refer to them as
variables['whatever']...

dont you think?

Use a Bunch.

class Bunch(dict):
def __init__(self, *args, **kwds):
dict.__init__(self, *args, **kwds)
self.__dict__ = self

--
Or use a version-sensitive byte-code hack to set some preset locals before executing,
either with one-time presets via normal decorator, e.g.,
... def foo():
... return locals()
... {'y': 222, 'x': 111, 'z': 'zee'}

Or the same, just using a predefined dict instead of the keyword format:
... def foo():
... return locals()
... {'a': 0, 'b': 1}

What happened to foo viat the decoration: 1 0 LOAD_CONST 1 ((0, 1))
3 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (a)
9 STORE_FAST 1 (b)

3 12 LOAD_GLOBAL 0 (locals)
15 CALL_FUNCTION 0
18 RETURN_VALUE

To mess with the same base function with different presets more dynamically,
use the explicit way of calling the decorator:

The base function:
... return locals()
...

decorate and invoke on the fly with particular presets:
{'a': 0, 'y': 123, 'b': 1, 'x': 'exx'}

The keyword way: {'y': 123, 'x': 'exx', 'hey': 'there'}

BTW, @presets does not change the signature of the function whose selected locals
are being preset from the decoration-time-generated constant, e.g.,
Traceback (most recent call last):
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: bar() takes at most 2 arguments (3 given)

Regards,
Bengt Richter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top