Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[doc] Questionable terminology ('free variables') for describing what locals() does #70870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rhettinger opened this issue Apr 1, 2016 · 7 comments · Fixed by #122545
Closed
Labels
docs Documentation in the Doc dir

Comments

@rhettinger
Copy link
Contributor

rhettinger commented Apr 1, 2016

BPO 26683
Nosy @rhettinger, @terryjreedy, @vadmium, @marco-buttu

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2016-04-01.01:31:32.772>
labels = ['3.11', '3.9', '3.10', 'docs']
title = "[doc] Questionable terminology ('free variables') for describing what locals() does"
updated_at = <Date 2021-11-27.00:24:37.261>
user = 'https://github.com/rhettinger'

bugs.python.org fields:

activity = <Date 2021-11-27.00:24:37.261>
actor = 'iritkatriel'
assignee = 'docs@python'
closed = False
closed_date = None
closer = None
components = ['Documentation']
creation = <Date 2016-04-01.01:31:32.772>
creator = 'rhettinger'
dependencies = []
files = []
hgrepos = []
issue_num = 26683
keywords = []
message_count = 6.0
messages = ['262713', '262718', '262722', '262753', '282284', '282989']
nosy_count = 5.0
nosy_names = ['rhettinger', 'terry.reedy', 'docs@python', 'martin.panter', 'marco.buttu']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue26683'
versions = ['Python 3.9', 'Python 3.10', 'Python 3.11']

Linked PRs

@rhettinger
Copy link
Contributor Author

The docs for locals() say that inside a function that local variables and free variables are included: https://docs.python.org/3/library/functions.html#locals

Elsewhere we use the terms "cell variables" or "nonlocals". Mathematically, the word "free" means unbound, so that isn't applicable here and isn't the usual way of describing variables in the enclosing scope.

>>> def f(x):
        def g(y):
            z = x + y
            print(locals())
        return g

>>> f(10)(20)
{'x': 10, 'y': 20, 'z': 30}

Also, I'm not sure why "x" and "y" are included in the definition for locals(). That seems strange given that "x" and "y" are not local to "g" and that "x += 1" would fail with an UnboundLocalError.

@rhettinger rhettinger added the docs Documentation in the Doc dir label Apr 1, 2016
@vadmium
Copy link
Member

vadmium commented Apr 1, 2016

Regarding “free variables”, in bpo-17546 I proposed the wording “. . . also includes non-local, non-global names”.

In your code example, I would consider y to be 100 percent local to the g() function. It is a function parameter, and “y += 1” should work. I agree x is not a true local, it is a “non-local non-global”. A national variable maybe :)

For functions, considering that you shouldn’t modify the dictionary (original concern in bpo-17546), I do agree the behaviour is a bit strange and inconsistent. It might have made more sense to either only return true locals, or return a complete namespace of locals, non-locals, globals, and builtins. It seems there is no way to get a similar list of non-local non-globals in a class scope.

@rhettinger
Copy link
Contributor Author

A national variable maybe :)

I would think that "nonlocal" is exactly the right term given that that is how you would declare it if you wanted to write to it.

>> w = 5

>>> def f(x):
        def g(y):
            nonlocal x
            global w
            z = x + y
            x += 1
            print(locals())
            print(globals())
        return g

>>> f(10)(20)
{'y': 20, 'x': 11, 'z': 30}
{'w': 5, ...}

@terryjreedy
Copy link
Member

I requested that we stop (mis)using 'free variable' in the docs years ago. A strong +1 from me.

The 'locals' function what named when 'local' and 'non-global' were synonyms. When non-local, non-global names were added, nonlocals were included with 'locals' as 'non-global'. (This must have been thought to be more useful than adding nonlocals() or excluding them.)

They are, of course, local in some surrounding non-global context. And for most purposes, their entries in locals() should also be treated as read-only. I think the doc should say that function locals() includes the locals of surrounding function contexts, even though they are called 'nonlocal' within the nested function.

@marco-buttu
Copy link
Mannequin

marco-buttu mannequin commented Dec 3, 2016

The documentation [1] says: "If a variable is used in a code block but not defined there, it is a free variable." According to this description, it seems to me that the variable x is free in foo()::

  >>> def foo():
  ...     print(x)

But actually for the code object it is not::

  >>> foo.__code__.co_freevars
  ()

The meaning of free variable used for the code object is consistent with the locals() documentation [2]: "Free variables are returned by locals() when it is called in function blocks". In fact, in the following code x` is not a free variable for locals()``::

  >>> def foo():
  ...     print(x)
  ...     print(locals())
  ... 
  >>> x = 3
  >>> foo()
  3
  {}

So, I thing there is an inconsistency between the definition of "free variable" given in [1] and the meaning of "free variable" both in the code object and in the locals() documentation [2].
But if we change the definition of "free variable" according to the meaning of both the code object and locals(), I am afraid we go in the wrong direction, because I suspect for most people the proper definition of free variable is the one given in [1]. Also for Wikipedia [3]: "In computer programming, the term free variable refers to variables used in a function that are neither local variables nor parameters of that function.".

Instead, if we keep or remove the definition of "free variable" given in [1], I agree with Terry to change the locals() documentation, writing that "locals() includes also the locals of the surrounding function contexts, even though they are called 'nonlocal' within the nested function.". Maybe it is also worth adding a shell example, to better clarify.

So, at the end, to me the ideal solution would be to keep in [1] the current definition of "free variable", to change the locals() documentation according to Terry suggestion, and to change the behavior of code.co_freevars (maybe not only this) in order to fit the definition.

[1] https://docs.python.org/3/reference/executionmodel.html#naming-and-binding
[2] https://docs.python.org/3/library/functions.html#locals
[3] https://en.wikipedia.org/wiki/Free_variables_and_bound_variables

@marco-buttu
Copy link
Mannequin

marco-buttu mannequin commented Dec 12, 2016

Another point in the doc, where the meaning of "free variable" is inconsistent with the locals() and code.co_freevars meaning:

https://docs.python.org/3/reference/executionmodel.html#interaction-with-dynamic-features

@iritkatriel iritkatriel added 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes labels Nov 27, 2021
@iritkatriel iritkatriel changed the title Questionable terminology for describing what locals() does [doc] Questionable terminology ('free variables') for describing what locals() does Nov 27, 2021
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
@erlend-aasland erlend-aasland removed 3.11 only security fixes 3.10 only security fixes 3.9 only security fixes labels Mar 3, 2024
ncoghlan added a commit to ncoghlan/cpython that referenced this issue Aug 1, 2024
@ncoghlan
Copy link
Contributor

ncoghlan commented Aug 1, 2024

In 3.13, the locals() docs now say:

In an optimized scope (including functions, generators, and coroutines), each call to locals() instead returns a fresh dictionary containing the current bindings of the function’s local variables and any nonlocal cell references.

I also posted a separate PR after reviewing all the references to "free variable" in the documentation: #122545

While I left some of them alone, I changed several others to either "closure variable", or "free (closure) variable" (sometimes including specific references to co_freevars).

The PR makes the definitions of co_freevars and __closure__ reference each other and defines both "free variable" and "closure variable" in the glossary. The definition of "free variable" notes that the term gets used in two different ways rather than attempting to exclusively reserve it for the formally correct meaning.

ncoghlan added a commit to ncoghlan/cpython that referenced this issue Oct 7, 2024
ncoghlan added a commit that referenced this issue Oct 8, 2024
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 <[email protected]>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 8, 2024
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".

---------

(cherry picked from commit 2739099)

Co-authored-by: Alyssa Coghlan <[email protected]>
Co-authored-by: Carol Willing <[email protected]>
ncoghlan added a commit that referenced this issue Oct 8, 2024
…125088)

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".

---------

(cherry picked from commit 2739099)

Co-authored-by: Alyssa Coghlan <[email protected]>
Co-authored-by: Carol Willing <[email protected]>
efimov-mikhail pushed a commit to efimov-mikhail/cpython that referenced this issue Oct 9, 2024
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 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants