0

There is a place in our code where we must load the contents of a dictionary into locally scoped variables. I know this is generally not a good idea, but .. reasons.

Among the various answers to a similar question (Python: load variables in a dict into namespace), it seems there are a few ways to do this, and I'd like to know if there are any significant differences between the two methods below.

(note, I'm only focusin on answers that load key value pairs directly into the local scope, not methods for loading them into a namespace or similar object)

 

the_dict = {'x': 1', 'y': 'foo'}

 

Method 1:

for k, v in the_dict.items():
    exec('{k} = v'.format(k=k))

 

Method 2:

locals().update(the_dict)

 

The last method seems the simplest, but I'm not sure if there are any issues or gotchas that I'm not seeing. The values of the dictionary can be of an arbritary type, including objects such as Exceptions.

If it is relevant, this is targeting Python 2.7

Brian H.
  • 2,092
  • 19
  • 39
  • Method 2 is both safer and almost certainly faster (as `update()` is a more-or-less atomic operation, thus cutting out the overhead of a full `for` loop as well as the execution time of the `exec` function). – Green Cloak Guy Jul 30 '19 at 15:35
  • As was [pointed out](https://stackoverflow.com/questions/2597278/python-load-variables-in-a-dict-into-namespace#comment10171897_4014070) in the question you linked to, Method 2 [does not work at all](https://docs.python.org/3/library/functions.html#locals). Method 1 is the only way to update function locals (although it is ugly and slow). But even that will only work in Python 2, since `exec` is no longer a statement in Python 3. So, since this question asks nothing that wasn't already answered previously, it looks like it should be closed as a duplicate. – ekhumoro Jul 30 '19 at 18:21

1 Answers1

0

It is not only a bad idea, as you put it - it simply won't work in Python. At least not for a "local scope" for which "local" means a function scope.

TL;DR: If you "local scope" is a **module scope, just updating locals() is enough. If your "local scope" is a function block, updating locals() is ordinarily useless to create local variables dynamically. However, in Python 2, the use of exec inside a function body will disable the fast locals optimization and the bytecode op used will be LOAD NAME, which will match the vairbales defined in the exec body. Therefore, inside a function, you have to resort to the exec option (although, if the function contains a single exec statement, updating locals should work, as the optimization is then disabled)

It can work for module scope (which is called global in Python anyway), and using class bodies as a naming scope.

That is because inside functions, local variables are used from fixed slots for speed reason, which shadow the mapping from names->local vars (the dictionary returned by locals().). So, if while you can create new "local variables" in the locals() dict, if any attempt to access that variable is made in code in a line bellow that, in the same function body, that code will be compiled trying to get the variable from globals instead - that is:

a = "global"
def b():
   locals()["a"] = "local"
   print(a)

Results in:

>>> b()
global

On the other hand, eval is hardly needed - when you get a scope that will work, updating the proper dictionary already works. As mentioned above, a class body can be used for that, with the advantage that it will work as a namespace in a straightforward way:

>>> the_dict = {'x': '1', 'y': 'foo'}
>>> class A:
...    locals().update(the_dict)
... 
>>> A.x
'1'
>>> A.y
'foo'

And this is certainly "recomended". Just pay attention that there may be some side-effects of having a class created, in corner cases (if one of the objects in the dict have a __get__ method, for example, it will be used as a descriptor).

jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • (And just so I don't forget to mention it: you really should not be using Python2 by now) – jsbueno Jul 30 '19 at 15:44
  • Thank you for the information in the first part of the answer. I wasn't aware that the locals() dictionary was just a copry of the local namespace (which is made clear in the docs. However, I', downvoting the answer because it ignores the part of my question where I state _"... load values directly into the local scope, not methods for loading them into a namespace or similar"_. I'm not able to modify the code that accesses these variables to include the use of a class. Additionally, the comment about not using Python 2 neither answers the question nor provides useful information. – Brian H. Jul 30 '19 at 16:21
  • So, I updated the answer. If you will care, it does take time to write these answers for they to get downvotes if there are no errors in the answer. You can comment up asking for extra clarification without donwvoting. And there is a reason for the comment about Python 2, and you know which it is - it is being discontinued with a hard shut down scheduled for 2020-1-1. – jsbueno Jul 30 '19 at 18:10
  • @jsbueno It **is** possible to create new function local variables dynamically. It can be done (in Python 2, but not 3) using the `exec` statement, as shown in the question (i.e. Method 1). – ekhumoro Jul 30 '19 at 18:30
  • yes- sorry, I forgot that. I think it is fixed now. – jsbueno Jul 30 '19 at 18:42
  • Anyay, creating function-level dynamic variables seems to be the one thing that has no use at all. I guess the OP wants it at module level. – jsbueno Jul 30 '19 at 18:43
  • @jsbueno No, the OP definitely wants to use a local scope, as stated in the question title (and elsewhere). If it was module level, the solution would be trivial (i.e. update `globals()`). AFAICS, the question is a duplicate of many other questions on this topic, since it is not asking anything new. – ekhumoro Jul 30 '19 at 18:49