views:

283

answers:

2

Is there a way in Python to add to the locals name-space by calling a function without explicitly assigning variables locally?

Something like the following for example (which of course doesn't work, because locals() return a copy of the local name-space) where the print statement would print '1'.

def A():
  B(locals())
  print x

def B(d):
  d['x'] = 1
+3  A: 

In Python 2.*, you can disable the normal optimizations performed by the Python compiler regarding local variable access by starting your function with exec ''; this will make the function very much slower (I just posted, earlier today, an answer showing how the local-variable optimization can easily speed code up by 3 or 4 times), but it will make your desired hack work. I.e., in Python 2.*:

def A():
  exec ''
  B(locals())
  print x

def B(d):
  d['x'] = 1

A()

will emit 1, as you desire.

This hack was disabled in Python 3.* (where exec is just a function, not a statement nor a keyword any more) -- the compiler now performs the local variable optimization unconditionally, so there is no longer any way to work around it and make such hacks work.

Alex Martelli
Brilliant, this is completely new to me. I'm going to investigate further. However I'm not quite really looking for a hack, esp one that'll degrade performance. Alex, how would you do it if you wanted to not explicitly define a set of variables locally (say have them defined dynamically by a subroutine) but want to access them locally as if they are local variables?
Dilan
@Dilan, I wouldn't -- "explicit is better than implicit" (do `import this` at an interactive Python prompt to see the whole text of the Zen of Python). There is no way to make local variables that isn't either explicit, or a hack.
Alex Martelli
@alex Thanks man!
Dilan
@Dilan, you're welcome, but remember to accept answers to your questions -- not just verbally, but by using the tickmark-like icon near the answer's count of upvotes. That's fundamental SO etiquette!-)
Alex Martelli
+1  A: 

Seems pretty horrible to rely on a hack like exec ''. What about communicating like this with the global statement, it seems to work:

>>> def outer():
...   global x
...   b()
...   print x
... 
>>> def b():
...   global x
...   x = 2
... 
>>> outer()
2

You could create a namespace for your variables instead:

class Namespace(object):
    pass

def A():
  names = Namespace()
  B(names)
  print names.x

def B(d):
  d.x = 1

Then use names.x or getattr(names, "x") to access the attributes.

kaizer.se