modifying locals

Discussion in 'Python' started by John [H2O], Oct 30, 2008.

  1. John [H2O]

    John [H2O] Guest

    I would like to write a function to write variables to a file and modify a
    few 'counters'. This is to replace multiple instances of identical code in a
    module I am writing.

    This is my approach:

    def write_vars(D):
    """ pass D=locals() to this function... """
    for key in D.keys():
    exec("%s = %s" % (key,D[key]))

    outfile.write(...)
    numcount += 1
    do this, do that...

    the issue is that at the end, I want to return outfile, numcount, etc... but
    I would prefer to not return them explicitly, that is, I would just like
    that the modified values are reflected in the script. How do I do this?
    Using global? But that seems a bit dangerous since I am using exec.

    Bringing up another matter... is there a better way to do this that doesn't
    use exec?

    Thanks!

    --
    View this message in context: http://www.nabble.com/modifying-locals-tp20255725p20255725.html
    Sent from the Python - python-list mailing list archive at Nabble.com.
     
    John [H2O], Oct 30, 2008
    #1
    1. Advertising

  2. On Thu, 30 Oct 2008 14:21:01 -0700, John [H2O] wrote:

    > I would like to write a function to write variables to a file and modify
    > a few 'counters'.


    Are you talking about a function to generate Python source code?


    > This is to replace multiple instances of identical
    > code in a module I am writing.



    Surely the right way to do this is to factor out the identical code into
    a function, and then call the function.


    > This is my approach:
    >
    > def write_vars(D):
    > """ pass D=locals() to this function... """
    > for key in D.keys():
    > exec("%s = %s" % (key,D[key]))


    That would be better written as:

    for key,item in D.iteritems():
    exec "%s = %s" % (key, item)

    exec is a statement, not a function, and doesn't require brackets.


    > outfile.write(...)
    > numcount += 1
    > do this, do that...
    >
    > the issue is that at the end, I want to return outfile, numcount, etc...
    > but I would prefer to not return them explicitly, that is, I would just
    > like that the modified values are reflected in the script. How do I do
    > this? Using global? But that seems a bit dangerous since I am using
    > exec.


    What you are actually trying to do is unclear to me. Perhaps you could
    try explaining better with a more concrete example?

    I wounder whether this might be what you are after?

    # start of script (untested)
    counter = 0 # define a counter in the module scope (global)
    filename = 'foo.txt'

    def make_vars():
    global outfile, numcount # force names to be in module scope
    outfile = open(filename, 'w')
    numcount = 99

    try:
    numcount
    except NameError:
    print "numcount doesn't exist yet, making it"
    make_vars()

    print numcount
    # end script


    But of course the above can be written much more concisely as:

    # start of script (untested)
    counter = 0
    filename = 'foo.txt'
    outfile = open(filename, 'w')
    numcount = 99
    print numcount
    # end script

    so I'm not really sure you're trying to do what you seem to be doing.




    --
    Steven
     
    Steven D'Aprano, Oct 30, 2008
    #2
    1. Advertising

  3. John [H2O]

    John [H2O] Guest

    Steven D'Aprano-7 wrote:
    >
    > What you are actually trying to do is unclear to me. Perhaps you could
    > try explaining better with a more concrete example?
    >
    > --
    > Steven
    > --
    >


    Actually, maybe a LACK of an example would make it simpler. What I'm after
    is a function, to which I can pass a dictionary defined from locals(), then
    in the function I would modify some of the variables from the dictionary.
    But I want the modifications to be 'seen' by the method that called the
    function without passing them back via return.

    Ideally, I would prefer not to use global, as I think (due to other problem
    in my scripting) this might cause problems.

    Currently I these two possibilities:

    def myFunction(D):
    for key,item in D.iteritems():
    exec "%s = %s" % (key, item)

    modify a few of the elements...
    return locals()

    then,
    #script
    D=locals()
    D=myFunction(D)
    for key,item in D.iteritems():
    exec "%s = %s" % (key,item)


    OR:

    def myFunction(D):
    for key,item in D.iteritems():
    exec "%s = %s" % (key, item)

    modify a few of the elements...
    declare global on the elements modified...


    then,
    #script
    D=locals()
    myFunction(D)

    As a point.. I thought I read somewhere that D.iteritems wasn't going to be
    available in Python3 so I've been trying to 'ween' myself from it.

    Thanks!
    --
    View this message in context: http://www.nabble.com/modifying-locals-tp20255725p20257394.html
    Sent from the Python - python-list mailing list archive at Nabble.com.
     
    John [H2O], Oct 30, 2008
    #3
  4. On Thu, 30 Oct 2008 16:19:11 -0700, John [H2O] wrote:

    > Steven D'Aprano-7 wrote:
    >>
    >> What you are actually trying to do is unclear to me. Perhaps you could
    >> try explaining better with a more concrete example?
    >>

    > Actually, maybe a LACK of an example would make it simpler. What I'm
    > after is a function, to which I can pass a dictionary defined from
    > locals(), then in the function I would modify some of the variables from
    > the dictionary. But I want the modifications to be 'seen' by the method
    > that called the function without passing them back via return.


    Why do you want that? That is typically something you don't want,
    because it can make the program hard to understand very easily. Python
    has no dynamic scoping and if you "hack" this into you program the
    function call has very unexpected and surprising side effects.

    So I ask for the use case too. What problem are you trying to solve?
    There might be a better way than executing strings as code and trying to
    inject names into the callers namespace.

    Ciao,
    Marc 'BlackJack' Rintsch
     
    Marc 'BlackJack' Rintsch, Oct 31, 2008
    #4
  5. On Fri, 31 Oct 2008 07:10:05 +0100, Tino Wildenhain wrote:

    > Also, locals() already returns a dict, no need for the exec trickery.
    > You can just modify it:
    >
    > >>> locals()["foo"]="bar"
    > >>> foo

    > 'bar'
    >


    That is incorrect. People often try modifying locals() in the global
    scope, and then get bitten when it doesn't work in a function or class.


    >>> def foo():

    .... x = 1
    .... locals()['y'] = 2
    .... y
    ....
    >>> foo()

    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in foo
    NameError: global name 'y' is not defined

    You cannot modify locals() and have it work. The fact that it happens to
    work when locals() == globals() is probably an accident.



    --
    Steven
     
    Steven D'Aprano, Oct 31, 2008
    #5
  6. On Oct 30, 9:21 pm, "John [H2O]" <> wrote:
    > I would like to write a function to write variables to a file and modify a
    > few 'counters'. This is to replace multiple instances of identical code in a
    > module I am writing.
    >
    > This is my approach:
    >
    > def write_vars(D):
    >     """ pass D=locals() to this function... """
    >     for key in D.keys():
    >         exec("%s = %s" % (key,D[key]))
    >
    >     outfile.write(...)
    >     numcount += 1
    >     do this, do that...
    >
    > the issue is that at the end, I want to return outfile, numcount, etc... but
    > I would prefer to not return them explicitly, that is, I would just like
    > that the modified values are reflected in the script. How do I do this?
    > Using global? But that seems a bit dangerous since I am using exec.
    >
    > Bringing up another matter... is there a better way to do this that doesn't
    > use exec?


    What you're trying to do is hard to achieve but there's a reason for
    that: it's a bad idea as it makes code really difficult to maintain.

    You may want to define a class to contain all those variables that
    need to be changed together:

    class MyVars(object):
    def __init__(self, outfile, numcount, ...):
    self.outfile = outfile
    self.numcount = numcount
    def write_and_do_stuff(self):
    self.outfile.write(...)
    self.numcount += 1
    # do this, do that...

    Then you can use this in your functions:

    def myfunction():
    vars = MyVars(open('my.log', w), 0, ...)
    # Do some stuff
    vars.write_and_do_stuff()
    # Do more stuff

    You may even consider making some of those functions into methods of
    the class.

    --
    Arnaud
     
    Arnaud Delobelle, Oct 31, 2008
    #6
  7. On 2008-10-31 09:08, Tino Wildenhain wrote:
    > Hi,
    >
    > Steven D'Aprano wrote:
    >> On Fri, 31 Oct 2008 07:10:05 +0100, Tino Wildenhain wrote:
    >>
    >>> Also, locals() already returns a dict, no need for the exec trickery.
    >>> You can just modify it:
    >>>
    >>> >>> locals()["foo"]="bar"
    >>> >>> foo
    >>> 'bar'
    >>>

    >>
    >> That is incorrect. People often try modifying locals() in the global
    >> scope, and then get bitten when it doesn't work in a function or class.

    >
    >>
    >>>>> def foo():

    >> ... x = 1
    >> ... locals()['y'] = 2
    >> ... y
    >> ...
    >>>>> foo()

    >> Traceback (most recent call last):
    >> File "<stdin>", line 1, in <module>
    >> File "<stdin>", line 4, in foo
    >> NameError: global name 'y' is not defined
    >>
    >> You cannot modify locals() and have it work. The fact that it happens
    >> to work when locals() == globals() is probably an accident.

    >
    > Ah thats interesting. I would not know because I usually avoid
    > such ugly hacks :)


    It doesn't even work for already defined local variables:

    >>> def foo():

    .... x = 1
    .... locals()['x'] = 2
    .... print x
    ....
    >>> foo()

    1

    The reason is that locals are copied in to a C array
    when entering a function. Manipulations are then
    done using the LOAD_FAST, STORE_FAST VM opcodes.

    The locals() dictionary only shadows these locals: it copies
    the current values from the C array into the frame's
    f_locals dictionary and then returns the dictionary.

    This also works the other way around, but only in very
    cases:

    * when running "from xyz import *"
    * when running code using "exec"

    globals() on the other hand usually refers to a module
    namespace dictionary, for which there are no such
    optimizations..

    I don't know of any way to insert locals modified in
    a calling stack frame... but then again: why would you
    want to do this anyway ?

    --
    Marc-Andre Lemburg
    eGenix.com

    Professional Python Services directly from the Source (#1, Oct 31 2008)
    >>> Python/Zope Consulting and Support ... http://www.egenix.com/
    >>> mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/
    >>> mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/

    ________________________________________________________________________

    :::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! ::::


    eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48
    D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg
    Registered at Amtsgericht Duesseldorf: HRB 46611
     
    M.-A. Lemburg, Oct 31, 2008
    #7
    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. Aaron Ackerman
    Replies:
    5
    Views:
    432
    Chris Dunaway
    Oct 13, 2003
  2. dgk
    Replies:
    2
    Views:
    428
  3. Giles Brown

    Question about "exec in globals, locals"

    Giles Brown, Jul 4, 2003, in forum: Python
    Replies:
    2
    Views:
    380
    Adrien Di Mascio
    Jul 4, 2003
  4. Paul Paterson

    How safe is modifying locals()?

    Paul Paterson, Jul 25, 2003, in forum: Python
    Replies:
    15
    Views:
    512
    Corey Coughlin
    Jul 28, 2003
  5. Ed Anuff
    Replies:
    2
    Views:
    218
    Ed Anuff
    Sep 14, 2009
Loading...

Share This Page