2

I've declared 2 variables the same exact way, but when I'm calling them stack is being referenced fine. But python is not able to reference top. What is the issue here?

def isValid(s):
    top=-1
    stack=[-1]*10005

    def push(x):
        print(top)
        top+=1
        stack[top]=x
    push()

print(isValid(input()))

  • 1
    This question was asked before, but the answer was never accepted - it is however the correct answer: https://stackoverflow.com/questions/64323757/why-does-python-3-8-0-allow-to-change-mutable-types-from-enclosing-function-scop – Grismar Oct 23 '21 at 05:50
  • top is immutable where as stack is mutable. – N Djel Okoye Oct 23 '21 at 06:22
  • Does this answer your question? [Why does Python 3.8.0 allow to change mutable types from enclosing function scope without using "nonlocal" variable?](https://stackoverflow.com/questions/64323757/why-does-python-3-8-0-allow-to-change-mutable-types-from-enclosing-function-scop) – wovano Oct 23 '21 at 06:30
  • @Grismar, I'm curious, if you think the other question has a correct answer, and you're even mentioning it here, why didn't you upvote it? Besides that's what the voting system is intended for, it allows this question to be flagged as duplicate as well. – wovano Oct 23 '21 at 06:32
  • @Edward, can you please fix the code and indentation? The code you posted has multiple errors (besides the one you're question is about). I would fix the indentation myself, but I'm not sure I can do that without changing the intended meaning of the code. – wovano Oct 23 '21 at 06:37

4 Answers4

1

The previous answer is mostly correct. Python considers any variable that you directly change the value of to be a local variable. Since you write top += 1, top is directly modified inside push, and is a local variable to push. The variable stack, on the other hand, is not changed.

I consider the fact that Python considers += operators to cause a variable to become local to be a bit of a bug. But it's too ingrained in the language.

And yes, the correct solution is to use nonlocal.

If you removed the top += 1 line, your code wouldn't work, but top would refer to the outer variable.

Frank Yellin
  • 9,127
  • 1
  • 12
  • 22
  • You're wrong though: " += operators to cause a variable to become local" - that's not the case. If the function only contained `top += 1`, it would still be a problem. – Grismar Oct 23 '21 at 05:56
  • I should add. Before `nonlocal` was added to the language, there was a hack to get around this issue. You would make `top` be a singleton list rather than just an integer. `top = [-1]`. `top[0] += 1`. `stack[top[0]] = x`. Ugly, but it means that the variable `top` itself wasn't modified, and so didn't become local to `push`. – Frank Yellin Oct 23 '21 at 05:58
  • @Grismar. I don't understand what you're disagreeing with. If a function contains `top += 1`, then `top` is a local variable in that function (barring `nonlocal`, `global`, etc). What is your issue with that statement? – Frank Yellin Oct 23 '21 at 06:00
  • I do understand what you're saying, however although for `top += 1` to work, `top` must be local, it doesn't *become* local - if all the function contained was `top += 1`, it would still throw an error (but for a slightly different reason). `top` would still have to be assigned prior to the increment. So, I feel your explanation is very much open to misinterpretation and likely wouldn't be understood from OP's perspective. – Grismar Oct 23 '21 at 06:04
  • @Grismar. I still don't understand the distinction you're making. If the code simply contained `top += 1`, the user would get an error indicating that the variable `top` is not defined. The reason the variable `top` is not defined is because it is local, and so the function can't access the outer definition. The reason it is local is because of the `top += 1`. I think that Python made a mistake in having `top += 1`, in the absence of any other assignment to `top`, create a local variable, but that boat sailed years ago. I still don't get what you're disagreeing with. – Frank Yellin Oct 23 '21 at 23:19
  • You should probably focus on what I actually said. You wrote "Python considers += operators to cause a variable to become local" - that's simply not the case. The `+=` operator requires a "local" (or more correctly a pre-existing and accessible) variable to operate on - it does nothing to actually change the nature of the variable to comply with that need. That's what `global` and `nonlocal` are for. Nothing is causing anything to become local unless you actually add a statement that does so - hence the error. – Grismar Oct 24 '21 at 22:17
0

Because push is indented inside isValid, it can't change the value of top from its "parent" function.

You can use nonlocal:

def isValid(s):
    top=-1
    stack=[-1]*10005

    def push(x):
        nonlocal top
        print(top)
        top+=1
        stack[top]=x
enzo
  • 9,861
  • 3
  • 15
  • 38
0

The difference lies in how you access the variable, which causes it to be treated differently. Consider this:

def isValid():
    top = -1

    def push():
        print(top)

    push()


isValid()

Here there is no error, since top can be found in the scope of isValid() and calling push() will print it correctly. However, if you add top += 1, Python correctly infers that top apparently has to be a local variable for that to work, but it isn't, so now top is showing as problematic everywhere inside push() (in most IDEs).

def isValid():
    top = -1

    def push():
        print(top)  # you'll see error messages / highlighting for top here
        top += 1  # and here as well

    push()


isValid()

Note that top += 1 isn't even the first statement, but its presence causes print(top) to no longer work either, since top now has to be local. But since there's no assignment to top before the other two statements, Python will correctly complain that it has been accessed before having been assigned.

The reason this doesn't happen with your stack is that you're not actually trying to change stack inside push(). You're trying to change one of its elements (stack[1]), but that doesn't change what object stack is naming - you're not assigning to stack itself, so it does not become local.

So, Python finds stack in the scope of isValid, allows you to access it (just like top in the first example) and you can access its elements without issue.

Grismar
  • 27,561
  • 4
  • 31
  • 54
0

top is pointing to a literal hence it is immutable and stack is a mutable object if you wanted to solve the problem with top which is not actually a problem, you have 2 options.

  1. create a copy of top in push
  2. use a mutable object

example:

  1. nonlocal top
  2. top = {'value': -1 }

case: 1

def isValid(s):
top=-1
stack=[-1]*10005

def push(x):
    nonlocal top
    print(top)
    top+=1
    stack[top]=x

case 2:

def isValid(s):
    top= {'value': -1}
    stack=[-1]*10005

def push(x):
    print(top)
    top['value']+=1
    stack[top]=x
N Djel Okoye
  • 950
  • 12
  • 10