I have a problem that sounds like a delegation problem. I have a code like the following:
class foo(object):
def __init__(self,onEvent1="", onEvent2=""):
self.OnEvent1=onEvent1
self.OnEvent1=onEvent2
def aLoop(self):
...
#in case of event1
self.OnEvent1()
...
#in case of event2
self.OnEvent2()
EventType=0
def MyBar1():
print("Event Type1")
EventType=1
def MyBar2():
print("Event Type2")
EventType=2
myFoo=foo(MyBar1,MyBar2)
while True:
myFoo.aLoop()
if (EventType==1):
print ("EventType is 1")
EventType=0
elif (EventType==2):
print ("EventType is 2")
EventType=0
I can see the message of the print() inside the callback functions but not the print() of the messages in the while loop.
The variable EventType doesn't change its value.
What I can do?
The EventType variables in MyBar1 and MyBar2 are local variables. Any variable you bind to is a local, unless explicitly configured otherwise; assignments, function parameters, a function or class definition and names you import are all ways to bind a name.
You need to use a global statement to change this:
def MyBar1():
global EventType
print("Event Type1")
EventType=1
def MyBar2():
global EventType
print("Event Type2")
EventType=2
Note that there is little point in giving your event arguments an empty string as default argument:
def __init__(self,onEvent1="", onEvent2=""):
If they are optional, set them to None and test for that:
def __init__(self, onEvent1=None, onEvent2=None):
self.OnEvent1 = onEvent1
self.OnEvent2 = onEvent2
def aLoop(self):
...
#in case of event1
if self.OnEvent1 is not None:
self.OnEvent1()
...
#in case of event2
if self.OnEvent2 is not None:
self.OnEvent2()
EventType=0
def MyBar1():
global EventType
print("Event Type1")
EventType=1
def MyBar2():
global EventType
print("Event Type2")
EventType=2
The problem is, you need to modify a global variable, but you're creating a local one instead. You can still access the global variable without using global variable. You need this to modify it.
Related
Here are my two functions:
def selectBadge(a):
curItem = SelectBadgeView.focus()
inter_var = SelectBadgeView.item(curItem)
ListValues = inter_var['values']
print(ListValues)
SelectedBadge.set("Selected Badge: "+str(ListValues[0]))
SelectedBadgeStr=str(ListValues[0])
return SelectedBadgeStr
def selectScout(a,SelectedBadgeStr):
print(a)
if SelectedPatrol.get()==("Selected Patrol: Please Select A Patrol"):
tk.messagebox.showerror("ERROR","Please select a Patrol")
return
if SelectedBadge.get()==("Selected Badge: Please Select A Badge"):
tk.messagebox.showerror("ERROR", "Please select a Badge")
return
print(SelectedBadgeStr)
return
I want to pass the variable SelectedBadgeStr from selectBadge() to selectScout().
The variable a is a internal variable used by the tkinter treeview widget.
a = <ButtonRelease event state=Button1 num=1 x=144 y=39>
I have tried:
return SelectedBadgeStr
However this did not work.
The function is called by:
SelectScoutView.bind('<ButtonRelease-1>', selectScout)
Did you declare SelectedBadgeStr as for example an empty string before defining the function selectBadge(a)?
So just declare it like:
SelectedBadgeStr = ''
def selectBadge(a):
curItem = SelectBadgeView.focus()
.
.
.
It should work.
You can also use a shared global variable:
Declare the variable outside of the functions use:
SelectedBadgeStr = None
At the beginning of the functions use:
global SelectedBadgeStr
Whenever you use the variable now within the functions, you access the global variable
I'm running into an issue where a global variable isn't "remembered" after it's modified in 2 different functions. The variable df is supposed to be a data frame, and it doesn't point to anything until the user loads in the right file. This is similar to something I have (using pandas and tkinter):
global df
class World:
def __init__(self, master):
df = None
....
def load(self):
....
df = pd.read_csv(filepath)
def save(self):
....
df = df.append(...)
save() is always called after load(). Thing is, when I call save(), I get the error that "df is not defined." I thought df got its initial assignment in init(), and then got "updated" in load()? What am I doing wrong here?
You have to use global df inside the function that needs to modify the global variable. Otherwise (if writing to it), you are creating a local scoped variable of the same name inside the function and your changes won't be reflected in the global one.
p = "bla"
def func():
print("print from func:", p) # works, readonly access, prints global one
def func1():
try:
print("print from func:", p) # error, python does not know you mean the global one
p = 22 # because function overrides global with local name
except UnboundLocalError as unb:
print(unb)
def func2():
global p
p = "blubb" # modifies the global p
print(p)
func()
func1()
print(p)
func2()
print(p)
Output:
bla # global
print from func: bla # readonly global
local variable 'p' referenced before assignment # same named local var confusion
bla # global
blubb # changed global
for anyone coming here using python3 - try using nonlocal instead of global - a new construct introduced in python3 which allows you to mutate and read global variables in local scope
You have to use the global keyword inside the function rather than outside. All the df that you have defined inside your function are locally scoped. Here is the right way -
df = pd.DataFrame() # No need to use global here
def __init__(self, master):
global df # declare here
df = None
....
def load(self):
global df # declare here
....
df = pd.read_csv(filepath)
def save(self):
global df # declare here
....
df = df.append(...)
My example is a progress bar
In its simplest form a progress bar is
bar = ProgressBar.Open()
for item in list:
bar.Update(count, len(list))
I would instead like my calling code to be
for item in list:
bar.Update(count, len(list))
I want my Update() function to Open() a bar for the caller if one is not open. The caller doesn't need any other access to the bar than to update it so there's no value in having the meter` handle.
How can I retain state to tell if the Update had been previously called?
I could create a global variable and keep track that way, but I have a gut sense there's a Pythonista way of doing it.
Trying again, but in a way that has no application to stumble on.
The base question is:
I have a function that will be called multiple times.
I want to do something different the first time it is called.
How can a function in Python do that?
In C, that of course would be a...
static variable
I'm just now kinda figuring it out as I type, sorry.
========================
I'm sure all these edits are not how stackoverflow is supposed to work. I'm sorry for not getting it right yet, but am very appreciative of the replies.
Despite it sounding like I'm breaking all the rules of good practices, it's when looked at from the CALLER'S point of view that I had hoped to make an impact.
What if the only thing you needed to do to add a progress meter, even for debugging, to your program was make a call to a progress meter update in the location you want to show progress?
That's the underlying motivation. Slide in 1-line, get something cool for the trouble.
This progress meter was added to my otherwise boring file de-duplicator by adding just the single call:
msg = f'Deduplicating {idx} of {total_files} files\n' f'{dup_count} Dupes found\n' f'{small_count} Too small'
not_cancelled = sGUI.ProgressBar('De-dupe', msg, idx, total_files)
To avoid using global variables, you can use decorator. Here's a simple example:
def open():
print 'open'
def update():
print 'update'
def call_once(func1, *args1, **kwargs1):
def decorator(func2):
called = [False]
def wrapper(*args2 ,**kwargs2):
if not called[0]:
func1(*args1, **kwargs1)
called[0] = True
return func2(*args2, **kwargs2)
return wrapper
return decorator
#call_once(open)
def my_update():
update()
for i in xrange(5):
my_update()
which give the result:
open
update
update
update
update
update
For more information about decorator, please visit: https://wiki.python.org/moin/PythonDecorators
For what you want, you can use a class:
class ProgressBar:
def __init__(self):
self._opened = False
def Open(self):
print("Open")
def Update(self):
if self._opened:
print("Update!")
else:
self.Open()
print("set flag")
self._opened = True
print("Update")
In action:
In [32]: bar = ProgressBar()
In [33]: bar.Update()
Open
set flag
Update
In [34]: bar.Update()
Update!
Note: I copied your casing so as to make it more clear to you, however, the official Python style would be like this:
class ProgressBar:
def __init__(self):
self._opened = False
def open(self):
pass # open stuff
def update(self):
if self._opened:
pass # update stuff
else:
self.open()
self._opened = True
Using snake_case for everything except the ClassName.
OK, I found a solution using 'globals'. I thought that a nested function was the way to do it... then I mixed the two.
By 'globals' I meant variables declared outside the scope of a function. I want to be able to import my module without the import creating anything.
Here's the code that shows how to do this with globals
def my_update(amount):
global flag
if 'flag' in globals():
print('I have been here')
else:
print('I have not been here')
flag = True
return
for i in range(10):
print(f'Calling number {i}')
result = my_update(1)
It does the job for the goals I had set out, but I'm SURE there are better, safer ways that are more elegant as well.
I posted this question on a Python forum and got back the best answer so far using a function attribute. It's brilliant and it works.
Here is code that demonstrates this construct... it should go in everyone's 'Favorite Python Constructs' notebook in my opinion.
def func():
if not hasattr(func, 'static_variable'):
func.static_variable = 0
func.static_variable += 1
return func.static_variable
def main():
for i in range(10):
print('func = {}'.format(func()))
if __name__ == '__main__':
main()
The output is
func = 1
func = 2
func = 3
func = 4
func = 5
func = 6
func = 7
func = 8
func = 9
func = 10
I've been playing around with tkinter a little and I found that you have to make a def method to use buttons. So I wanted to make 13 buttons because I wanna make a calculator but I really didn't want to make 13 def methods that do very similar things. I tried doing a nested def method (I've never really done that before) but, from what I've tried, it won't work. Am I just doing it wrong or is it just impossible. If it is impossible, is there any other way to mass produce def methods besides a lot of copy and a lot of paste? Here's the code:
from tkinter import *
window=Tk()
window.geometry("500x500")
print("Restarting")
user=""
def oneb():
global user
print("1",end="")
user+="1"
def twob():
global user
user+="2"
print("2",end="")
def threeb():
global user
user+="3"
print("3",end="")
def fourb():
global user
user+="4"
print("4",end="")
def fiveb():
global user
user+="5"
print("5",end="")
def sixb():
global user
user+="6"
print("6",end="")
def sevenb():
global user
user+="7"
print("7",end="")
def eightb():
global user
user+="8"
print("8",end="")
def nineb():
global user
user+="9"
print("9",end="")
def zerob():
global user
user+="0"
print("0",end="")
def plusb():
global user
user+="+"
print("+",end="")
def minusb():
global user
print("-",end = "")
def equalb():
global user
if "+" in user:
user=user.partition("+")
symbol="+"
elif "-" in user:
user=user.partition("-")
symbol="-"
else:
print("=",user)
num1=user[0]
num2=user[2]
num1=int(num1)
num2=int(num2)
if symbol=="+":
answer=num1+num2
else:
answer=num1-num2
answer=str(answer)
print("="+answer)
heightb=5
widthb=10
#I know here I probably should've just made a def method.
one=Button(window, text="1",command=oneb,height=heightb,width=widthb)
one.grid(row=1,column=1)
two=Button(window, text="2",command=twob,height=heightb,width=widthb)
two.grid(row=1,column=2)
three=Button(window, text="3",command=threeb,height=heightb,width=widthb)
three.grid(row=1,column=3)
four=Button(window, text="4",command=fourb,height=heightb,width=widthb)
four.grid(row=2,column=1)
five=Button(window, text="5",command=fiveb,height=heightb,width=widthb)
five.grid(row=2,column=2)
six=Button(window, text="6",command=sixb,height=heightb,width=widthb)
six.grid(row=2,column=3)
seven=Button(window, text="7",command=sevenb,height=heightb,width=widthb)
seven.grid(row=3,column=1)
eight=Button(window, text="8",command=eightb,height=heightb,width=widthb)
eight.grid(row=3,column=2)
nine=Button(window, text="9",command=nineb,height=heightb,width=widthb)
nine.grid(row=3,column=3)
zero=Button(window, text="0",command=zerob,height=heightb,width=widthb)
zero.grid(row=4,column=2)
plus=Button(window, text="+", command=plusb,height=heightb, width=widthb)
plus.grid(row=2,column=4)
minus=Button(window,text="-", command=minusb,height=heightb, width=widthb)
minus.grid(row=1,column=4)
equal=Button(window,text="=", command=equalb,height=heightb, width=widthb)
equal.grid(row=3,column=4)
mainloop()
#button location var.grid(row=x,column=x)
You can make a function that returns a function.
For example,
def build_button(number):
def button():
global user
user += str(number)
print(number, end="")
return button
oneb = build_button(1)
twob = build_button(2)
# ...
This should be functionally identical to the [number]b functions you have above.
i want to make a calculator program such that on pressing each button the value of the button will add to the string for example if 1 is pressed four times it should be 1111 so when i try do that it says variable referenced before assigned so that i may capture numbers of any digits but I'm having error: variable st referenced before assigned
st=""
def but1():
v.set("1")
global st=st+"1"
can we solve this error or is there any way to make the variable global?
def but1():
st=st+"1"
v.set(st)
def but2():
st=st+"2"
v.set(st)
def but3():
st=st+"3"
v.set(st)
def but4():
st=st+"4"
v.set(st)
def but5():
st=st+"5"
v.set(st)
def but6():
st=st+"6"
v.set(st)
def but7():
st=st+"7"
v.set(st)
def but8():
st=st+"8"
v.set(st)
def but9():
st=st+"9"
v.set(st)
global takes just the name that should be treated as a global:
def but1():
global st
v.set("1")
st=st+"1"
You cannot put it on a line with a full expression.
The global declaration applies to the whole of the current scope (so to any use of the st name in the function). If you need to access st in multiple functions, you need to use the same keyword in all those functions.