Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #38744 > unrolled thread

Python3 exec locals - this must be a FAQ

Started byHelmut Jarausch <jarausch@igpm.rwth-aachen.de>
First post2013-02-12 11:46 +0000
Last post2013-02-12 11:40 -0500
Articles 8 — 6 participants

Back to article view | Back to comp.lang.python


Contents

  Python3 exec locals - this must be a FAQ Helmut Jarausch <jarausch@igpm.rwth-aachen.de> - 2013-02-12 11:46 +0000
    Re: Python3 exec locals - this must be a FAQ Dave Angel <davea@davea.name> - 2013-02-12 08:27 -0500
      Re: Python3 exec locals - this must be a FAQ Helmut Jarausch <jarausch@skynet.be> - 2013-02-12 14:29 +0000
        Re: Python3 exec locals - this must be a FAQ Dave Angel <davea@davea.name> - 2013-02-12 10:40 -0500
          Re: Python3 exec locals - this must be a FAQ Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-02-13 11:05 +1100
    Re: Python3 exec locals - this must be a FAQ MRAB <python@mrabarnett.plus.com> - 2013-02-12 15:15 +0000
    Re: Python3 exec locals - this must be a FAQ Terry Reedy <tjreedy@udel.edu> - 2013-02-12 10:50 -0500
    Re: Python3 exec locals - this must be a FAQ Dave Angel <davea@davea.name> - 2013-02-12 11:40 -0500

#38744 — Python3 exec locals - this must be a FAQ

FromHelmut Jarausch <jarausch@igpm.rwth-aachen.de>
Date2013-02-12 11:46 +0000
SubjectPython3 exec locals - this must be a FAQ
Message-ID<anuoc6F8kvaU1@mid.dfncis.de>
Hi,

I've tried but didn't find an answer on the net.

The exec function in Python modifies a copy of locals() only.
How can I transfer changes in that local copy to the locals of my function
** without **  knowing the names of these variables.

E.g.  I have a lot of local names.

Doing

_locals= locals()
expr=compile(input('statements assigning to some local variables '),
                                                 'user input','exec')
exec(expr,globals(),_locals)

How can I "copy" the new values within _locals to my current locals.

If I knew that  Var1  has changed I could say
Var1 = _locals['Var1'] but what to do in general?

Many thanks for a hint,
Helmut.

[toc] | [next] | [standalone]


#38748

FromDave Angel <davea@davea.name>
Date2013-02-12 08:27 -0500
Message-ID<mailman.1699.1360675679.2939.python-list@python.org>
In reply to#38744
On 02/12/2013 06:46 AM, Helmut Jarausch wrote:
> Hi,
>
> I've tried but didn't find an answer on the net.
>
> The exec function in Python modifies a copy of locals() only.
> How can I transfer changes in that local copy to the locals of my function
> ** without **  knowing the names of these variables.
>
> E.g.  I have a lot of local names.
>
> Doing
>
> _locals= locals()

This doesn't copy everything.  But perhaps you know that and you're just 
testing us.

> expr=compile(input('statements assigning to some local variables '),
>                                                   'user input','exec')
> exec(expr,globals(),_locals)
>
> How can I "copy" the new values within _locals to my current locals.
>
> If I knew that  Var1  has changed I could say
> Var1 = _locals['Var1'] but what to do in general?

locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.

So you might write a loop on _locals.

But beware if someone has deleted one of the "variables" it may not do 
what you'd like.  You cannot necessarily add back a local with the above 
syntax.


-- 
DaveA

[toc] | [prev] | [next] | [standalone]


#38750

FromHelmut Jarausch <jarausch@skynet.be>
Date2013-02-12 14:29 +0000
Message-ID<511a51b3$0$3111$ba620e4c@news.skynet.be>
In reply to#38748
On Tue, 12 Feb 2013 08:27:41 -0500, Dave Angel wrote:

> On 02/12/2013 06:46 AM, Helmut Jarausch wrote:
>> Hi,
>>
>> I've tried but didn't find an answer on the net.
>>
>> The exec function in Python modifies a copy of locals() only.
>> How can I transfer changes in that local copy to the locals of my
>> function ** without **  knowing the names of these variables.
>>
>> E.g.  I have a lot of local names.
>>
>> Doing
>>
>> _locals= locals()
> 
> This doesn't copy everything.  But perhaps you know that and you're just
> testing us.

No, I didn't know. And I'm bit surprised since this is recommend
several times, e.g. in "Python Essential Reference, 4th ed" by
David Beazley.

> 
>> expr=compile(input('statements assigning to some local variables '),
>>                                                   'user input','exec')
>> exec(expr,globals(),_locals)
>>
>> How can I "copy" the new values within _locals to my current locals.
>>
>> If I knew that  Var1  has changed I could say Var1 = _locals['Var1']
>> but what to do in general?
> 
> locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.

Thanks for this hint which surprises me again since I thought
locals() by itself is a copy only.

> 
> So you might write a loop on _locals.
> 
> But beware if someone has deleted one of the "variables" it may not do
> what you'd like.  You cannot necessarily add back a local with the above
> syntax.

Does this mean that adding something completely new won't work?

Many thanks,
Helmut.

[toc] | [prev] | [next] | [standalone]


#38762

FromDave Angel <davea@davea.name>
Date2013-02-12 10:40 -0500
Message-ID<mailman.1707.1360683643.2939.python-list@python.org>
In reply to#38750
On 02/12/2013 09:29 AM, Helmut Jarausch wrote:
> On Tue, 12 Feb 2013 08:27:41 -0500, Dave Angel wrote:
>
>> On 02/12/2013 06:46 AM, Helmut Jarausch wrote:
>>> Hi,
>>>
>>> I've tried but didn't find an answer on the net.
>>>
>>> The exec function in Python modifies a copy of locals() only.
>>> How can I transfer changes in that local copy to the locals of my
>>> function ** without **  knowing the names of these variables.
>>>
>>> E.g.  I have a lot of local names.
>>>
>>> Doing
>>>
>>> _locals= locals()
>>
>> This doesn't copy everything.  But perhaps you know that and you're just
>> testing us.
>
> No, I didn't know. And I'm bit surprised since this is recommend
> several times, e.g. in "Python Essential Reference, 4th ed" by
> David Beazley.
>

That assignment is useful, because it presumably saves the time that 
calling locals() will take constructing the dict.  But copying anything 
by assignment just makes a new reference to the same thing.  If that 
thing is mutable, as a dict is, then changes made to the (dict) object 
are visible to both.


>>
>>> expr=compile(input('statements assigning to some local variables '),
>>>                                                    'user input','exec')
>>> exec(expr,globals(),_locals)
>>>
>>> How can I "copy" the new values within _locals to my current locals.
>>>
>>> If I knew that  Var1  has changed I could say Var1 = _locals['Var1']
>>> but what to do in general?
>>
>> locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.
>
> Thanks for this hint which surprises me again since I thought
> locals() by itself is a copy only.
>

(Thanks MRAB for your correction.)

As MRAB points out, I was in error on this point.  I only tested it in 
global scope.  Inside a function it doesn't seem to work.  See docs below.

>>
>> So you might write a loop on _locals.
>>
>> But beware if someone has deleted one of the "variables" it may not do
>> what you'd like.  You cannot necessarily add back a local with the above
>> syntax.
>
> Does this mean that adding something completely new won't work?
>
> Many thanks,
> Helmut.
>

That depends.  All I can say for sure is it won't work for CPython to 
create new local variables inside a function that way.  It seems to work 
for globals (which are also locals when code of global scope is using 
it), but I wouldn't count on it.

http://docs.python.org/2/library/functions.html#locals

Note the sentence:

"""The contents of this dictionary should not be modified; changes may 
not affect the values of local and free variables used by the interpreter"""

Notice that adding or removing items from a dictionary is modifying it, 
while changing values that the keys are associated with is not.  But 
apparently the documentation meant it more strictly than it was worded.

If you really have dozens of "variables" that you want to pass to exec, 
then restore their original values after it returns, I suggest you make 
your own (empty) class, and use that as a namespace to accomplish it. 
The reason I was tripped up on this definition is that I've concluded 
long ago that messing with locals() is a losing game, so I'd forgotten 
some of the subtlety.

If we knew what the real problem was, we might have a suggestion.  For 
example, if the intent is for the exec to work with a copy of the 
variables, without affecting the originals, then why not use the copy 
module, and pass the *copy* to the exec logic.


-- 
DaveA

[toc] | [prev] | [next] | [standalone]


#38790

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-02-13 11:05 +1100
Message-ID<511ad8b8$0$29972$c3e8da3$5496439d@news.astraweb.com>
In reply to#38762
Dave Angel wrote:

>> Thanks for this hint which surprises me again since I thought
>> locals() by itself is a copy only.
>>
> 
> (Thanks MRAB for your correction.)
> 
> As MRAB points out, I was in error on this point.  I only tested it in
> global scope.  Inside a function it doesn't seem to work.

One of the two classic blunders:

- Never get involved in a land war in Asia;
- Never go against a Sicilian when death is on the line;
- Never test locals() outside of a function and extrapolate the 
  behaviour to inside a function!



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#38756

FromMRAB <python@mrabarnett.plus.com>
Date2013-02-12 15:15 +0000
Message-ID<mailman.1702.1360682099.2939.python-list@python.org>
In reply to#38744
On 2013-02-12 13:27, Dave Angel wrote:
> On 02/12/2013 06:46 AM, Helmut Jarausch wrote:
>> Hi,
>>
>> I've tried but didn't find an answer on the net.
>>
>> The exec function in Python modifies a copy of locals() only.
>> How can I transfer changes in that local copy to the locals of my function
>> ** without **  knowing the names of these variables.
>>
>> E.g.  I have a lot of local names.
>>
>> Doing
>>
>> _locals= locals()
>
> This doesn't copy everything.  But perhaps you know that and you're just
> testing us.
>
>> expr=compile(input('statements assigning to some local variables '),
>>                                                   'user input','exec')
>> exec(expr,globals(),_locals)
>>
>> How can I "copy" the new values within _locals to my current locals.
>>
>> If I knew that  Var1  has changed I could say
>> Var1 = _locals['Var1'] but what to do in general?
>
> locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.
>
> So you might write a loop on _locals.
>
> But beware if someone has deleted one of the "variables" it may not do
> what you'd like.  You cannot necessarily add back a local with the above
> syntax.
>
The docs for locals() warns """The contents of this dictionary should
not be modified; changes may not affect the values of local and free
variables used by the interpreter."""

That's because local variables in a function are implemented (at least
in CPython) using 'slots' for efficiency reasons.

 >>> def example():
     a = 1
     print("a is {}, locals() is {}".format(a, locals()))
     print("Changing a to 2 directly")
     a = 2
     print("a is {}, locals() is {}".format(a, locals()))
     print("Changing a to 3 via locals()")
     locals()["a"] = 3
     print("a is {}, locals() is {}".format(a, locals()))
     locals()["b"] = 0
     print("locals() is {}".format(locals()))
     print("b is {}".format(b))

 >>> example()
a is 1, locals() is {'a': 1}
Changing a to 2 directly
a is 2, locals() is {'a': 2}
Changing a to 3 via locals()
a is 2, locals() is {'a': 2}
locals() is {'b': 0, 'a': 2}
Traceback (most recent call last):
   File "<pyshell#39>", line 1, in <module>
     example()
   File "<pyshell#38>", line 12, in example
     print("b is {}".format(b))
NameError: global name 'b' is not defined

Note how attempting to change variable "a" in locals() is ignored, and
how adding variable "b" to locals() doesn't actually make it a local
variable (it raises a NameError).

[toc] | [prev] | [next] | [standalone]


#38765

FromTerry Reedy <tjreedy@udel.edu>
Date2013-02-12 10:50 -0500
Message-ID<mailman.1710.1360684232.2939.python-list@python.org>
In reply to#38744
On 2/12/2013 8:27 AM, Dave Angel wrote:
> On 02/12/2013 06:46 AM, Helmut Jarausch wrote:

>> I've tried but didn't find an answer on the net.

You should start with the fine manual, which is on the net as well as 
included with at least the Windows distribution. It has a nice index 
that includes an entry for locals (built-in function).

>> The exec function in Python modifies a copy of locals() only.
>> How can I transfer changes in that local copy to the locals of my
>> function
>> ** without **  knowing the names of these variables.

You cannot. Just accept that.

>> E.g.  I have a lot of local names.
>>
>> Doing
>>
>> _locals= locals()

This merely gives you a handle of the dict returned by locals() for when 
you want to do more than just pass it to (for example) exec). This is 
unusual because it is not very useful.

> This doesn't copy everything.

I have no idea what you mean. The locals() dict contains all local and 
nonlocal names, excluding global names, which are in the globals() dict.

>> expr=compile(input('statements assigning to some local variables '),
>>                                                   'user input','exec')
>> exec(expr,globals(),_locals)
>>
>> How can I "copy" the new values within _locals to my current locals.
>>
>> If I knew that  Var1  has changed I could say
>> Var1 = _locals['Var1'] but what to do in general?

If you want to put a value back into the function local namespace, this 
is the only thing you can do. In CPython, at least, all function local 
names must be explicit and known when the function statement is executed 
and the function object is created. Read the Library manual entry for 
locals(), including the highlighted note.

> locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.

Huh??? The dict returned by this locals call is the same dict returned 
by the previous locals call and bound to _locas. So the above does 
nothing. It is the same thing as _locals["Var1"] = _locals["Var1"].

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#38770

FromDave Angel <davea@davea.name>
Date2013-02-12 11:40 -0500
Message-ID<mailman.1713.1360687277.2939.python-list@python.org>
In reply to#38744
On 02/12/2013 10:50 AM, Terry Reedy wrote:
> On 2/12/2013 8:27 AM, Dave Angel wrote:
>> On 02/12/2013 06:46 AM, Helmut Jarausch wrote:
>
>>> <snip>
>>>
>>> Doing
>>>
>>> _locals= locals()
>
> This merely gives you a handle of the dict returned by locals() for when
> you want to do more than just pass it to (for example) exec). This is
> unusual because it is not very useful.
>
>> This doesn't copy everything.

The OP presumably wanted to restore the original values of the original 
variables.  The above "assignment" won't help a bit with that.


>
> I have no idea what you mean. The locals() dict contains all local and
> nonlocal names, excluding global names, which are in the globals() dict.
>
>>> expr=compile(input('statements assigning to some local variables '),
>>>                                                   'user input','exec')
>>> exec(expr,globals(),_locals)
>>>
>>> How can I "copy" the new values within _locals to my current locals.
>>>
>>> If I knew that  Var1  has changed I could say
>>> Var1 = _locals['Var1'] but what to do in general?
>
> If you want to put a value back into the function local namespace, this
> is the only thing you can do. In CPython, at least, all function local
> names must be explicit and known when the function statement is executed
> and the function object is created. Read the Library manual entry for
> locals(), including the highlighted note.
>
>> locals()["Var1"] = _locals["Var1"]  will set the same Var1 local.
>
> Huh??? The dict returned by this locals call is the same dict returned
> by the previous locals call and bound to _locas. So the above does
> nothing. It is the same thing as _locals["Var1"] = _locals["Var1"].
>

My claim was based on the assumption that the earlier assignment had 
been fixed by some kind of copy.  If not, there's nothing to restore.

I also retracted my use of that trick anyway, since being corrected by 
MRAB.  I only tested it in top-level code, not inside a function.


-- 
DaveA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web