2

Can someone please explain to me when and how I would use the closure parameter of the exec function?

https://docs.python.org/3/library/functions.html#exec

The closure argument specifies a closure–a tuple of cellvars. It’s only valid when the object is a code object containing free variables. The length of the tuple must exactly match the number of free variables referenced by the code object.

Ben Ellis
  • 163
  • 1
  • 8
  • https://github.com/python/cpython/issues/92203 makes an oblique reference to using it for [PEP 649](https://peps.python.org/pep-0649/) – Barmar Apr 05 '23 at 15:09
  • Check out https://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python. `i` in this answer https://stackoverflow.com/a/141426/218663 would be an example. – JonSG Apr 05 '23 at 15:16
  • `cellvars` are buried deep in [the implementation](https://docs.python.org/3/c-api/code.html#c.PyCodeObject). It seems somewhat unusual that the documentation should mention "cellvars" when this isn't a thing you would typically be aware of – roganjosh Apr 05 '23 at 15:29

1 Answers1

1

It is not intended to be used when the object to be exec-ed is a string - only when it is a code object.

It happens that when one defines a function in Python, it becomes an object tthat references a code object, and some meta information that code needs to run: the globals namespace and locals namespaces for its variables - but also, if that function references any variable in an enclosing function, that is a "nonlocal" variable - that gets annotated in the __closure__ attribute of a function. When the associated code is executed, it has access to it.

If there was no way to pass a __closure__ to the exec function, any code that would refer to non-local variables simply would not be able to run.

It is possible to create a closure from "scratch", as it is simply a tuple of "cells" - each cell is a somewhat opaque handler to a Python value, and can be created with types.CellType

Now, onto the example:


In [61]: def a():
    ...:     b = 1
    ...:     def c():
    ...:         nonlocal b
    ...:         print(b)
    ...:     return c
    ...: 

In [62]: inner_func = a()

In [63]: import types

In [68]: cell = types.CellType(42)

In [69]: exec(inner_func.__code__, closure=(cell,))
42
jsbueno
  • 99,910
  • 10
  • 151
  • 209