Title: locals() and free variables
Type: enhancement Stage: patch review
Components: Documentation Versions: Python 3.7, Python 3.6, Python 3.5, Python 2.7
Status: closed Resolution: postponed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, marco.buttu, martin.panter, mdk, r.david.murray, terry.reedy, xdegaye
Priority: normal Keywords: patch

Created on 2016-12-01 16:54 by marco.buttu, last changed 2016-12-03 18:07 by marco.buttu. This issue is now closed.

File name Uploaded Description Edit
locals_func.patch marco.buttu, 2016-12-02 11:32
Messages (11)
msg282200 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-01 16:54
The locals() documentation [1] says that "Free variables are returned by locals() when it is called in function blocks". A free variable inside a function has a global scope, and in fact it is not returned by locals()::

>>> x = 33
>>> def foo():
...     print(x)
...     print(locals())
>>> foo()

Maybe "function blocks" here means "closure"? Does the doc mean this?

>>> def foo():
...     x = 33
...     def moo():
...         print(x)
...         print(locals())
...     return moo
>>> moo = foo()
>>> moo()
{'x': 33}

In that case, I think it is better to write "closures" instead of 
"function blocks".

msg282236 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-02 11:32
In addition, also if here "function blocks" means nested function, the sentence "Free variables are returned by locals() when it is called in function blocks" I think is wrong. It is true only in case of free variables belonging to the local enclosing scope. For instance, in the following case ``x`` is free in ``moo()``, but it is not in ``locals()``::

>>> x = 10
>>> def foo():
...     def moo():
...         print(x)
...         print(locals())
...     return moo
>>> moo = foo()
>>> moo()

I attach a patch with a new description and an example.

PS. Is the rst rendered by Sphinx? In that case, why we are not using the doctest Sphinx extension to test the code examples?
msg282239 - (view) Author: Julien Palard (mdk) * (Python committer) Date: 2016-12-02 14:21
Hi, thanks for reporting,

Variables are defined in python docs¹ by:

> If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global. If a name is bound at 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 defined there, it is a free variable.

According to this definition, global variables used in a code block are free variable, is this intentional? I think so, but can't be sure, maybe someone is seeing this as "globals are NOT free variables", in this case, this definition should probably be enhanced.

If I'm right, maybe we should just change "Free variables are returned"… to "Non-global free variables are returned"…?
msg282242 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-02 14:58
Yes, it is the same. "Free variables belonging to the enclosing local namespaces" are "non-global free variables". In order for the reader to better contextualize the sentence, I think it is worth keeping the code example in the patch.
msg282244 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2016-12-02 15:17
To answer the parenthetical question (I haven't looked at the doc issue itself), we don't doctest the examples because most of them were written before sphinx grew a doctest extension, so a lot of them don't pass for reasons that have nothing to do with code validity.  Issues and patches for making the doctests pass are welcome, and we have applied a number of them.  There is a lot of work to do before we could add running doctest to our doc buildbot, though.
msg282255 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2016-12-02 20:19
FWIW the definition of free variables can be found in the Language Reference at section 4.2.1. "Binding of names" [1].
The list of the free variables of a user defined function can be accessed through the __closure__ function attribute, a tuple. The function attributes are listed in the Language Reference
at section 3.2. "The standard type hierarchy" [2]. In the example given at msg282236, the value of x would be printed by the statement:

msg282260 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2016-12-03 01:21
Marco, your patch removes the description for class blocks. Is that your intent, or just an accident? See r53954.

My understanding is “function block” is there to distinguish these three modes:

def foo():
    # Function block

class Bar:
    # Class block

# Module level

There are patches proposed at Issue 17546 which also address this reference to “free variables”. Perhaps you could provide feedback on them?

Also see Issue 26683 and Issue 12165, about what free variables, locals, nonlocals, non-globals, etc can mean to different people.
msg282282 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-03 12:25
Martin, I removed the class blocks by accident. In any case, I reject the patch by myself, because to me the definition of "free variable" is not clear. 
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 ``x`` is free both in ``foo()`` and in ``moo()``:

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

But actually for the code object it is not:

>>> foo.__code__.co_freevars
>>> moo.__code__.co_freevars

Thank you for your feedback, I will continue the discussion in issue 26683.

msg282283 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-03 12:37
Sorry, in the last example, for the code object ``x`` is not free both in ``foo()`` and ``moo()``, but this does not affect the conclusion.
msg282288 - (view) Author: Julien Palard (mdk) * (Python committer) Date: 2016-12-03 15:44
Should this issue be closed so?
msg282290 - (view) Author: Marco Buttu (marco.buttu) * Date: 2016-12-03 18:07
I close it because the meaning of "free variable" is not clear. We are discussing about it in issue 26683.
Date User Action Args
2016-12-03 18:07:06marco.buttusetstatus: open -> closed
resolution: postponed
messages: + msg282290
2016-12-03 15:44:06mdksetmessages: + msg282288
2016-12-03 12:37:58marco.buttusetmessages: + msg282283
2016-12-03 12:25:03marco.buttusetmessages: + msg282282
2016-12-03 01:44:38terry.reedysetnosy: + terry.reedy
2016-12-03 01:21:49martin.pantersetversions: - Python 3.3, Python 3.4
nosy: + martin.panter

messages: + msg282260

stage: patch review
2016-12-02 20:19:55xdegayesetnosy: + xdegaye
messages: + msg282255
2016-12-02 15:17:07r.david.murraysetnosy: + r.david.murray
messages: + msg282244
2016-12-02 14:58:04marco.buttusetmessages: + msg282242
2016-12-02 14:21:40mdksetnosy: + mdk
messages: + msg282239
2016-12-02 11:32:02marco.buttusetfiles: + locals_func.patch
keywords: + patch
messages: + msg282236
2016-12-01 16:54:37marco.buttucreate