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.