I'm definitely being nerd-sniped here, so take this with a grain of salt.
The reason why Python currently doesn't have block scopes is the rule that assignment creates or updates a variable in the closest scope. This rule has taken Python far, because it means you can create a local variable by assigning to it without having to declare it.
If we wanted to introduce something similar to C's block scopes, regardless of syntax, we'd have to answer the question where local variables will be declared.
Suppose we had the (crummy) syntax
block var1 = exp1, var2 = exp2, ...:
<statements>
as the moral equivalent to C's
{
int var1 = exp1, var2 = exp2, ...;
<statements>
}
then we'd still have to decide what should happen to assignments to variables *other* than var1, var2, etc. Consider
def f(x):
block a = 1:
x = a # Is this local to the block or to the function?
return x
print(f(0)) # 0 or 1?
IMO, if 'x = a' were to create a new variable in the block, I think that would be confusing. It would also beg the question why we bother to adorn the block with variable declarations.
There are other good reasons why any variable *not* explicitly declared as being part of a block should have function scope: otherwise it would be impossible for code in a block to have a side effect to be consumed by the code outside the block. That could be addressed by using nonlocal, but at that point we might as well use a nested class statement (at least that gives us a way to get the locals out, as attributes of said class). Certainly it would be a shame if we added a block scope concept and we couldn't upgrade the for-loop to make use of it, optionally (like you can do in C with "for (int i = 0; i < n; i++) { ... }"). And a for-loop that can't set other variables that survive the loop seems crippled.
So maybe we should go with Paul's original gut feelings and introduce
let var1 = exp1, var2 = exp2, ...:
<statements>
with the semantics that it doesn't really create a fully-fledged new scope but defines scoped constants.
It then would make sense to add some other syntax extensions like
for let i in ...:
<statements>
(makes i local to the loop, giving it the semantics needed by closures as well) and
with contextmanager() as let x:
<statements>
Clearly we need to bikeshed more about the syntax.
We could then declare that comprehensions implicitly use 'for let ...'. If we were to drive this through quickly enough we could even make it apply to pattern matching captures. (Or maybe pattern matching could get these semantics anyway.)
My bus is here, so that's it,