The following Python program A outputs 1
, as expected, while the following Python program B raises an unbound local variable x
error, counterintuitively.
- Program A:
def f(): print(x)
x = 1
f()
- Program B:
def f(): print(x); x = 2
x = 1
f()
Javascript has the exact same behaviour.
- Program A:
function f() { console.log(x); }
let x = 1;
f();
- Program B:
function f() { console.log(x); let x = 2; }
let x = 1;
f();
However, C++ outputs 1
in both cases, as expected.
- Program A:
#include <iostream>
int x;
void f() { std::cout << x; }
int main() { x = 1; f(); return 0; }
- Program B:
#include <iostream>
int x;
void f() { std::cout << x; int x = 2; }
int main() { x = 1; f(); return 0; }
So all programs A output 1
. The differences in programs B between Python and Javascript on the one hand, and C++ on the other hand, result from their different scoping rules: in C++, the scope of a variable starts at its declaration, while in Python and Javascript, it starts at the beginning of the block where the variable is declared. Consequently, in C++ printing variable x
in function f
resolves to the value 1
of global variable x
since it is the only variable in context at this point of execution. In Python and Javascript printing variable x
in function f
resolves to nothing and raises an unbound local variable x
error since local variable x
is already in context at this point of execution and therefore it masks global variable x
without being bound yet to the value 2
. This counterintuitive behaviour of Python and Javascript is also known as variable hoisting since it ‘hoists’ variable declarations (but not definitions) at the beginning of their blocks.
What are the benefits and drawbacks of variable hoisting in programming languages?