15

Possible Duplicate:
How to programmatically set a global (module) variable?

I have a class called Variable defined as so:

class Variable():
    def __init__(self, name):
        self.name = name
        self._value = None
    def value(self):
        return self._value
    def __repr__(self):
        return self.name

I want to create 26 capital single letter instances of Variable like this:

A = Variable('A')
B = Variable('B')
...
Z = Variable('Z')

So far I've tried various solutions, and the best I've come up with is:

from string import uppercase
for char in uppercase:
    exec "%s = %s" % (char, Variable(str(char))) in None

However, this doesn't run and gives me this error:

Traceback (most recent call last):
  File "C:\Users\Administrator\Dev\python\truthtable\truthtable.py", line 7, in <module>
    exec "%s = %s" % (char, Variable(str(char))) in None
  File "<string>", line 1, in <module>
NameError: name 'A' is not defined

What am I doing wrong?

Community
  • 1
  • 1
deeb
  • 1,332
  • 4
  • 15
  • 27

6 Answers6

27
from string import uppercase
_g = globals()
for char in uppercase:
    _g[char] = Variable(char)

Whether this is a good idea remains questionable :)

This only works for globals() (i.e. module level assignments), as the language definition explicitly states that modifying the dictionary returned by locals() may not actually change the value of any local variables.

You can also do something similar with the __dict__ of a class or instance.

John Y
  • 14,123
  • 2
  • 48
  • 72
ncoghlan
  • 40,168
  • 10
  • 71
  • 80
  • Modifying globals() is not a good idea: there is no official guarantee that this works: "This can fail: "there's no documented guarantee that changes to globals() will actually persist the changes to the underlying module. It works today, might break tomorrow." (Alex Martelli) – Eric O. Lebigot Feb 01 '11 at 08:46
  • 2
    That's not correct - that caveat applies *only* to locals(). globals() is guaranteed to work correctly by the language spec. – ncoghlan Feb 02 '11 at 05:27
  • @ncoghian: Here is the original comment by Alex, who is arguably an very seasoned Python programmer: http://stackoverflow.com/questions/1429814/how-to-programmatically-set-a-global-module-variable :) – Eric O. Lebigot Jun 27 '11 at 21:18
  • 10
    Alex is *definitely* well worth listening to, but given the hoops we jump through as core devs to make sure that modifying module state via `globals()` doesn't break, he's wrong on this particular point. There's also plenty of standard library code that uses `globals()` that way. The written guarantee in this case is one of omission: for `locals()`, we include an explicit note to say that modifications are not reliable (http://docs.python.org/library/functions.html#locals), but there is no such note for `globals()` (http://docs.python.org/library/functions.html#globals) – ncoghlan Jun 28 '11 at 01:36
  • Thank you for sharing this. I'm convinced about `globals()` being updatable. :) – Eric O. Lebigot Jun 28 '11 at 06:36
11

The cleanest approach by far that I have ever seen is to directly add variables (i.e. attributes) to your program (i.e. main module), as answered in another question on StackOverflow:

import sys
from string import ascii_uppercase

this_module = sys.modules[__name__]
for char in ascii_uppercase:
    setattr(this_module, char, Variable(char))

print A  # Works!

PS: Python core developers worked hard so as to make modifying globals() possible (see the comments to ncoghlan's answer). So, modifying globals() is an alternative. I'm not sure why many people feel that it is not so clean, though (maybe because the fact that globals() can be modified is not explicitly documented?).

Community
  • 1
  • 1
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
4

Well, it's not pretty but you can access the globals() dictionary directly:

for c in 'ABC':
    globals()[c] = Variable(c)
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
nimrodm
  • 23,081
  • 7
  • 58
  • 59
  • 1
    "Attribute assignment updates the module’s namespace dictionary, e.g., m.x = 1 is equivalent to m.__dict__["x"] = 1." (from http://docs.python.org/reference/datamodel.html) – ncoghlan Feb 02 '11 at 05:30
3

You don't need to use exec for this, as the other answers show, but here is the problem with your original code:

for char in uppercase:
    exec "%s = %s" % (char, Variable(str(char))) in None

This will exec A = A, B = B, etc. While this may be interesting if you're an ardent follower of Objectivism, python won't care too much for it.

You want to have the Variable() stuff inside the exec for it to matter:

for char in uppercase:
    exec "%s = Variable('%s')" % (char, char)

In the future, if you're trying to use exec, first try not to. Second, print what you're execing -- that will help to debug.

tkerwin
  • 9,559
  • 1
  • 31
  • 47
  • No downvote, but there is a cleaner way (and likely faster way, if that matters), as made explicit in my answer. – Eric O. Lebigot Feb 01 '11 at 14:29
  • 1
    Of course there is, I say not to do it this way in my answer. This is a fixed version of the original code that actually does use `exec`. – tkerwin Feb 01 '11 at 15:23
3

Don't try to stick this into a global variable. It's horrid. Do this instead.

import string

variables = {}
for x in string.ascii_uppercase:
    variables[x] = Variable(x)

The you access it from variables['Q'], for example.

If you want attribute access you can do:

class Variables(object): pass

and

variables = Variables()
for x in string.ascii_uppercase:
    setattr(variables, x) = Variable(x)

But in that case I'd rather create them dynamically in a Variables container class.

import string

class Variable(object):
    def __init__(self, value):
        self.value = value
    def __repr__(self):
        return "<Variable %s>" % self.value

class Variables(object):
    def __getattr__(self, n):
        if n in string.ascii_uppercase:
            v = Variable(n)
            setattr(self, n, v)
            return v
        raise AttributeError("Variables instance has no attribute %s" % n)

Usage:

>>> v = Variables()
>>> v.G
<Variable G>
>>> v.Y
<Variable Y>
>>> v.G is v.G
True

Simple and clean, no ugly hacks (well, except a getattr, and it's not that ugly), and you don't even have to make the Variables you don't need.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • While I agree that this is usually the way to go, there are definitely use cases for adding lots of objects directly into the local namespace, e.g. for math. x==sin(w)*(y+exp(-z)) is worlds better than v.x==m.sin(v.w)*(v.y+m.exp(-v.z)). – DSM Feb 01 '11 at 10:30
  • 1
    Well, I think mathematicians obsession with one letter long variable names is a mistake in the first place. :) It's useful when you do your calculations on paper, or simplify/expand formulas. But in the actual implementation of a formula in a programing language? Nyaaahhhh'm skeptical... :-) – Lennart Regebro Feb 01 '11 at 10:57
  • 2
    Maybe you can immediately remove all the "v." and "m." in my second expression and see the first expression, but I had to compare them almost character by character to be sure they were the same. An extra letter for each variable and function, and a dot which makes me think multiplication. I'm far more likely to miss an obvious error in the second form, as I have to overcome years of experience reading the standard form without extraneous characters strewn about. Readability counts, as I heard someone say somewhere ;-), and that principle doesn't always tilt in favour of more characters. – DSM Feb 01 '11 at 11:26
  • @DSM: Well, you do have a point with the dot. – Lennart Regebro Feb 01 '11 at 11:31
  • @Lennart: Interesting ideas! Raising AttributeError for non-matching attribute names would be more robust (otherwise, illegal attribute names like in `v.x` return a well-defined None value). – Eric O. Lebigot Feb 01 '11 at 13:48
  • @EOL: Absolutely, I just forgot it. :) – Lennart Regebro Feb 01 '11 at 14:04
0
from string import uppercase
for c in uppercase:
    locals()[c] = Variable(c)
print A

I'm not sure why you want to do this, though.

carl
  • 49,756
  • 17
  • 74
  • 82
  • 2
    This can officially fail: "The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter." (from the documentation). – Eric O. Lebigot Feb 01 '11 at 08:45