Python – Change variable outside function without return
Question:
I just started learning Python and I ran into this problem. I want to set a variable from inside a function, but the variable is outside the function.
The function gets activated by a button. Then I want to get the value from that variable that I set when I press another button. The problem is that the value that I put inside a variable from inside the function doesn’t stay. How would I solve this?
The code is underneath. currentMovie
is the variable I try to change. When I press the button with the function update_text()
, it prints out a random number like it is supposed to. But when I press the button that activates update_watched()
it prints out 0. So I am assuming the variable never gets set.
import random
from tkinter import *
current_movie = 0
def update_text():
current_movie = random.randint(0, 100)
print(current_movie)
def update_watched():
print(current_movie)
root = Tk()
root.title("MovieSelector9000")
root.geometry("900x600")
app = Frame(root)
app.grid()
canvas = Canvas(app, width = 300, height = 75)
canvas.pack(side = "left")
button1 = Button(canvas, text = "SetRandomMovie", command = update_text)
button2 = Button(canvas, text = "GetRandomMovie", command = update_watched)
button1.pack(anchor = NW, side = "left")
button2.pack(anchor = NW, side = "left")
root.mainloop()
Answers:
Use global
to modify a variable outside of the function:
def update_text():
global current_movie
current_movie= random.randint(0, 100)
print(current_movie)
However, don’t use global
. It’s generally a code smell.
Here’s a simple (python 2.x) example of how to 1 not use globals and 2 use a (simplistic) domain model class.
The point is: you should first design your domain model independently from your user interface, then write the user interface code calling on your domain model. In this case your UI is a Tkinter GUI, but the same domain model should be able to work with a command line UI, a web UI or whatever.
NB : for python 3.x, replace Tkinter
with tkinter
(lowercase) and you can get rid of the object
base class for Model
.
import random
from Tkinter import *
class Model(object):
def __init__(self):
self.current_movie = 0
def update_current_movie(self):
self.current_movie = random.randint(0, 100)
print(self.current_movie)
def update_watched(self):
print(self.current_movie)
def example_with_args(self, arg):
print("ExampleWithArg({})".format(arg))
def main():
model = Model()
root = Tk()
root.title("MovieSelector9000")
root.geometry("900x600")
app = Frame(root)
app.grid()
canvas = Canvas(app, width = 300, height = 75)
canvas.pack(side = "left")
button1 = Button(canvas, text = "SetRandomMovie", command=model.update_current_movie)
button2 = Button(canvas, text = "GetRandomMovie", command=model.update_watched)
button3 = Button(canvas, text = "ExampleWithArg", command=lambda: model.example_with_args("foo"))
button1.pack(anchor = NW, side = "left")
button2.pack(anchor = NW, side = "left")
button3.pack(anchor = NW, side = "left")
root.mainloop()
if __name__ == "__main__":
main()
Here’s a simple but dirty solution: use a mutable variable.
Instead of
currentMovie = 0
def UpdateText():
currentMovie = random.randint(0, 100)
print(currentMovie)
you can use a single-cell list for currentMovie and pass it as a (default) argument to UpdateText():
currentMovie = [0]
def UpdateText(cM=currentMovie): # The default value will 'bind' currentMovie to this argument
cM[0] = random.randint(0, 100) # This will change the *contents* of the variable
print(cM[0]) # I used a different name for the parameter to distinguish the two
UpdateText() # Calls UpdateText, updating the contents of currentMovie with a random number
Note that setting currentMovie
itself (not its contents) with a new value—even with a new list—would cause UpdateText()
to stop updating currentMovie
unless the def
block were run again.
currentMovie = [0]
def UpdateText(cM=currentMovie): # The default value will 'bind' currentMovie to this argument
cM[0] = random.randint(0, 100) # This will change the *contents* of the list
print(cM[0]) # I used a different name for the parameter to distinguish the two
currentMovie = 3 # UpdateText() will no longer affect this variable at all
# This will thus not throw an error, since it's modifying the 'old' currentMovie list:
UpdateText() # The contents of this list can also no longer be accessed
This is more of a handy trick if you’re building something quick and dirty and don’t want to build a class; I find that Python is great for such things, so I think that this is still worthwhile to share despite the other answers.
For more serious purposes, though, creating a class as in bruno’s answer would almost certainly be better.
For me, the already mentioned answers did not work for two reasons.
- In case of an error, I need the variable for a deeper analysis of the data which led to the error.
- I’m using the function in a
pandas.DataFrame.apply()
to paste the usual output into a column of the existing DataFrame. Therefore the error information shall not be in the return statement.
Solution for me:
Since I did not find a direct solution I decided to write the variable on disk:
with open('var.pickle', 'wb') as f:
pickle.dump(var, f)
And then to import it where ever I need it:
with open('var.pickle', 'rb') as f:
var = pickle.load(f)
I just started learning Python and I ran into this problem. I want to set a variable from inside a function, but the variable is outside the function.
The function gets activated by a button. Then I want to get the value from that variable that I set when I press another button. The problem is that the value that I put inside a variable from inside the function doesn’t stay. How would I solve this?
The code is underneath. currentMovie
is the variable I try to change. When I press the button with the function update_text()
, it prints out a random number like it is supposed to. But when I press the button that activates update_watched()
it prints out 0. So I am assuming the variable never gets set.
import random
from tkinter import *
current_movie = 0
def update_text():
current_movie = random.randint(0, 100)
print(current_movie)
def update_watched():
print(current_movie)
root = Tk()
root.title("MovieSelector9000")
root.geometry("900x600")
app = Frame(root)
app.grid()
canvas = Canvas(app, width = 300, height = 75)
canvas.pack(side = "left")
button1 = Button(canvas, text = "SetRandomMovie", command = update_text)
button2 = Button(canvas, text = "GetRandomMovie", command = update_watched)
button1.pack(anchor = NW, side = "left")
button2.pack(anchor = NW, side = "left")
root.mainloop()
Use global
to modify a variable outside of the function:
def update_text():
global current_movie
current_movie= random.randint(0, 100)
print(current_movie)
However, don’t use global
. It’s generally a code smell.
Here’s a simple (python 2.x) example of how to 1 not use globals and 2 use a (simplistic) domain model class.
The point is: you should first design your domain model independently from your user interface, then write the user interface code calling on your domain model. In this case your UI is a Tkinter GUI, but the same domain model should be able to work with a command line UI, a web UI or whatever.
NB : for python 3.x, replace Tkinter
with tkinter
(lowercase) and you can get rid of the object
base class for Model
.
import random
from Tkinter import *
class Model(object):
def __init__(self):
self.current_movie = 0
def update_current_movie(self):
self.current_movie = random.randint(0, 100)
print(self.current_movie)
def update_watched(self):
print(self.current_movie)
def example_with_args(self, arg):
print("ExampleWithArg({})".format(arg))
def main():
model = Model()
root = Tk()
root.title("MovieSelector9000")
root.geometry("900x600")
app = Frame(root)
app.grid()
canvas = Canvas(app, width = 300, height = 75)
canvas.pack(side = "left")
button1 = Button(canvas, text = "SetRandomMovie", command=model.update_current_movie)
button2 = Button(canvas, text = "GetRandomMovie", command=model.update_watched)
button3 = Button(canvas, text = "ExampleWithArg", command=lambda: model.example_with_args("foo"))
button1.pack(anchor = NW, side = "left")
button2.pack(anchor = NW, side = "left")
button3.pack(anchor = NW, side = "left")
root.mainloop()
if __name__ == "__main__":
main()
Here’s a simple but dirty solution: use a mutable variable.
Instead of
currentMovie = 0
def UpdateText():
currentMovie = random.randint(0, 100)
print(currentMovie)
you can use a single-cell list for currentMovie and pass it as a (default) argument to UpdateText():
currentMovie = [0]
def UpdateText(cM=currentMovie): # The default value will 'bind' currentMovie to this argument
cM[0] = random.randint(0, 100) # This will change the *contents* of the variable
print(cM[0]) # I used a different name for the parameter to distinguish the two
UpdateText() # Calls UpdateText, updating the contents of currentMovie with a random number
Note that setting currentMovie
itself (not its contents) with a new value—even with a new list—would cause UpdateText()
to stop updating currentMovie
unless the def
block were run again.
currentMovie = [0]
def UpdateText(cM=currentMovie): # The default value will 'bind' currentMovie to this argument
cM[0] = random.randint(0, 100) # This will change the *contents* of the list
print(cM[0]) # I used a different name for the parameter to distinguish the two
currentMovie = 3 # UpdateText() will no longer affect this variable at all
# This will thus not throw an error, since it's modifying the 'old' currentMovie list:
UpdateText() # The contents of this list can also no longer be accessed
This is more of a handy trick if you’re building something quick and dirty and don’t want to build a class; I find that Python is great for such things, so I think that this is still worthwhile to share despite the other answers.
For more serious purposes, though, creating a class as in bruno’s answer would almost certainly be better.
For me, the already mentioned answers did not work for two reasons.
- In case of an error, I need the variable for a deeper analysis of the data which led to the error.
- I’m using the function in a
pandas.DataFrame.apply()
to paste the usual output into a column of the existing DataFrame. Therefore the error information shall not be in the return statement.
Solution for me:
Since I did not find a direct solution I decided to write the variable on disk:
with open('var.pickle', 'wb') as f:
pickle.dump(var, f)
And then to import it where ever I need it:
with open('var.pickle', 'rb') as f:
var = pickle.load(f)