Hi Géry,
“However, C++ outputs 1
in both cases, as expected.”
Expected by who? Not me. Why does C++ behave so strangely?
"What was the rationale of the Python language designers for choosing
variable hoisting?"
I have never heard of “variable hoisting” before, but if this is it:
"Variables declared using var are created before any code is executed in
a process known as hoisting. Their initial value is undefined."
then Python doesn’t have it. It’s certainly not a term commonly used in
Python. Python is not like Javascript, it doesn’t not create variables
before the code runs.
(Although some Python interpreters, not all, may sometimes allocate
space for local variables at runtime, when the function object is
created, before it is called.)
Your Python code:
def f():
print(x)
x = 1
f()
Here x is a global variable. Inside the function f
, the scope of the
names “print” and “x” are both global, so when you call the function,
the builtin print function and the global variable x are both found.
def f():
print(x)
x = 2
x = 1
f()
Here x is a local variable, and you try to print its value before
x exists, so you get an UnboundLocalError exception.
When the compiler is compiling a function, read-only access to a name
like “print” or “x” uses the global and builtin scopes. But if you
assign a value to a name, then the compiler treats it as a local
variable unless you declare it global.
So in your example above, the name “print” is read but not written to,
so it is looked for in the global scope and the builtin print
function is located and called.
The name “x” is written to (with the “x = 2” assignment) so the
compiler treats it as a local variable. At lookup time, the variable
doesn’t yet exist and so you get an exception.
The global
statement is a compiler directive: it tells the compiler to
treat the name as a global variable, even if it otherwise would have
been treated as a local.
def f():
global x
print(x)
x = 2
x = 1
f()
will print 1
and then assign 2 to the global variable x.
You can google for “Python scoping rule LEGB” for more information:
https://duckduckgo.com/?q=python+scoping+LEGB+rule
Why does Python work this way? I don’t know, why does any language
choose the scoping rules they choose?
Why does Lua default to having variables be global unless declared
local? Why does Javascript have a separate global and module scope? Why
does BASIC have only global variables? (1970s BASIC, not modern Visual
Basic.)
People design their languages to work the way they want them to work. I
imagine the same applied to Python: Guido chose the scoping rules
because they were easy to implement, or similar to what ABC used, or
because they solved a problem, or because he liked that rule and
disliked more complicated rules, or something like that.
“This counterintuitive behaviour of Python”
Counter-intuitive to who? It is perfectly intuitive to me, and the C++
behaviour seems strange even after you explained it.
"and Javascript is also known
as variable hoisting since it ‘hoists’ variable declarations (but
not definitions) at the beginning of their blocks."
Python doesn’t have declarations, with the possible exception of the
global and nonglobal statements. (I personally consider them to be more
like compiler directives than a declaration.) But in any case, in both
of your examples of Python code, there are no variable declarations, so
there is nothing to be hoisted.