I'm trying to design a counter which would be incremented each time an action is done. Something like this:
def action_on_accounts(self, accounts):
for account in accounts[9:]:
try:
self.browser.get(account)
time.sleep(5)
action_button = self.browser.find_element_by_xpath(u'//button[contains(#class, "Heart")]').click()
counter_var = self.count_actions(counter_var)
print(counter_var)
except selenium.common.exceptions.NoSuchElementException:
break
def count_actions(self, counter_var):
return counter_var + 1
def main(self):
counter_var = 0
(...)
This is throwing a UnboundLocalError: local variable 'counter_var' referenced before assignment
I have read that I have to declare counter_var as global inside function and did this:
def count_actions(self, counter_var):
global counter_var
return counter_var + 1
It was throwing SyntaxError: name 'counter_var' is parameter and global error.
So I tried this :
def count_actions(self):
global counter_var
return counter_var + 1
Calling it this way :
counter_var = self.count_actions()
print(counter_var)
And now I have NameError: name 'counter_var' is not defined....
Please assist
An alternative, simpler solution is to use python's built-in enumerate(); It exists so we don't have to make our own functions. You can then set the count equal to a global variable declared outside your functions
So the code would look like this:
counter_var = 0
def action_on_accounts(self, accounts):
for count, account in enumerate(accounts[9:]):
global counter_var
counter_var = count
print(counter_var)
def main(self):
global counter_var
counter_var = 0
(...)
You should consider defining counter_var as an attribute: self.counter_var. It'll be accessible throughout your class (assuming that's what's going on). You won't have to provide it explicitly as an argument in your functions/methods and no need to worry about global variables.
def action_on_accounts(self, accounts):
for account in accounts[9:]:
try:
self.browser.get(account)
time.sleep(5)
action_button = self.browser.find_element_by_xpath(u'//button[contains(#class, "Heart")]').click()
self.count_actions()
print(self.counter_var)
except selenium.common.exceptions.NoSuchElementException:
break
def count_actions(self):
self.counter_var += 1
def main(self):
self.counter_var = 0
(...)
You'll probably want to initialize self.counter_var with your class however.
Well if you really want to use a global variable (which I advice against) you can do it with the global keyword. You need that keyword to declare the variable. Example:
def action_on_accounts():
global counter_var #declare the variable
counter_var = count_actions(counter_var)
print(counter_var)
def count_actions(cv):
global counter_var #you do not have to declare the variable here since we are theoretically in the right scope
print(counter_var) #just to check that is global
return cv + 1 #no need to declare since it is a parameter
if __name__ == "__main__":
counter_var = 0
action_on_accounts() #1
action_on_accounts() #2
print(counter_var) #will return 2
I tested this in the IPython console (Python 3.6).
However I strongly recommend that you use attributes of classes to do the same effect (as in use self not global). Global variables may create bad code.
Related
I want to know if there are some cases where declaring global keyword is necessary in python.
Yes, there are some cases where global is neccessary.
Have a look at this code, which will work fine:
i = 42 # this is a global var
def f():
print(i)
But what if you would like to edit i (which is a global variable).
If you do this, you get an error:
i = 42 # this is a global var
def f():
i += 1 # this will not work
print(i)
We can only access i. If python compiles the function to bytecode it detects an assignment to a variable and it assumes it is a local variable. But this is not the case here (it is a global variable). Therefore if we also want to modify the global var i we must use the global keyword.
i = 42 # this is a global var
def f():
global i
i += 1 # this will change the global var without error
print(i)
When you have shared resources and want to make changes in global one.
a = 0
def add_five():
global a
a += 5
def remove_two():
global a
a -= 2
add_five() # a = 5
add_five() # a = 10
add_five() # a = 15
remove_two() # a = 13
I try to stop a .after function i tkinter in python 3.6, but have probems with namspace of variables. I get NameError: "name 'counter' is not defined" when I use this code:
I read this excellent post, from where I got the idea of using two buttons instead of one.
Use start and stop function with same button in Tkinter
def count(ac,rs):
if ac:
global counter
counter += 1
label.config(text=counter)
label.after(1000, count)
if rs:
counter = 0
counter += 1
count()
def start():
ac = True
rs=False
count(ac,rs)
def stop():
ac = False
label.configure(text='0')
rs = True
count(ac,rs)
Since I declare the variable counter in the count() function, I don't understand the NameError.
I create a list and try to append it to another list, but even though it is a global list it still is not defined.
I had the same issue trying to apppend a string to another list, and that had the same error so I tried to make the string a list.
sees if the player hits
def hit_or_stand():
global hit **<-- notice hit is a global variable**
if hitStand == ("hit"):
player_hand()
card = deck()
hit = []
hit.append(card)
now I need to append hit to pHand (player's hand)
def player_hand():
global pHand
deck()
pHand = []
pHand.append(card)
deck()
pHand.append(card)
pHand.append(hit) **<--- "NameError: name 'hit' is not defined"**
pHand = (" and ").join(pHand)
return (pHand)
hit_or_stand()
player_hand()
global hit
This does not declare a variable which is global. It does not create a variable which does not exist. It simply says "if you see this name in this scope, assume it's global". To "declare" a global variable, you need to give it a value.
# At the top-level
hit = "Whatever"
# Or in a function
global hit
hit = "Whatever"
The only time you need a global declaration is if you want to assign to a global variable inside a function, as the name could be interpreted as local otherwise. For more on globals, see this question.
There is a misunderstanding of the global operation in OP's post. The global inside a function tells python to use that global variable name within that scope. It doesn't make a variable into a global variable by itself.
# this is already a global variable because it's on the top level
g = ''
# in this function, the global variable g is used
def example1():
global g
g = 'this is a global variable'
# in this function, a local variable g is used within the function scope
def example2():
g = 'this is a local variable'
# confirm the logic
example1()
print( g ) # prints "this is a global variable"
example2()
print( g ) # still prints "this is a global variable"
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.
I'm using ftplib to create a simple script to push out a file to multiple IP addresses, all set up as FTP servers. I wanted to display progress in the file upload process, but I'm having an issue. I use the callback argument of FTP.storbinary() and it works with something like this:
count = 0
def update(block):
count2 = str(count + 1)
print count2
However, if I try to do any arithmetic outside of a str() call, the program hangs. So the following doesn't work:
count = 0
def update(block):
count += 1
print count
Even wrapping count in a str() call doesn't work. It just hangs on the first call.
If you just try calling update yourself, instead of passing it to FTP.storbinary, you'll see the problem immediately:
>>> update('')
UnboundLocalError: local variable 'count' referenced before assignment
If you want to update a global variable, you have to mark it global explicitly:
def update(block):
global count
count += 1
print count
See the FAQ entry Why am I getting an UnboundLocalError when the variable has a value? and the following question What are the rules for local and global variables in Python?, and the docs on global, for more details.
A better way to solve this would be to write a class:
class FtpHandler(object):
def __init__(self):
self.count = 0
def update(self, block):
self.count += 1
print self.count
Then, to use it, you construct an instance of the class, and pass a bound method instead of a plain function to the FTP code. For example, instead of this:
ftp = ftplib.FTP(...)
# ...
ftp.storbinary(spam, eggs, callback=update)
… do this:
myhandler = FtpHandler()
ftp = ftplib.FTP(...)
# ...
ftp.storbinary(spam, eggs, callback=myhandler.update)
It doesn't just hang, it produces an Exception (specifically an UnboundLocalError). You're trying to modify a global variable inside of a function; to do this the variable must be declared global:
count = 0
def update(block):
global count
count += 1
print count
This is almost always a sign of bad design, in your case it would probably be better to use a class with an attribute:
class MyCallbackHandler(object):
def __init__(self):
self.count = 0
def update(self, block):
self.count += 1
#... etc.