gh-70870: Clarify dual usage of 'free variable' (#122545)

The term "free variable" has unfortunately become genuinely
ambiguous over the years (presumably due to the names of
some relevant code object instance attributes).

While we can't eliminate that ambiguity at this late date, we can
at least alert people to the potential ambiguity by describing
both the formal meaning of the term and the common
alternative use as a direct synonym for "closure variable".

---------

Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
This commit is contained in:
Alyssa Coghlan 2024-10-08 17:52:12 +10:00 committed by GitHub
parent cc9b9bebb2
commit 27390990fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 66 additions and 21 deletions

View File

@ -32,11 +32,13 @@ bound into a function.
.. c:function:: Py_ssize_t PyCode_GetNumFree(PyCodeObject *co) .. c:function:: Py_ssize_t PyCode_GetNumFree(PyCodeObject *co)
Return the number of free variables in a code object. Return the number of :term:`free (closure) variables <closure variable>`
in a code object.
.. c:function:: int PyUnstable_Code_GetFirstFree(PyCodeObject *co) .. c:function:: int PyUnstable_Code_GetFirstFree(PyCodeObject *co)
Return the position of the first free variable in a code object. Return the position of the first :term:`free (closure) variable <closure variable>`
in a code object.
.. versionchanged:: 3.13 .. versionchanged:: 3.13
@ -144,7 +146,8 @@ bound into a function.
Equivalent to the Python code ``getattr(co, 'co_freevars')``. Equivalent to the Python code ``getattr(co, 'co_freevars')``.
Returns a new reference to a :c:type:`PyTupleObject` containing the names of Returns a new reference to a :c:type:`PyTupleObject` containing the names of
the free variables. On error, ``NULL`` is returned and an exception is raised. the :term:`free (closure) variables <closure variable>`. On error, ``NULL`` is returned
and an exception is raised.
.. versionadded:: 3.11 .. versionadded:: 3.11

View File

@ -231,6 +231,28 @@ Glossary
A variable defined in a class and intended to be modified only at A variable defined in a class and intended to be modified only at
class level (i.e., not in an instance of the class). class level (i.e., not in an instance of the class).
closure variable
A :term:`free variable` referenced from a :term:`nested scope` that is defined in an outer
scope rather than being resolved at runtime from the globals or builtin namespaces.
May be explicitly defined with the :keyword:`nonlocal` keyword to allow write access,
or implicitly defined if the variable is only being read.
For example, in the ``inner`` function in the following code, both ``x`` and ``print`` are
:term:`free variables <free variable>`, but only ``x`` is a *closure variable*::
def outer():
x = 0
def inner():
nonlocal x
x += 1
print(x)
return inner
Due to the :attr:`codeobject.co_freevars` attribute (which, despite its name, only
includes the names of closure variables rather than listing all referenced free
variables), the more general :term:`free variable` term is sometimes used even
when the intended meaning is to refer specifically to closure variables.
complex number complex number
An extension of the familiar real number system in which all numbers are An extension of the familiar real number system in which all numbers are
expressed as a sum of a real part and an imaginary part. Imaginary expressed as a sum of a real part and an imaginary part. Imaginary
@ -454,6 +476,13 @@ Glossary
the :term:`global interpreter lock` which allows only one thread to the :term:`global interpreter lock` which allows only one thread to
execute Python bytecode at a time. See :pep:`703`. execute Python bytecode at a time. See :pep:`703`.
free variable
Formally, as defined in the :ref:`language execution model <bind_names>`, a free
variable is any variable used in a namespace which is not a local variable in that
namespace. See :term:`closure variable` for an example.
Pragmatically, due to the name of the :attr:`codeobject.co_freevars` attribute,
the term is also sometimes used as a synonym for :term:`closure variable`.
function function
A series of statements which returns some value to a caller. It can also A series of statements which returns some value to a caller. It can also
be passed zero or more :term:`arguments <argument>` which may be used in be passed zero or more :term:`arguments <argument>` which may be used in

View File

@ -1434,7 +1434,7 @@ iterations of the loop.
slot ``i`` of the "fast locals" storage in this mapping. slot ``i`` of the "fast locals" storage in this mapping.
If the name is not found there, loads it from the cell contained in If the name is not found there, loads it from the cell contained in
slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading
free variables in class bodies (which previously used :term:`closure variables <closure variable>` in class bodies (which previously used
:opcode:`!LOAD_CLASSDEREF`) and in :opcode:`!LOAD_CLASSDEREF`) and in
:ref:`annotation scopes <annotation-scopes>` within class bodies. :ref:`annotation scopes <annotation-scopes>` within class bodies.
@ -1463,8 +1463,8 @@ iterations of the loop.
.. opcode:: COPY_FREE_VARS (n) .. opcode:: COPY_FREE_VARS (n)
Copies the ``n`` free variables from the closure into the frame. Copies the ``n`` :term:`free (closure) variables <closure variable>` from the closure
Removes the need for special code on the caller's side when calling into the frame. Removes the need for special code on the caller's side when calling
closures. closures.
.. versionadded:: 3.11 .. versionadded:: 3.11
@ -1937,10 +1937,10 @@ instructions:
.. data:: hasfree .. data:: hasfree
Sequence of bytecodes that access a free variable. 'free' in this Sequence of bytecodes that access a :term:`free (closure) variable <closure variable>`.
context refers to names in the current scope that are referenced by inner 'free' in this context refers to names in the current scope that are
scopes or names in outer scopes that are referenced from this scope. It does referenced by inner scopes or names in outer scopes that are referenced
*not* include references to global or builtin scopes. from this scope. It does *not* include references to global or builtin scopes.
.. data:: hasname .. data:: hasname

View File

@ -684,9 +684,10 @@ are always available. They are listed here in alphabetical order.
``__builtins__`` dictionary into *globals* before passing it to :func:`exec`. ``__builtins__`` dictionary into *globals* before passing it to :func:`exec`.
The *closure* argument specifies a closure--a tuple of cellvars. The *closure* argument specifies a closure--a tuple of cellvars.
It's only valid when the *object* is a code object containing free variables. It's only valid when the *object* is a code object containing
The length of the tuple must exactly match the number of free variables :term:`free (closure) variables <closure variable>`.
referenced by the code object. The length of the tuple must exactly match the length of the code object'S
:attr:`~codeobject.co_freevars` attribute.
.. audit-event:: exec code_object exec .. audit-event:: exec code_object exec

View File

@ -167,11 +167,12 @@ Examining Symbol Tables
.. method:: get_nonlocals() .. method:: get_nonlocals()
Return a tuple containing names of nonlocals in this function. Return a tuple containing names of explicitly declared nonlocals in this function.
.. method:: get_frees() .. method:: get_frees()
Return a tuple containing names of free variables in this function. Return a tuple containing names of :term:`free (closure) variables <closure variable>`
in this function.
.. class:: Class .. class:: Class

View File

@ -199,7 +199,7 @@ Standard names are defined for the following types:
.. data:: CellType .. data:: CellType
The type for cell objects: such objects are used as containers for The type for cell objects: such objects are used as containers for
a function's free variables. a function's :term:`closure variables <closure variable>`.
.. versionadded:: 3.8 .. versionadded:: 3.8

View File

@ -564,8 +564,9 @@ Special read-only attributes
in which the function was defined. in which the function was defined.
* - .. attribute:: function.__closure__ * - .. attribute:: function.__closure__
- ``None`` or a :class:`tuple` of cells that contain bindings for the - ``None`` or a :class:`tuple` of cells that contain bindings for the names specified
function's free variables. in the :attr:`~codeobject.co_freevars` attribute of the function's
:attr:`code object <function.__code__>`.
A cell object has the attribute ``cell_contents``. A cell object has the attribute ``cell_contents``.
This can be used to get the value of the cell, as well as set the value. This can be used to get the value of the cell, as well as set the value.
@ -1285,10 +1286,14 @@ Special read-only attributes
* - .. attribute:: codeobject.co_cellvars * - .. attribute:: codeobject.co_cellvars
- A :class:`tuple` containing the names of :ref:`local variables <naming>` - A :class:`tuple` containing the names of :ref:`local variables <naming>`
that are referenced by nested functions inside the function that are referenced from at least one :term:`nested scope` inside the function
* - .. attribute:: codeobject.co_freevars * - .. attribute:: codeobject.co_freevars
- A :class:`tuple` containing the names of free variables in the function - A :class:`tuple` containing the names of
:term:`free (closure) variables <closure variable>` that a :term:`nested scope`
references in an outer scope. See also :attr:`function.__closure__`.
Note: references to global and builtin names are *not* included.
* - .. attribute:: codeobject.co_code * - .. attribute:: codeobject.co_code
- A string representing the sequence of :term:`bytecode` instructions in - A string representing the sequence of :term:`bytecode` instructions in

View File

@ -90,7 +90,7 @@ If a name is bound in a block, it is a local variable of that block, unless
declared as :keyword:`nonlocal` or :keyword:`global`. If a name is bound at declared as :keyword:`nonlocal` or :keyword:`global`. If a name is bound at
the module level, it is a global variable. (The variables of the module code the module level, it is a global variable. (The variables of the module code
block are local and global.) If a variable is used in a code block but not block are local and global.) If a variable is used in a code block but not
defined there, it is a :dfn:`free variable`. defined there, it is a :term:`free variable`.
Each occurrence of a name in the program text refers to the :dfn:`binding` of Each occurrence of a name in the program text refers to the :dfn:`binding` of
that name established by the following name resolution rules. that name established by the following name resolution rules.
@ -337,6 +337,9 @@ enclosing namespace, but in the global namespace. [#]_ The :func:`exec` and
:func:`eval` functions have optional arguments to override the global and local :func:`eval` functions have optional arguments to override the global and local
namespace. If only one namespace is specified, it is used for both. namespace. If only one namespace is specified, it is used for both.
.. XXX(ncoghlan) above is only accurate for string execution. When executing code objects,
closure cells may now be passed explicitly to resolve co_freevars references.
Docs issue: https://github.com/python/cpython/issues/122826
.. _exceptions: .. _exceptions:

View File

@ -0,0 +1,3 @@
Clarified the dual usage of the term "free variable" (both the formal
meaning of any reference to names defined outside the local scope, and the
narrower pragmatic meaning of nonlocal variables named in ``co_freevars``).