namespace hacking question

K

kj

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)?

TIA!

kj
 
B

bruno.desthuilliers

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)

The local namespace is not implemented as a dict - locals() only
returns a dict representation of it, so updating this dict has no
effect on the local namespace. This is documented FWIW.
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".

eval only accepts expressions. You'd need exec here - but that's a bit
ugly.
 
M

MRAB

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)
 
A

Arnaud Delobelle

MRAB said:
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:
.... return "init " + name
.... .... for name in "bar", "baz":
.... exec "%s = init(name)" % name
.... print bar
.... print baz
.... init bar
init baz

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

alex23

kj said:
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.

Here's an approach that uses a decorator. It requires strings to be
passed, so it's not an exact fit for your requirement, but it's very
straightforward:

d = dict(
spam = 1,
ham = 2,
eggs = 3
)

def argdispatch(func):
def _wrapper(*args):
args = [d[k] for k in args if k in d]
return func(*args)
return _wrapper

@argdispatch
def foo(x):
print x

@argdispatch
def bar(x):
print x*2

With a good editor, it should even take care of one of the quotes for
you ;)
 
F

Fuzzyman

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)?

One way:

import sys
module = sys.modules[__name__]

for entry in ('spam', 'eggs', 'ham'):
setattr(module, entry, 'some value')


Michael Foord
 
B

bruno.desthuilliers

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)?

One way:

import sys
module = sys.modules[__name__]

for entry in ('spam', 'eggs', 'ham'):
    setattr(module, entry, 'some value')

Only works on globals - which you can already set using globals()
IIRC.
 
L

Lawrence D'Oliveiro

MRAB said:
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)

I’d add my vote to this. If you want to hack a namespace, make your own,
don’t try to mess with the unqualified local or global namespaces.

And as above, you can use a short prefix to save typing.
 
S

Steve Howell

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.

I'm curious what a typical use case for this is. It seems funny to
have variables that are initialized the same way, yet which are not in
a collection.
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'])

If you really want to get vars into the local namespace after the
initialization step, you can do something like this:

spam, ham, eggs = [init(v) for v in ['spam', 'ham', 'eggs']]

It's certainly awkward and brittle, but it lets you trade off re-
typing init() and d[] for another type of lexical duplication.

I wonder if you are either a) trying too hard to work around harmless
duplication or b) dealing with a duplication smell that actually runs
deeper than namespace hacking can fix.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top