python variable not defined within function - python

I need some help with python variables. I have a sketch that sends variables to a function called roomControl. If during this function the status has changed from a previous time then I want it to do something. At present it should printout the variable values.
I am getting the error that the status variable has not been assigned and I believe this is due to it being set outside the function. But if I set the variable within the function then this will override the functions control of these variables. (hope this makes sense)
My script is below but basicly if status changes then print out something is what I am trying to achieve
import MySQLdb
status = 0
previousStatus = 0
connSolar = MySQLdb.connect("localhost", "######", "#######", "######")
cursSolar = connSolar.cursor()
def roomControl(name, demand, thermostat, roomTemp):
if demand == 1 and thermostat > roomTemp:
status = 1
if demand == 1 and thermostat < roomTemp:
status = 0
if demand == 0:
status = 0
if (status == 1 and previousStatus == 0):
print("Room: %s, Heating demand = %s, Thermostate = %s, Room Temp = %s, Status = %s") % (name, demand, thermostat, roomTemp, status)
print("")
previousStatus = status
return (status)
while 1:
Ntemp = 25
try:
cursSolar.execute ("SELECT * FROM roomHeatingControl")
connSolar.commit()
for reading in cursSolar.fetchall():
Nthermostat = reading[6]
NSW = reading[7]
except (MySQLdb.Error, MySQLdb.Warning) as e:
print (e)
NPy_status = roomControl('Niamh', NSW, Nthermostat, Ntemp)

This is related global, local variable types. You can find here https://www.programiz.com/python-programming/global-keyword explanation.
You need only define "status" as a global.

A solution is to bring in status as a global.
status = 0
previousStatus = 0
# other code
def roomControl(name, demand, thermostat, roomTemp):
global status
global previousStatus
# This will bring in both variables to be edited within the function
Good luck!

You should learn to cut out bits of your code and simplify it without losing the error. That way you will nail down the error.
Ultimately you will end up with code like this:
status = 0
def f():
print(status)
f()
Out: 0
As expected, the function can't find status, so it looks in the global scope, finds it, and prints it.
status = 0
def f():
status = 1
print(status)
f()
Out: 1
Again as expected. We've defined a local variable status for f, so f just uses that when it prints.
status = 0
def f():
print(status)
status = 1
f()
UnboundLocalError: local variable 'status' referenced before assignment
Now it becomes clear why there is an error. The only difference from the second example is that we've swapped the order so that status is defined in f only after it is used in print, and the only difference from the first example is that we define status inside f. So this is the problem: when we define a variable inside a function -- anywhere inside a function -- Python decides that that variable must be local to the function. So when it hits the print function, it looks for a local variable status, but it hasn't been defined yet. Hence the error. Similar to if you ran this code:
print(status)
status = 1
NameError: name 'status' is not defined

Avoid implementing a solution where you need to access the Global variables directly. One way is to change to a better data structure where possible.
You can define your statuses as a list or dict (I'd prefer dict) and you can access them directly inside the function without passing them. Here is the small example
to make things bit more clear
d={'status':0, 'prev_status':0} # Intialize the dict
def myfunc():
d['status']=5 # make modifications
myfunc()
print(d) # {'status': 5, 'prev_status': 0}

Related

Python - UnboundLocalError: local variable "ID" referenced before assignment

Id, conf = recognizer.predict(gray[y:y+h,x:x+w]
def hour(cn):
for z in range(9,17):
if now.hour == z:
worksheet(cn, str(z)+":00")
def identify(number):
sht = gc.open("Test")
wks3 = sht.worksheet("NAMES")
b = wks3.acell('B'+str(number)).value
a = wks3.acell('A'+str(number)).value
if(Id == a and conf<65):
print(Id, conf)
Id = str(b)
Time = time.ctime()
hour(number)
elif(conf>64):
print(conf)
Id = "Unknown"
for m in range(2,100):
identify(m)
The above code is being used for facial recognition, I copied what I felt was necessary, it is not the entire code.
I'm trying create a function which I want to call back in a for loop
What am I doing wrong? I've been looking t this for 6 hours now, and anything I try doesn't seem to work.
I get a message back saying "UnboundLocalError: local variable 'Id' referenced before assignment"
It's impossible because I'm assigning with:
a = wks3.acell('A'+str(number)).value
So it grabs the ID number from the google spread sheet and checks if it is equaled to that, can someone tell me where I'm going wrong here?
def identify(number):
sht = gc.open("Test")
wks3 = sht.worksheet("NAMES")
b = wks3.acell('B'+str(number)).value
a = wks3.acell('A'+str(number)).value
#because you did, Id = ?
if(Id == a and conf<65):
print(Id, conf)
Id = str(b)
Time = time.ctime()
hour(number)
elif(conf>64):
print(conf)
Id = "Unknown"
Because you did, variable Id isn't passed as any parameter or global/local variable or as an argument to existing class.
If Id was parameter:
def identify(number,Id):
If Id was global variable:
def identify(number):
global Id
If Id was local variable:
def identify(number):
id = None # or some other data type
And if Id was argument from some class:
some_class.Id
In short you referenced Id before it was initialised. This is rookie mistake and there is some stuff where you can actually init a variable in if elif else statement but you need to trow a none of above logic of the rule.
if True: Id = 2; elif False: Id = 3; else: Id =0 #this is pseudocode, don't paste it in.
Also have in mind that next variable is also Unbound conf
EDIT:
Often to avoid this problem we write code like this:
def somefunction(parm1,parm2... ):
# global variables : description for variable stack is optional
global var1,var2 # if needed
#local variables
var3,var4 = None;
var5 = 'something else'
#in body functions : functions inside functions or just general program functions
def a(... ): return ...
#body : actually what function/program does.
# returning , finishing statement.

How can I use an 'update' function to 'open' and update if the object is not yet open?

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

Python alter external variable from within function

When I run this it works, but it says
"name 'select_place' is assigned to before global declaration"
When I get rid of the second global, no comment appears, but as select_place is no longer global it is not readable (if selected) in my last line of code.
I'm really new to python, ideally I'd like a way of not using the global command but after searching i still can't find anything that helps.
My code:
def attempt(x):
if location =='a':
global select_place
select_place = 0
if location =='b'
global select_place
select_place = 1
place = ([a,b,c,d])
This is the start of some turtle graphics
def Draw_piece_a(Top_right):
goto(place[select_place])
You need to declare the variable first, additionally the function code can be made clearer:
select_place = False
def attempt(x):
global select_place
if location == 'a':
select_place = 0
elif location == 'b':
select_place = 1
Also, there is no return value for attempt(), is this what you want?

Python FTP hangs in callback

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.

How to change global variables in Python [duplicate]

This question already has answers here:
Using global variables in a function
(25 answers)
Closed 3 years ago.
I am trying to change a variable further down the program. I have a global variable declared at the start of the program and I want to change the variable in different functions down the program. I can do this by declaring the variable inside the function again but I would like to know is there a better way of doing this. Some test code is below to explain what I mean.
ID = 'No'
project = ("Yep"+ID) # ID added with 'No' value which I later want to change
def pro():
ID = "YES"
print ID
def pro1(ID):
# I could declare project again to get this to work, but I would like to avoid this
print project # I want this to print with the new ID number.
if __name__ == '__main__':
pro()
pro1(ID)
Has anyone any ideas, thanks
I have tried the global variable but when I do this the project variable still prints out YepNo instead of YepYES. I want the new variable from the function proto change the variable in the project variable.
To update global variables you could use
global ID
ID="Yes"
before assigning variable to ID = "YES"
But changing ID will be no effect on project variable, project = ("Yep"+ID), because project is already a string
you need to make a function like
def getprojectname(ID):
return project+ID
The whole program may be like this
UPDATE:
... removed
Beware, you're doing it wrong multiple times.
Even though you could use the global statement to change a global (it is discouraged since it's better to use function parameters and return values), that would NOT change other already assigned values. E.g. even though you reassign ID, you would NOT reassign project. Also: your functions return nothing, there's no point in assigning a name to their return value, and it's a BAD habit using an all-uppercase name (ID) for a variable since it's a convention to use them for constants.
This should clarify you the way global works:
myid = ''
project = ("Yep"+myid) #ID added with no value which I later want to change
def mutate_id():
global myid
myid = "YES"
def mutate_project():
global project
project = ("YEP" + myid)
if __name__ == '__main__':
print "myid", myid
print "project ", project
print
mutate_id()
print "myid", myid
print "project ", project
print
mutate_project()
print "myid", myid
print "project ", project
print
But the best way is to do WITHOUT globals:
def get_new_id(old):
return "YES"
def get_new_project(old):
return ("YEP" + myid)
if __name__ == '__main__':
myid = ''
project = ("Yep"+myid)
print "myid", myid
print "project ", project
print
myid = get_new_id(myid)
print "myid", myid
print "project ", project
print
project = get_new_project(project)
print "myid", myid
print "project ", project
print
This will make all code interaction clear, and prevent issues related to global state change.
Use the global statement.
The global statement is a declaration which holds for the entire current code block. It means that the listed identifiers are to be interpreted as globals.
Example: http://www.rexx.com/~dkuhlman/python_101/python_101.html#SECTION004340000000000000000
P.S.
But don't use global too often, see http://www.youtube.com/watch?v=E_kZDvwofHY#t=10m45
In your code you have two problems. The first about changing ID variable, which could be solved by using global.
The second that your code calculate project string and after that project don't know about ID.
To avoid code duplication you can define function to calc project.
So we have:
ID = 'No'
def GetProject():
return "Yep"+ID
def pro():
global ID
ID = "YES"
print ID
print GetProject()
pro()
print GetProject()
You can mutate without reassigning:
variables = {}
def pro():
if variables['ID'] == '':
variables['ID'] = 'default'
Why not use a dictionary?
>>> attr = {'start':'XXX', 'myid':'No'}
>>>
>>> def update_and_show(D, value = None):
... if value: D['myid'] = value
... print D['start'] + ' ' + D['myid']
...
>>> update_and_show(attr)
XXX No
>>> update_and_show(attr, 'Yes')
XXX Yes
>>> update_and_show(attr, 'No')
XXX No
>>>

Categories