Execution Model

A Python program is executed by a stack-based interpreter.

When a function is called, a new `execution environment' for it is pushed onto the stack. An execution environment contains (among other data) pointers to two `symbol tables' that are used to hold variables: the local and the global symbol table. The local symbol table contains local variables of the current function invocation (including the function arguments); the global symbol table contains variables defined in the module containing the current function.

The `global' symbol table is thus only global with respect to the current function. There are no system-wide global variables; using the import statement it is easy enough to reference variables that are defined in other modules. A system-wide read-only symbol table is used for built-in functions and constants though.

On assignment to a variable, by default an entry for it is made in the local symbol table of the current execution environment. The global command can override this (it is not enough that a global variable by the same name already exists). When a variable's value is needed, it is searched first in the local symbol table, then in the global one, and finally in the symbol table containing built-in functions and constants.

The term `variable' in this context refers to any name: functions and imported modules are searched in exactly the same way.

Names defined in a module's symbol table survive until the end of the program. This approximates the semantics of file-static global variables in C or module variables in Modula-3. A module is initialized the first time it is imported, by executing the text of the module as a parameterless function whose local and global symbol tables are the same, so names are defined in module's symbol table. (Modules implemented in C have another way to define symbols.)

A Python main program is read from standard input or from a script file passed as an argument to the interpreter. It is executed as if an anonymous module was imported. Since import statements are executed like all other statements, the initialization order of the modules used in a program is defined by the flow of control through the program.

The `attribute' notation m.name, where m is a module, accesses the symbol name in that module's symbol table. It can be assigned to as well. This is in fact a special case of the construct x.name where x denotes an arbitrary object; the type of x determines how this is to be interpreted, and what assignment to it means.

For instance, when a is a list object, a.append yields a built-in `method' object which, when called, appends an item to a. (If a and b are distinct list objects, a.append and b.append are distinguishable method objects.) Normally, in statements like a.append(x), the method object a.append is called and then discarded, but this is a matter of convention.

List attributes are read-only — the user cannot define new list methods. Some objects, like numbers and strings, have no attributes at all. Like all type checking in Python, the meaning of an attribute is determined at run-time — when the parser sees x.name, it has no idea of the type of x. Note that x here does not have to be a variable — it can be an arbitrary (perhaps parenthesized) expression.

Given the flexibility of the attribute notation, one is tempted to use methods to replace all standard operations. Yet, Python has kept a small repertoire of built-in functions like len() and abs(). The reason is that in some cases the function notation is more familiar than the method notation; just like programs would become less readable if all infix operators were replaced by function calls, they would become less readable if all function calls had to be replaced by method calls (and vice versa!).

The choice whether to make something a built-in function or a method is a matter of taste. For arithmetic and string operations, function notation is preferred, since frequently the argument to such an operation is an expression using infix notation, as in abs(a+b); this definitely looks better than (a+b).abs(). The choice between make something a built-in function or a function defined in a built-in method (requiring import) is similarly guided by intuition; all in all, only functions needed by `general' programming techniques are built-in functions.