Why is this global value treated as a local value?(python) - python

I am writing a tic-tac-toe program and I noticed that the "turn" value must have "global" before it. However, it is already considered a global value. Why is it treated as a local value? Out of all the other projects I have done, I have never encountered this problem. Further, all the other variables I have declared are already considered global here.
Here is the code:
players = ["O", "X"]
buttons = [[0,0,0],
[0,0,0],
[0,0,0]]
def newGame():
turn = random.choice(players)
def nextTurn(row, column):
global turn
if buttons[row][column]['text'] == "" and check_win == False:
if turn == players[0]:
buttons[row][column]['text'] = turn
if check_win == False:
turn = players[1]
label.config(text=players[1] + " turn")
The problem seems to lie in the 'turn = players[1], but I do not know why.
Also, in a piece of sample code I experimented with, there was one instance that said the variable was not even defined, is it somehow connected to this problem? I have tried removing the global call, even in the sample code I learned from, where there were no nested functions, however, the same problem seems to occurs due to the line mentioned above. I have even tried making the variable an int or string, still the line mentioned above causes an error.

You don't need the global keyword to access the value of a global variable, but you do need global to assign a new definition to that global variable name.
Here's a simpler example to explain what's happening here.
x = 2
def test_1():
print(x)
def test_2():
x += 1
print(x)
def test_3():
global x
x+=1
print(x)
You should find that test_1() and test_3() can be used without any issues, but test_2() throws an error.
Note: this does not necessarily prevent us from modifying global variables without the global keyword. For example, the following code runs without any error.
x=[10]
def test():
x[0]+=1
print(x)
Also, in particular your context, you should really be using nonlocal rather than global because you are using nested functions. Consider the following.
x=10
def test():
x = 2
def test_1():
global x
x+=1
print(x)
def test_2():
nonlocal x
x+=1
print(x)
test_1()
Try running this script and changing the test_1() call at the end to test_2().

Related

Globalizing variables in sub functions doesnt work

My problem is simple.
This code,
def first():
x = "hello"
def second():
global x
x = "goodbye"
second()
first()
does not work, since globalizing a variable declared inside a function isn't valid. If it was just one function I know it would, but how could I get this to work like this?
Problem :
The problem is there are two different x variables getting confused. The x in second() is global and can be printed out after running after running your code. The problem is the first x is considered a local in first(). You can check it on your own by copy-pasting the following code in different places to understand how the code considers the variable:
if 'x' in locals():
print("local")
elif 'x' in globals():
print("global")
else:
print("unedfined")
Solution 1:
Always having x as a global by adding global x right on the line after def first() (and keeping it after def second())
Solution 2:
Accepting not having globals. Instead you can return x at the end of your functions and assigning an other variable (that could have the same name) to the returned value of the function eg.x = second(). Then using that variable as an argument eg. def first(x):
Hope it helps.
You need to declare a variable as global before you write to it in each of the functions. Like so:
def first():
global x
x = "hello"
def second():
global x
x = "goodbye"
second()
first()
print(x)
# >>> goodbye
I'd recommend a different structure to your project though
What I understood, you can initialize x variable before the function and then assign value to it when you want like this:
x = ''
def first():
x = "hello"
def second():
x = "goodbye"
second()
first()

Python while loop UnboundLocalError

I need a function with a while loop, that as long as the condition is False calls the function again.
This code will obviously result in a endless loop since "i" is always re declared as "0":
def fun():
i = 0
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
So I see no other way than to go about it like this:
i = 0
def fun():
# does something else
while i < 5:
i += 1
fun()
print('done')
fun()
But with this code It says local variable 'i' referenced before assignment.
I don't get it what am I doing wrong here?
I know I could use an if statement here, but that would be much more complicated and can get in my way in the project, although its not impossible.
Basically, the value i has not been declared in the function, so the function doesn't know what i is. There are a couple of ways to fix this.
One way is to put the while loop OUTSIDE of the function. This is how it would look like:
i = 0
def fun():
#Do Something...
print('done')
while i < 5:
i += 1
fun()
Another way would be to add the parameter i into the function. You do this by adding i into the brackets. This is how it would look like:
i = 0
def fun(i): #I Added An i Here So That The Function Knows We Need A Value Of i
#Do Something...
while i < 5:
i += 1
fun(i) #I Use The Same Value Of i Set At The Beginning
print('done')
fun(i) #I Call The Function Again
You could also set i as a global variable, meaning it can be used in the function too. To do that, just add global and the variable. This is how I did it:
i = 0
def fun():
global i
#Do Something...
while i < 5:
i += 1
fun()
print('done')
fun()
If neither of these 3 methods work, you could try and maybe use a for loop: something like for i in range(0, 5): , which would work too.
variable i is not in the scope of the function. Hence it is not accessible.
I believe the below code will help you get the desired output.
def fun(count):
# does something else
if(count >= 5):
return
fun(count + 1)
print('done')
fun(0)
Since other posts has offered remedy already, here I just point out the reason more detail:
This is one of the common gotchas to the new learners, the reason is that -
it's because when you make an assignment to a variable in a scope, that variable becomes local to that scope and shadows any similarly named variable in the outer scope.
In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.
It can be a surprise to get the UnboundLocalError in previously working code when it is modified by adding an assignment statement somewhere in the body of a function.
hope this is what you are looking for:
def fun(i=0):
# does something else
result = True
while result:
print(i)
i = i + 1
while i >= 5:
result = False
break
print('done')
fun()

Python function argument can't be used as a variable in a child function

Here's the problem I try to solve:
I have a first function, to which I put in arguments. Then, later on, I have a second function, from which I want to call, as a variable, the said argument of the parent function. So it goes like:
def parent_function(argument=x):
if statement:
child_function()
else:
...
return result
def child_function():
x = x + 5
return x
If I run such a code, I get an error in the child function saying name 'x' is not defined.
However, if I fix my code to make x global in the parent function, like this:
def parent_function(argument=x):
global x
if statement:
child_function()
else:
...
return result
def child_function():
x = x + 5
return x
I get the error name 'x' is parameter and global
I need to import both functions in another file and I can't "dismantle" the child function inside the parent function.
Thanks very much for any help !
Don't use global Variables. Every function needs it's own arguments:
def parent_function(x):
if statement:
x = child_function(x)
else:
...
return result
def child_function(x):
x = x + 5
return x
name 'x' is parameter and global means you can't overwrite parameter x for being global also. To fix this, use another variable y, like this:
def parent_function(argument=x):
global y
y = x
if statement:
child_function()
else:
...
return result
def child_function():
y = y + 5
return y
This error happens because you are trying to overwrite a parameter in a function whose scope is local to that function by giving it a global scope. The problem is that variables defined in the context of a function are by definition local variables. To better illustrate this problem ,you can simply try to launch this piece of code:
def parent_function(argument="Hello"):
global argument
return argument
You will see that it will fail to run for the same reason that I have explained. I hope I have been clear in my explanation. Good luck.
The first thing you need to change is this:
def parent_function(argument=x):
If you search for how to make a default argument in a function you will get something like this: https://www.geeksforgeeks.org/default-arguments-in-python/
. This means instead of x you need to have some default value, for example:
def parent_function(argument=5):
This means that if you do not pass the argument called argument to the function value 5 will be passed.
On the other hand, it seems that you want x to be an argument, which means the def line should look like this:
def parent_function(x=5):
Second, global keyword needs to be used in the child_function since x has not been used in parent_function. This leads to this:
def parent_function(x=5):
if statement:
child_function()
else:
...
return result
def child_function():
global x
x = x + 5
return x
To have all this work, there must be at least two more lines one to set x and another to call parent_function, like this:
x = 6
parent_function(4)
But, to be even funnier, x from the arguments in parent_function and x used in child_function are not the same thing, and you can see for yourself in this example which is similar to your code, but fully executable:
def parent_function(x=5):
if True:
print(child_function())
else:
print("else branch")
return True
def child_function():
global x
x = x + 5
return x
x = 6
parent_function(4)
This prints out 11 even you might think it will print out 9!
This is due to fact that keyword global refers to the (as the word says) global variable declared outside of the functions, the variable with value 6. Usually, local and global variables should have different names, so either the argument x in parent_function or global x variable needs to be renamed.
IDK if this helps, but you will learn something from this, for sure!

Why can I edit object by calling their methods but not referencing their variable names from within a smaller scope

My main() function is:
def main():
...
def _help():
...
1 a += somelist
2 a.append(something)
a=[]
_help()
What's weird is that line 2 works perfectly fine, but line 1 throws an UnboundLocalError: Local variable 'a' referenced before assignment.
Even when I declare a as a global variable at the top of either main or _help, it still doesn't work. Why is this?
Both of these lines are editing the same variable which makes me think either both or neither of them should work. How do I get line 1 to work?
Whenever you use <variable> = <something> in Python, Python automatically assumes it is a local variable, unless specifically told otherwise.
For example:
a = 1
def f():
if False:
a = 0
print(a) # UnboundLocalError
f()
In this case, += works as assignment as well, but .append does not assign to a, but calls a method.
This is fixed by placing a nonlocal a in your function, so it can assign to the a outside of its scope:
def main():
...
def _help():
nonlocal a
a += somelist # Works!
But in this case, you can just do a.extend(somelist).

Local variable referenced before assignment error python

def outside(x=1):
def printHam():
x = x+1
print x
return printHam
myfunc = outside(7)
myfunc()
This doesn't works gives error Local variable referenced before assignment error python
However this works
def outside(x=1):
def printHam():
print x + 1
return printHam
myfunc = outside(7)
myfunc()
Because you are assigning x in the first case, python will assume x is a local variable (which is the default). In the second case you aren't assigning it so it will check the global scope.
If you want this example to work, you have to pass the outer x into the inner function like so:
def outside(x=1):
def printHam(x=x):
x = x+1
print x
return printHam
That being said, this seems like a horribly contrived use case, so there's probably a better solution for your actual use case. But I can't tell you what it is without knowing more about what you're trying to do.

Categories

Resources