42

I would like to define globals in a "programmatic" way. Something similar to what I want to do would be:

definitions = {'a': 1, 'b': 2, 'c': 123.4}
for definition in definitions.items():
    exec("%s = %r" % definition)  # a = 1, etc.

Specifically, I want to create a module fundamentalconstants that contains variables that can be accessed as fundamentalconstants.electron_mass, etc., where all values are obtained through parsing a file (hence the need to do the assignments in a "programmatic" way).

Now, the exec solution above would work. But I am a little bit uneasy with it, because I'm afraid that exec is not the cleanest way to achieve the goal of setting module globals.

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260

3 Answers3

70

Here is a better way to do it:

import sys
definitions = {'a': 1, 'b': 2, 'c': 123.4}
module = sys.modules[__name__]
for name, value in definitions.iteritems():
    setattr(module, name, value)
Nadia Alramli
  • 111,714
  • 37
  • 173
  • 152
  • 10
    Maybe you could describe why this is better? – tom10 Sep 16 '09 at 00:30
  • 10
    @tom10, there's no documented guarantee that _changes_ to `globals()` will actually persist the changes to the underlying module. It works today, might break tomorrow. Nadia's solution OTOH is solid (hence +1 from me;-). – Alex Martelli Sep 16 '09 at 01:45
  • 2
    I like this way of doing it, but about locals() and globals(): the current docs seem to imply it will always work: locals: Update and return a dictionary representing the current local symbol table. Warning: The contents of this dictionary should not be modified; changes may not affect the values of local variables used by the interpreter. globals: Return a dictionary representing the current global symbol table. This is always the dictionary of the current module. Maybe the docs need to be tweaked up... – Ned Batchelder Sep 16 '09 at 03:15
  • Ah ah! `sys.modules[…]` is the key… Thank you for pointing it out! Time to study the documentation of `sys`… – Eric O. Lebigot Sep 16 '09 at 13:07
  • 4
    @Alex: It appears that `globals()` is (at least as of now) updatable. Ref.: http://stackoverflow.com/questions/4859217/programmatically-creating-variables-in-python/4859312#4859312 (discussion with ncoghlan). – Eric O. Lebigot Jun 28 '11 at 06:54
  • If you have a module that is first run as `__main__` and then later imported (like when using `python-webpy`) and you only want to set the global module variables once, then do something like `if __name__ == '__main__': setattr(sys.modules['__main__'], 'debug', debug)` . Outside of that `if` block do `debug = getattr(sys.modules['__main__'], 'debug')` – JamesThomasMoon Mar 25 '15 at 23:01
51

You can set globals in the dictionary returned by globals():

definitions = {'a': 1, 'b': 2, 'c': 123.4}
for name, value in definitions.items():
    globals()[name] = value
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 11
    +1, as `globals()` can indeed be modified (http://stackoverflow.com/questions/4859217/programmatically-creating-variables-in-python/4859312#4859312). – Eric O. Lebigot Aug 03 '11 at 14:04
  • Another good point brought up is that changes to `globals()` will persist without error, but there is no such guarantee with `locals()`. – RattleyCooper Apr 12 '16 at 02:41
  • this causes a TypeError: '<' not supported between instances of 'int' and 'str' for me when I then call dir() – lobi Oct 21 '19 at 01:48
  • Here's [Nick Coghlan's comment](https://stackoverflow.com/q/4859217/673991#comment7646823_4859312) that clinches it for me in support of `globals()[name] = value`: "...given the hoops we jump through as core devs to make sure that modifying module state via globals() doesn't break... There's also plenty of standard library code that uses globals() that way." – Bob Stein Feb 23 '22 at 14:13
4

You're right, exec is usually a bad idea and it certainly isn't needed in this case.

Ned's answer is fine. Another possible way to do it if you're a module is to import yourself:

fundamentalconstants.py:

import fundamentalconstants

fundamentalconstants.life_meaning= 42

for line in open('constants.dat'):
    name, _, value= line.partition(':')
    setattr(fundamentalconstants, name, value)
bobince
  • 528,062
  • 107
  • 651
  • 834