3

Consider the following example:

def main():
  a = 'predefined'
  
  variables = {'a':'dynamic'}
  locals().update(variables)

  print a

if __name__ == '__main__':
    main()

When running the script, I would expect to see:

dynamic

but I see

predefined

Why? How can I get the dynamic value instead?

Update:

The reason why I ask: I have a program that takes many input arguments, with lengthy variable names. I was hoping to simply "unpack" whatever the argparse parser receives in a single call to locals().update(...)

def main():
  a = 'predefined'
  parser = argparse.ArgumentParser(description='My program')
  parser.add_argument('-a', type=int, default=a,  required=False);
  
  # Hoping to avoid typing lines like the following for every parameter:
  # a = parser.parse_args().a

  input_variables = vars(parser.parse_args())
  locals().update(input_variables)

  # Process stuff using the parameter names directly, e.g. 
  print a
Community
  • 1
  • 1
Amelio Vazquez-Reina
  • 91,494
  • 132
  • 359
  • 564
  • What exactly is the use model? You would usually just do `a = 'dynamic'`... it will overwrite the previous declaration, in the current scope. – Corley Brigman Feb 21 '14 at 14:36
  • Please wait a minute. I strongly suspect there IS a valid way to solve your problem, but I should check it. – Ellioh Feb 21 '14 at 14:40
  • @CorleyBrigman I have updated the post to explain where the question comes from. – Amelio Vazquez-Reina Feb 21 '14 at 14:42
  • @Ellioh see my update in case it helps. – Amelio Vazquez-Reina Feb 21 '14 at 14:43
  • 1
    Checked. My ideas did nor work. They all were around exec but using exec to set the variables is ugly and works only on python2, and using exec to create a nested scope that is a function definition and to call it requires the function code to be given as string, that is even more ugly. So, it can be done, but it better should not be done, as far as I see. – Ellioh Feb 21 '14 at 15:07
  • 1
    see the answer to this question: http://stackoverflow.com/questions/3109289/how-can-python-function-access-its-own-attributes – Corley Brigman Feb 21 '14 at 16:08

2 Answers2

6

It's mentioned in the locals documentation:

Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.

AFAIK, there's no reliable way to define/change local variables.

falsetru
  • 357,413
  • 63
  • 732
  • 636
  • Thanks, I am surprised that I don't see an error or a warning though. – Amelio Vazquez-Reina Feb 21 '14 at 14:29
  • @user815423426 -- Why would you see an error? it's documented to return a dictionary (and dict objects are mutable). – mgilson Feb 21 '14 at 14:30
  • Thanks @mgilson. You are right. One note on this: I have seen `locals().update(some_dict)` in SO all over the place. This is good to know. – Amelio Vazquez-Reina Feb 21 '14 at 14:30
  • you might do this to take the current scope, override some values, and then pass to `exec` or `eval`. – Corley Brigman Feb 21 '14 at 14:37
  • 1
    @CorleyBrigman, `exec "a = 'dynamic'"` works in Python 2.x. But not in Python 3.x (`exec("a = 'dynamic'")`) . See [this (Python 2)](http://ideone.com/zvtGUL), and [this (Python 3)](http://ideone.com/C8jF2Q). – falsetru Feb 21 '14 at 14:40
  • @falsetru I updated the OP. Perhaps I am a victinm of the XY problem – Amelio Vazquez-Reina Feb 21 '14 at 14:47
  • Thanks falsetru - I was just hoping to avoid using the prefix everywhere (e.g. sometimes I decide a variable should be an input argument and I need to update every usage of it in the code with `args.variable`) – Amelio Vazquez-Reina Feb 21 '14 at 14:56
  • @user815423426, I removed the comment after I see `..using the parameter names directly` in the comment in the code. – falsetru Feb 21 '14 at 14:59
  • @falsetru - sorry, i wasn't clear. my point was that doing `locals().update(some_dict)` is useless to change local variables. however, it is occasionally useful to do something like `exec(statement, locals=locals().update(some_dict))`. in this case, you're not changing locals, but retrieving & modifying to change the environment `statement` executes in. – Corley Brigman Feb 21 '14 at 15:48
0

Do you want to create variables in locals()? I would suggest to create an empty object and access the variables as obj.var_name. It is almost as readable as just var_name, and is much less ugly than all the games with exec.

from copy import copy


class Empty(object):
    pass


def main():
    a = 'predefined'
    b = 'predefined'
    loc = copy(locals())
    variables = {'a':'dynamic'}
    loc.update(variables)
    obj = Empty()
    for k, v in loc.items(): #iteritems for Python 2
        setattr(obj, k, v)

    print(obj.a, obj.b) # print statement for Python 2
Ellioh
  • 5,162
  • 2
  • 20
  • 33
  • [`argparse.ArgumentParser.parse_args`](http://docs.python.org/2/library/argparse.html#the-parse-args-method) returns a [`Namespace` object](http://docs.python.org/2/library/argparse.html#argparse.Namespace) (similar to `Empty` in your code). BTW `print` is not an operator. It's a statement in Python 2, a function in Python 3. – falsetru Feb 21 '14 at 15:29
  • Ok for Namespace, though importing it is not much shorter. As for statement/operator -- agree, fixed it. In Russian the word "operator" (оператор) usually means both "operator" and "statement". That often leads to misuse. – Ellioh Feb 21 '14 at 15:40
  • 1
    I mean OP is already using `argparse.ArgumentParser`. See Update in the question. – falsetru Feb 21 '14 at 15:44
  • Ellioh - As @falsetru said, I already have an object that groups the variables (`argparse.ArgumentParser`). What I am hoping to do is to be able to refer to `a` directly, without the need of an encapsulating object. – Amelio Vazquez-Reina Feb 21 '14 at 16:49