This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, qpeter, steven.daprano
Date 2021-12-23.05:47:33
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1640238453.32.0.417281119585.issue46153@roundup.psfhosted.org>
In-reply-to
Content
> 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
History
Date User Action Args
2021-12-23 05:47:33eryksunsetrecipients: + eryksun, steven.daprano, qpeter
2021-12-23 05:47:33eryksunsetmessageid: <1640238453.32.0.417281119585.issue46153@roundup.psfhosted.org>
2021-12-23 05:47:33eryksunlinkissue46153 messages
2021-12-23 05:47:33eryksuncreate