Title: Execution model should explain compile vs definition vs execution time
Type: enhancement Stage: resolved
Components: Documentation Versions: Python 3.2, Python 3.3, Python 2.7
Status: closed Resolution: postponed
Dependencies: Superseder:
Assigned To: docs@python Nosy List: cheryl.sabella, daniel.urban, docs@python, eric.araujo, eric.snow, ncoghlan, r.david.murray, terry.reedy
Priority: normal Keywords:

Created on 2011-06-20 15:15 by ncoghlan, last changed 2019-02-13 14:04 by ncoghlan. This issue is now closed.

Messages (11)
msg138728 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2011-06-20 15:15
The current execution model documentation in the Language Reference doesn't clearly explain the multiple phases of code execution:

1. Compilation time (statement by statement in the main module and at the interactive prompt, all at once for module import and the exec() and compile() builtins)
2. Definition time for function statements (i.e. when the function statement itself is executed)
3. Execution time for function and generator bodies (i.e. when a function is called and when next() is invoked on a generator)
msg138733 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-06-20 15:42
Isn't 'definition time' vs 'execution time' an artificial distinction?  I'm surprised that the main module is compiled differently than a regular module.  Is that an artifact of the CPython implementation or a part of the language?  If the latter it should certainly be documented. 

What are the concrete differences between 'execution time' and 'defintion time' that are missing from the language specification?
msg139005 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2011-06-25 00:05
I presume we are discussing Chapter 4. Execution model. I do not see any mention there of the difference between function definition and body execution time. The section of def statements (7.6) has this: "The function definition does not execute the function body; this gets executed only when the function is called. [3]". This is true for lambda expressions also, but nothing is said there. This may contribute to people expecting name resolution of non-parameters in lambda bodies to happen at lambda expression == function definition time rather than later at function call time (what Nick called function execution time).

I think 4.1. Naming and binding should be slightly expanded and renamed 4.1. Name binding and resolution. People need to understand both how and when associations are made and when they are used (as well as what scopes are used). Not understanding this results in the perennial confusion about params with defaults and free names in lambda expressions.

In function headers, param = expr creates a binding when the header is executed, which is when the function object is created. It is stored in the function object. It may optionally be used when the function is called.

In lambda bodies such as in lambda x: x + i, there is no binding of i so i must be bound in a surrounding context either before the function is defined or before the function is called, depending on the surrounding contex, and used when the function is called.

These two facts are somehow not clear enough at present. In any case, my point is the the 'when' part of name binding and use is related to the when of execution.

The relevance of compilation time is more subtle. It is when module, statement, and function code objects are created, including by compile(). If code objects (and compile) are part of the language, then so is compile time. The existence of both interactive statement execution and delayed module execution also has implications for how Python works and how it can and cannot be modified. I am not sure how much to say, though.

Part of the execution model of Python is that, leaving aside i/o, the effect of executing statements is to change user visible name and slot bindings. There are essentially no declarations for changing invisible compiler/interpreter settings.
msg139009 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2011-06-25 00:28
Most of my thoughts on this topic can be found at (that was written ~2.5, so some parts of it are a little dated, but the Essential Concepts section is still fairly accurate).

The most relevant part to the current discussion is 1.5:

1.5 Classes and Functions are Defined at Runtime
In static language such as Java or C++, function and class definitions are essentially directives to the language compiler regarding the nature of certain identifiers. While class and function definition statements still have implications for the compilation stage in Python, these definitions are also first class statements that are executed at runtime, just like any other statement. While the code within the function or class definition statement is compiled at compile time, the actual definition of the function or class does not occur until the statement is executed at runtime.
In top level code, this distinction usually doesn't matter, but it has some significant implications when class and function definitions are placed inside a function. Doing so means that a new class or a new function is defined every time the containing function is executed. This means that not only is it possible to have factory functions that create new class instances (as is common in all object-oriented languages), but it is also possible to have factory functions that create new classes or new functions. Another key feature of nested functions is that they employ lexical scoping, allowing nested functions to see identifiers defined in outer scopes.
msg139010 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2011-06-25 00:32
The other directly relevant part is in section 5:

5.1.3 Evaluating Default Arguments
In static languages such as C, function definitions are instructions to the compiler rather than executable statements. This leads to such languages making distinctions between events that occur at run-time and at compile time. In Python, function definitions are statements in their own right, so run-time events related to functions are further divided into events that occur at definition time and at execution time. Definition time refers to the point where the function is defined by the def statement. Execution time refers to the point where the function is called and the body of the function is executed.
Where these distinctions matter the most is in the evaluation of default arguments, as this occurs only once at the time the function is defined. For immutable default values like numbers or the constant value None, this doesn't have any perceptible effect. For mutable defaults like lists and dictionaries, however, it makes a significant difference, as it means a single instance of the object is created at definition time, and is then shared across all invocations of the function that use the default argument value. This can result in unintended side effects. The typical approach to handling this situation is to use None as the default argument, and include code in the body of the function to create the appropriate default value when the parameter is set to None. The following listing shows the difference between the two mechanisms.
[Grab the ODF file if you want to see the code listing]
msg139016 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-06-25 05:11
My understanding is that the language reference is a purposefully minimalist document that specifies the language (insofar as anything other than the CPython implementation does so).  So while better explanations of the implications of the language design are a good thing, they don't necessarily belong in the language reference.  (I'm not saying they don't, I'm just repeating what the intro says: "this is not a tutorial".)  In particular I am suspicious that statements that begin "in languages such as ..." don't belong in the language reference as it is currently written.
msg139025 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2011-06-25 08:11
Oh, agreed - the quotes above are from the "Python's User Reference" I wrote several years back, but never got around to converting from ODF and subsequently updating and publishing in a more accessible way.

It was designed to fill the gap that exists in the documentation between the tutorial (which gives usage information, but doesn't really explain how things work) and the language reference (which is a fairly minimalist spec that doesn't explain design intent or rationale very much). The associated publishing deal fell through so I got the copyrights back and donated the whole thing to the PSF.

However, the first step in making it more useful is extracting the text from the ODF files into reStructuredText and that's never made it to the top of my to-do list.

The ideal situation from my point of view would be for someone on doc-sig to help with the conversion process, but I've never had any takers (although I'll grant that the last time I asked was a number of years ago).
msg139096 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2011-06-25 16:19
You could try the mentors list or Doug Hellman's volunteer group, too.
msg334795 - (view) Author: Cheryl Sabella (cheryl.sabella) * (Python committer) Date: 2019-02-03 15:33
Hi Nick,

Would you be able to update the link as isn't valid anymore?  Have things changed even more since you created this issue or are you still interested in pursuing this?  Thanks!
msg334819 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2019-02-04 12:13 is the Chapter 1 userref link now that the PSF SVN server has shut down.

Depending on what you mean by "Pursuing this...":

1. I'd definitely still love to see the content from the user reference converted into a collaborative documentation project and brought up to speed with modern Python - it's just a large enough task that I've shied away from doing it myself.

2. For this ticket itself, I'm thinking this kind of explanatory material might find a better home in the Developer's Guide these days, as there it could also provide pointers to the different parts of CPython that implement each phase.
msg335442 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2019-02-13 14:04
Cheryl has started the conversion process for the old user reference manuscript [1], so I think it makes sense to mark this as "postponed" for now, until we see how far we can get with this more usage-centric level of explanation as a separate project from the language reference.

Date User Action Args
2019-02-13 14:04:52ncoghlansetstatus: open -> closed
type: enhancement
messages: + msg335442

resolution: postponed
stage: needs patch -> resolved
2019-02-04 12:13:53ncoghlansetmessages: + msg334819
2019-02-03 15:33:34cheryl.sabellasetnosy: + cheryl.sabella
messages: + msg334795
2011-07-09 20:57:38eric.snowsetnosy: + eric.snow
2011-06-25 16:19:25r.david.murraysetmessages: + msg139096
2011-06-25 08:11:52ncoghlansetmessages: + msg139025
2011-06-25 05:11:46r.david.murraysetmessages: + msg139016
2011-06-25 00:32:08ncoghlansetmessages: + msg139010
2011-06-25 00:28:36ncoghlansetmessages: + msg139009
2011-06-25 00:05:35terry.reedysetnosy: + terry.reedy
messages: + msg139005
2011-06-24 19:03:57daniel.urbansetnosy: + daniel.urban
2011-06-20 18:48:45eric.araujosetversions: + Python 2.7, Python 3.2, Python 3.3
nosy: + eric.araujo, docs@python

assignee: docs@python
components: + Documentation
stage: needs patch
2011-06-20 15:42:16r.david.murraysetnosy: + r.david.murray
messages: + msg138733
2011-06-20 15:15:11ncoghlancreate