# Why does Python have variable hoisting like Javascript?

**URL:** <https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944>\
**Category:** Ideas\
**Created:** [August 11, 2020, 9:32am UTC](https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944 "2020-08-11T09:32:42Z")\
**Posts on this page:** 3\
**Page:** 2

<div class="post-metadata">

**Author:** ![McSinyx](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mcsinyx/32/2107_2.png) [@McSinyx](https://discuss.python.org/u/McSinyx)\
**Post date:** [May 13, 2021, 7:29am UTC](https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944/22 "2021-05-13T07:29:05Z")

</div>

Python only creates lexical scopes in functions, classes and modules.  
As for why, I guess it is for homogenity: unlike ALGOL-like languages  
there’s no way to create a block/scope on-the fly anyway. The `for`  
loop (and later `with`) is the only case where a name is bound without  
explicit assignment, so it behaves as-if it is assigning the name  
in the current scope every iteration.

As for why it stays this way: backward compatibility.  
A lot of code will break will break if we change the semantic now,  
instead static analysis would be more suitable to enforce what you want.

---

<div class="post-metadata">

**Author:** ![steven.daprano](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steven.daprano/32/1083_2.png) [@steven.daprano](https://discuss.python.org/u/steven.daprano)\
**Post date:** [May 13, 2021, 12:04pm UTC](https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944/23 "2021-05-13T12:04:58Z")

</div>

It is not the job of the compiler to catch every imaginable error. There  
are many circumstances were the human programmer knows better than the  
compiler that something that looks like an error is not. For example:

> [@guido](#):
>
> ```auto
> def f(a):
> for i in a:
> if isprime(i): break
> return i
> 
> ```

I may know that `a` always contains at least one prime number. I might  
know that `f` is only called inside a try…except block that will catch  
the exception.

Or I might know that the function can fail, but don’t care, because the  
code is only used in circumstances where a failure doesn’t matter.

Or I might be working under a development model based on the idea of  
software contracts: the function _requires_ a non-empty list containing  
at least one prime, and if the caller provides something else, the bug  
is in the caller’s code, and is not the responsibility of f.

So it is _not_ clear at all that the interpreter should refuse to  
compile the above function, or refuse to allow it to execute. It would  
be a poor user experience for the interpreter to insist that I fix  
non-bugs that can never happen before I can run the code.

In a very practical sense, I often write and run code which I know is  
“buggy” in some purely pedantic sense. I have the interactive  
interpreter open, I write a quick function to test some behaviour, or to  
compute some result, and run it once only to discard it afterwards. Code  
just like Guido’s `f` above. I know it is “buggy” in the sense that it  
doesn’t handle an empty list or a list with no primes correctly, and I  
_literally don’t care_ because it works under the circumstances I am  
calling it, and that’s all that matters.

Fundamentally, Python is a scripting language which is excellent for  
this sort of thing, and making the interpreter strictly refuse to  
compile or execute “buggy” code would be a _huge_ regression.

---

<div class="post-metadata">

**Author:** ![cben](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cben/32/872_2.png) [@cben](https://discuss.python.org/u/cben)\
**Post date:** [June 24, 2021, 9:49am UTC](https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944/24 "2021-06-24T09:49:15Z")

</div>

> Anyway, on my bike ride today I realized that the key here is that Python uses function scopes – unlike C++, any variable defined anywhere in a function has that whole function as its scope.

Here is another common pattern where Python benefits from this:

```auto
def f():
    if foo():
        x = bar()
    else:
        x = baz()
    do_something(x)

```

Supporting this is a necessary part of Python _not having separate variable declarations_. You just assign it, and it’s a local you can use 🪄.  
It doesn’t matter to the parser on what code path the assignment happens, or whether it happens at all (but in the latter case UnboundLocalError catches the bug).  
If it had block scope, like C / C++, the above would define 2 separate `x` variables, none of them accessible from `do_something(x)` 😕. In such language you’re forced to have a separate declaration line to clarify which scope you intended:

```auto
    SomeType x; // separate declaration line needed!
    if (foo()) {
        x = bar();
    } else {
        x = baz();
    }
    do_something(x);

```

Consider also Go which is good example of block-scoped language where it’s _mostly_ painless thanks to _syntax sugar_ to declare and initialize in same line, `x := bar()`.  
But semantically that’s still declaration + assignment, and logic like the above forces you to split them up:

```auto
    var x SomeType
    if foo() {
        x = bar() // `=` is assignment to existing variable
    } else {
        x = baz()
    }

```

[P.S. Technically, all forms of “binding” — assignment, for loops etc. — also tell the parser “this name is a local”. Arguably that’s also syntax sugar for a top-of-function declaration, just sweeter 😋 and in the rare case when you want to mutate a variable outside the function, you do need an “anti-declaration” `global` / `nonlocal`.]

[Previous page](https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944.md?page=1)
