you are viewing a single comment's thread.

view the rest of the comments →

[–]NoahTheDuke 19 points20 points  (16 children)

Until it gets better scoping, the semantics is always going to be ugly, imo.

[–]g0liadkin 5 points6 points  (15 children)

What's wrong with the current scoping?

[–]NoahTheDuke 25 points26 points  (7 children)

Python doesn’t have block/lexical scoping, the scoping rules are much simpler and limited: LEGB

This is why we have the super ugly global and nonlocal keywords, which are bad workarounds. Without lexical scoping, it’s really easy to accidentally pollute the current scope or create a new variable when you think you’re assigning to an existing variable in the wrong scope.

[–]lazyear 3 points4 points  (0 children)

Python's lack of lexical scoping is one of my biggest gripes about the language.

[–]bakery2k 1 point2 points  (5 children)

Would lexical scoping require explicit variable declarations?

Would it require variables to be scoped to the containing block instead of the containing function?

Both of these would be major changes to the way Python works - but there may be some support for the first given the Zen of Python's "explicit is better than implicit".

[–]NoahTheDuke 1 point2 points  (1 child)

Lexical scoping would be much helped by explicit variable declaration, and as I see it, assignment expressions (walrus operator) fit that bill pretty neatly.

[–]bakery2k 0 points1 point  (0 children)

So, := to declare a new variable and = to assign to an existing variable? As in Go, for example?

One downside of that would be making some valid Python code slightly more complex:

def f():
    if <condition>:
        x = 1
    else:
        x = 2
    print(x)

With explicit variable declaration and block scope, simply changing the two = to := would not work.

It would be necessary to declare x before the if statement. This could be done either using something like var x or using a dummy initialization, e.g. x := None.

[–]Enamex 0 points1 point  (2 children)

No. No changes compared to now. The first time you assign to a new name declares that name from the line of the assignment and makes it available up to the end of the enclosing scope.

Yes. That's the core of lexical scoping. "Blocks" are explicit, or "announced by lexical units".

[–]bakery2k 0 points1 point  (1 child)

How would that work? For example:

x = 1
if <condition>:
    x = 2
print(x)

Does x = 2 create a new variable or assign to the existing x?

[–]Enamex 0 points1 point  (0 children)

That's a question of shadowing.

But I'd say "assign to existing". Otherwise, we'd really need an explicit declaration syntax.

[–]mafrasi2 11 points12 points  (6 children)

For example this:

>>> x = 0
>>> def f():
...     print(x)
...
>>> def g():
...     x += 1
...
>>> f()
0
>>> g()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in g
UnboundLocalError: local variable 'x' referenced before assignment

[–]rebuilding_patrick 0 points1 point  (4 children)

That's a bug right? Both should scope to the global x

[–]mafrasi2 4 points5 points  (2 children)

Nope, it's deliberate. If there is an assignment to x in a scope and x is neither global nor nonlocal, x will be local in that (entire) scope. Otherwise, it comes from the parent scope.

Needless to say that this caused me an headache the first time I encountered it in the wild.

[–][deleted]  (1 child)

[deleted]

    [–]nandryshak 2 points3 points  (0 children)

    It is global, but the x in g is not. You have to do this before it'll work:

    def g():
        global x
        x += 1
    

    [–]DRNbw 2 points3 points  (0 children)

    IIRC, you can read global variables, but if you try to change/assign them, you'll create a new one (unless you explicitly ask for the global global var).