exec and globals and locals ...

Peter Otten __peter__ at web.de
Thu Sep 19 09:33:44 EDT 2019


Richard Damon wrote:

> On 9/19/19 6:16 AM, Eko palypse wrote:
>>> In all cases, if the optional parts are omitted, the code is executed in
>>> the current scope. ...
>>>
>>>
>>> You can see from it that "globals" is optional.
>>> And that, if "globals" is missing, then
>>> "exec" is executed in the current scope ("f1" in your case).
>> Thank you for your answer, and that is exactly what confuses me?
>> Where does x come from? If I only would read x then I would understand
>> why it can be found/read but I alter it and as such I either have to
>> provide the info that this is a global variable, declare it inside of f1
>> or provide the globals dict to exec. But I don't do any of it. Why is
>> exec able to use the global x?
>>
>> Eren
> 
> I think the issue is that x += 1 isn't exactly like x = x + 1, and this
> is one case that shows it. x = x + 1 is an assignment to the symbol x,
> which makes x a local, and thus the read becomes an undefined symbol. x
> += 1 is different, it isn't a plain assignment so doesn't create the
> local. The read of x is inherently tied to the writing of x so x stays
> referring to the global.

I think you are wrong; both x += 1 and x = x + 1 turn x into a local 
variable: 

>>> def f():
...    x += 1
... 
>>> x = 42
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
>>> def g():
...     x = x + 1
... 
>>> g()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in g
UnboundLocalError: local variable 'x' referenced before assignment

The difference is that

x = x + 1

is evaluated as

x = x.__add__(1)

while x += 1 becomes

x = x.__iadd__(1)

For mutable x this allows modifying x in place with consequences likely to 
surprise when you see it for the first time:

>>> t = ["Nobody expects "],
>>> t[0] = t[0] + ["the Spanish inquisition"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t
(['Nobody expects '],)

The above creates a new list which cannot become the first item of the 
(immutable) tuple.

>>> t[0] += ["the Spanish inquisition"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

The above changes the first item of the tuple in place, but afterwards the 
attempt to rebind t[0] still fails.




More information about the Python-list mailing list