Eryk Sun <eryk...@gmail.com> added the comment:

> That's taken straight out of the documentation.

Yes, but it's still a misleading comparison.

> Until I understood that exec with two different mapping objects as 
> globals and locals behaves as if the code where embedded inside a 
> class, I found the reported behaviour totally perplexing.

The basic execution model of Python is that a frame that executes with 
non-optimized locals -- in module and class definitions -- can use the same 
mapping for globals and locals. Indeed, that's how the interpreter executes 
modules. However, exec() is generalized to allow executing module code with 
separate globals and locals. 

Saying that code will be "executed as if it were embedded in a class 
definition" is correct only so far as the fact that globals and locals are 
different in this case. But it's also misleading because the code gets compiled 
as module-level code, not as class code.

It should be pretty obvious why the following fails:

    exec("a = 1\ndef f(): return a\nprint(f())", {}, {})

Assignment is local by default, unless otherwise declared. Function f() has no 
access to the local scope where `a` is defined because Python doesn't support 
closures over non-optimized locals, particularly because we emphatically do not 
want that behavior for class definitions. 

It should be equally clear why the following succeeds:

    exec("global a\na = 1\ndef f(): return a\nprint(f())", {}, {})

> because a class definition intentionally supports nonlocal closures, 
>
>I don't know what you mean by that. Classes are never closures. Only 
>functions can be closures.

I didn't say that a class can be a closure. That's never the case because a 
class uses non-optimized locals. But a class definition does support free 
variables that are bound to an enclosing scope. exec() does not support this, 
so the exact same code can execute differently in the context of a class 
definition.

> It is equivalent to code executed inside a class scope.

That depends on the code and the context. Please refer to my first example in 
comparison to the following:

    a = 1
    def f():
        a = 2
        exec('print(a)', globals(), {})

    >>> f()
    1

It's different behavior for print(a) because both exec() and compile(source, 
filename, 'exec') produce module code, not class code. The free variable `a` 
gets bound to the global scope for the exec() example, while for the class 
definition free variable `a` is bound to the local `a` in the frame of the 
function call.

To implement this different behavior, the code object for a class definition 
uses bytecode operations such as COPY_FREE_VARS and LOAD_CLASSDEREF, which are 
never used for module-level code. For example, from the original example, 
here's the class definition code:

    >>> dis.dis(f.__code__.co_consts[2])
                  0 COPY_FREE_VARS           1
                  2 LOAD_NAME                0 (__name__)
                  4 STORE_NAME               1 (__module__)
                  6 LOAD_CONST               0 ('f.<locals>.C')
                  8 STORE_NAME               2 (__qualname__)
    
      4          10 LOAD_NAME                3 (print)
                 12 LOAD_CLASSDEREF          0 (a)
                 14 CALL_FUNCTION            1
                 16 POP_TOP
                 18 LOAD_CONST               1 (None)
                 20 RETURN_VALUE

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue46153>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to