namespace hacking question

Arnaud Delobelle arnodel at gmail.com
Thu Sep 30 14:41:59 EDT 2010


MRAB <python at mrabarnett.plus.com> writes:

> On 30/09/2010 18:07, kj wrote:
>>
>>
>>
>> This is a recurrent situation: I want to initialize a whole bunch
>> of local variables in a uniform way, but after initialization, I
>> need to do different things with the various variables.
>>
>> What I end up doing is using a dict:
>>
>> d = dict()
>> for v in ('spam', 'ham', 'eggs'):
>>      d[v] = init(v)
>>
>> foo(d['spam'])
>> bar(d['ham'])
>> baz(d['eggs'])
>>
>>
>>
>> This is fine, but I'd like to get rid of the tedium of typing all
>> those extra d['...']s.
>>
>> I.e., what I would *like* to do is something closer to this:
>>
>> d = locals()
>> for v in ('spam', 'ham', 'eggs'):
>>      d[v] = init(v)
>>
>> foo(spam)
>> bar(ham)
>> baz(eggs)
>>
>> ...but this results in errors like "NameError: global name 'spam' is
>> not defined".
>>
>> But the problem is deeper than the fact that the error above would
>> suggest, because even this fails:
>>
>> spam = ham = eggs = None
>> d = locals()
>> for v in ('spam', 'ham', 'eggs'):
>>      d[v] = init(v)
>>
>> foo(spam) # calls foo(None)
>> bar(ham)  # calls bar(None)
>> baz(eggs) # calls baz(None)
>>
>>
>> In other words, setting the value of locals()['x'] does not set
>> the value of the local variable x.
>>
>> I also tried a hack using eval:
>>
>> for v in ('spam', 'ham', 'eggs'):
>>      eval "%s = init('%s')" % (v, v)
>>
>> but the "=" sign in the eval string resulted in a "SyntaxError:
>> invalid syntax".
>>
>> Is there any way to use a loop to set a whole bunch of local
>> variables (and later refer to these variables by their individual
>> names)?
>>
> The handling of local variables in CPython is optimised, so changing
> locals() won't have any effect, as you discovered.
>
> An alternative is to create a namespace in an instance of a class and
> then add attributes to it:
>
> class Namespace(object):
>     pass
>
> n = Namespace()
> for v in ('spam', 'ham', 'eggs'):
>     setattr(n, v, init(v))
>
> foo(n.spam)
> bar(n.ham)
> baz(n.eggs)

Note that "exec" can be used:

>>> def init(name):
...     return "init " + name
... 
>>> def foo():
...     for name in "bar", "baz":
...         exec "%s = init(name)" % name
...     print bar
...     print baz
... 
>>> foo()
init bar
init baz

Not that I can think of a reason to do this :)

-- 
Arnaud



More information about the Python-list mailing list