namespace hacking question

Discussion in 'Python' started by kj, Sep 30, 2010.

  1. kj

    kj Guest

    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
     
    kj, Sep 30, 2010
    #1
    1. Advertising

  2. kj

    Guest

    On 30 sep, 19: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)


    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.
     
    , Sep 30, 2010
    #2
    1. Advertising

  3. kj

    MRAB Guest

    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)
     
    MRAB, Sep 30, 2010
    #3
  4. MRAB <> 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
     
    Arnaud Delobelle, Sep 30, 2010
    #4
  5. kj

    alex23 Guest

    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.


    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

    >>> foo('spam')

    1
    >>> bar('spam')

    2
    >>>


    With a good editor, it should even take care of one of the quotes for
    you ;)
     
    alex23, Oct 1, 2010
    #5
  6. kj

    Fuzzyman Guest

    On Sep 30, 6:07 pm, 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)?
    >


    One way:

    import sys
    module = sys.modules[__name__]

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


    Michael Foord
    --
    http://www.voidspace.org.uk/
     
    Fuzzyman, Oct 1, 2010
    #6
  7. kj

    Guest

    On 1 oct, 14:12, Fuzzyman <> wrote:
    > On Sep 30, 6:07 pm, 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)?

    >
    > 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.
     
    , Oct 1, 2010
    #7
  8. In message <>, MRAB
    wrote:

    > 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.
     
    Lawrence D'Oliveiro, Oct 5, 2010
    #8
  9. kj

    Steve Howell Guest

    On Sep 30, 10:07 am, 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.
    >


    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.
     
    Steve Howell, Oct 5, 2010
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. RA
    Replies:
    12
    Views:
    1,536
    Kevin Spencer
    Aug 3, 2004
  2. paul Z

    hacking /dev/audio

    paul Z, Mar 7, 2004, in forum: C Programming
    Replies:
    2
    Views:
    392
    Malcolm
    Mar 7, 2004
  3. Hugh Macdonald

    Hacking the scope to pieces

    Hugh Macdonald, May 24, 2005, in forum: Python
    Replies:
    4
    Views:
    338
  4. Daniel Vallstrom
    Replies:
    7
    Views:
    380
    Daniel Vallstrom
    Oct 19, 2004
  5. e.expelliarmus
    Replies:
    1
    Views:
    277
    A. Sinan Unur
    Sep 25, 2007
Loading...

Share This Page