Accessing global variable - python

All I want is to assign some initial value to the variable 'extra_devices' and if the user specifies some value to this variable at the run time, the default value gets replaced with the user's specified value. I am adding a minimal code snippet to show what I am doing.
While running this program, if I do not specify 'extra_devices', the program fails to run saying 'UnboundLocalError: local variable 'extra_devices' referenced before assignment', but I fail to understand the reason since I have already assigned the value to it. However, the program works fine if I specify 'extra_devices' at runtime. Anyone got any reasoning for this behavior?
Notice variable 'abc' prints fine in main().
#/usr/bin/python
import argparse
extra_devices=10
abc = 1
def main():
parser = argparse.ArgumentParser(description='')
parser.add_argument('-extra_devices','--extra_devices',help='extra_devices',required=False)
args = parser.parse_args()
if args.extra_devices is not None: extra_devices = args.extra_devices
print "abc="+str(abc)
print "extra_devices = "+str(extra_devices)
if __name__ == '__main__':
main()

Add one line in the function:
global extra_devices
Then you can write to the global variable in the function.
The reason is because you may change that variable in the function, and the interpreter will define it as a local variable instead of global variable for protecting the global variable, except you assign that variable is a global variable.
Update:
Add the reason.

Related

Discrepancy in using global variable in a function in Python

I was just playing around with Python and I came across something interesting which I didn't quite understand. The code goes as follows:
a = 1
def function():
print(a)
function()
print(a)
Here, a is a global variable and I used it in my function and the result was:
1
1
I was able to use a global variable locally in my function without having to use global a in my function.
Then, I experimented further with this:
a = 1
def function():
a = a+1
print(a)
function()
print(a)
When I ran this code, an error showed up and it said that the local variable a was referenced before assignment. I don't understand how before it recognized that a was a global variable without global a
but now I need global a like this
a = 1
def function():
global a
a = a+1
print(a)
function()
print(a)
in order for this code to work. Can someone explain this discrepancy?
You can read the value from a global variable anytime, but the global keyword allows you to change its value.
This is because when you try and set the a variable in your function, by default it will create a new local function variable named a. In order to tell python you want to update the global variable instead, you need to use the global keyword.
When creating "=" ,a new variable inside a function python does not check if that variable is a global variable, it treats that new variable as a new local variable, and since you are assigning it to itself and it does not exist locally yet, it then triggers the error

variable not passed to lower level?

I understand that variables in python are always (?) available in subfunctions.
That is why this works:
def abc():
print(a)
def main():
abc()
pass
if __name__=='__main__':
a = 'a'
main()
However, why does it not work if the definition of ais moved to main()?
def abc():
print(a)
def main():
a = 'a'
abc()
pass
if __name__=='__main__':
main()
It is still defined before it is used and should be available in abc(), right?
No, variables defined in a function are scoped to the function by default. To make it available in abc() you would need to make it global:
def main():
global a
a = 'a'
abc()
As for the 'shadowing' warning, you get it when you redefine names at inner scopes. For example, this code creates a global variable a:
if __name__ == '__main__':
a = 'a'
main()
Now if you do a = 'b' inside main() you are effectively 'shadowing' a from the outer scope by creating a local variable with the same name.
In the first code sample, 'a' is defined as a global variable. Meaning that it can be accessed by any function called after it is instantiated.
In the second code sample, 'a' is defined as a local variable. Meaning that it only exists within the "main" function. If you wanted to pass it into the "abc" function, you would have to explicitly pass that variable. That would look like:
def abc(a):
print(a)
def main():
a = 'a'
abc(a)
pass
if __name__=='__main__':
main()
Variables at the module level of your function are available to read anywhere else in the module. When you do
if __name__=='__main__':
a = 'a'
you are defining a as a module-level variable, hence why you can read it inside abc(). This is similar to why you can see your imports within other functions.
When you define a inside abc(), it's no longer at the module-level and no longer is implicitly readable elsewhere in the module.
As an aside, global variables are usually a bad idea, it's better to pass them around explicitly. See http://wiki.c2.com/?GlobalVariablesAreBad for a good summary of why.

I am getting the error 'redefined-outer-name'

When running my lint, I am getting the error below:
Redefining name 'tmp_file' from outer scope (line 38) (redefined-outer-name)
Here is my snippet of code in that line:
tmp_file = open('../build/' + me_filename + '.js','w')
That happens because you have a local name identical to a global name. The local name takes precedence, of course, but it hides the global name, makes it inaccesible, and cause confusion to the reader.
Solution
Change the local name. Or maybe the global name, whatever makes more sense. But note that the global name may be part of the public module interface. The local name should be local and thus safe to change.
Unless... your intention is for these names to be the same. Then you will need to declare the name as global in the local scope:
tmp_file = None
def do_something():
global tmp_file # <---- here!
tmp_file = open(...)
Without the global declaration, the local tmp_file will be unrelated to the global one. Hence the warning.
Solution
Create main() function which be holding all the main logic etc.
def pow(x):
return x ** 2
def add(x, y):
return x + y
def main():
x, y = 2, 4
print(pow(x))
print(add(x, y))
if __name__ == '__main__':
main()
Explanation
This works, because every new function instance creates a new local scope.
Open with with
Apart from #Rodrigo's correct answer about scopes: if your tmp_file is just that, a temporary file, you can use
with open('../build/' + me_filename + '.js','w') as tmp_file:
# do something
in both cases. It clearly defines where your tmp_file is going to be used.
It is the recommended way of dealing with variables whose scope needs to be clearly bounded.
Error description
Pylint has a built-in description:
pylint --help-msg=redefined-outer-name
gives
:redefined-outer-name (W0621): Redefining name %r from outer scope
(line %s) Used when a variable's name hide a name defined in the
outer scope. This message belongs to the variables checker.
You get this error if you have defined the same variable in multiple places like outside the def and inside the def.
If you are using the single variable define it as global variable_name and use global keyword all the places. Else please rename the other variables.

Unbound Local Error

I keep getting an unbound local error with the following code in python:
xml=[]
global currentTok
currentTok=0
def demand(s):
if tokenObjects[currentTok+1].category==s:
currentTok+=1
return tokenObjects[currentTok]
else:
raise Exception("Incorrect type")
def compileExpression():
xml.append("<expression>")
xml.append(compileTerm(currentTok))
print currentTok
while currentTok<len(tokenObjects) and tokenObjects[currentTok].symbol in op:
xml.append(tokenObjects[currentTok].printTok())
currentTok+=1
print currentTok
xml.append(compileTerm(currentTok))
xml.append("</expression>")
def compileTerm():
string="<term>"
category=tokenObjects[currentTok].category
if category=="integerConstant" or category=="stringConstant" or category=="identifier":
string+=tokenObjects[currentTok].printTok()
currentTok+=1
string+="</term>"
return string
compileExpression()
print xml
The following is the exact error that I get:
UnboundLocalError: local variable 'currentTok' referenced before assignment.
This makes no sense to me as I clearly initialize currentTok as one of the first lines of my code, and I even labeled it as global just to be safe and make sure it was within the scope of all my methods.
You need to put the line global currentTok into your function, not the main module.
currentTok=0
def demand(s):
global currentTok
if tokenObjects[currentTok+1].category==s:
# etc.
The global keyword tells your function that it needs to look for that variable in the global scope.
You need to declare it global inside the function definition, not at the global scope.
Otherwise, the Python interpreter sees it used inside the function, assumes it to be a local variable, and then complains when the first thing that you do is reference it, rather than assign to it.

Global variable causing an error

So I am trying to declare the variable "checks" as a global variable because I get the following issue:
File "C:\Python27\Projects\Automatic Installer\autoinstall.py", line 11, in installFunc
if checks[0] == 1:
NameError: global name 'checks' is not defined
Here's my code, I've tried to add global checks to both the main body of the program as well as the installFunc function. Is there another location I should be adding it/some other way to indicate that checks should contain the information in the program?
import urllib
import subprocess
from Tkinter import *
global checks
def installFunc():
global checks
subprocess.call("md c:\MGInstall", shell=True)
subprocess.call (u"net use w: \\it01\files")
if checks[0] == 1:
subprocess.call(u"w:\\software\\snagitup.exe")
if checks[1] == 1:
subprocess.call(u"w:\\software\\camtasia.exe")
if checks[2] == 1:
urllib.urlretrieve(u"SUPERLONGURLLOLOLOL", u"c:\\MGinstall\\gotomeeting.exe")
subprocess.call (u"c:\\MGinstall\\gotomeeting.exe")
urllib.urlretrieve(u"http://ninite.com/.net-7zip-air-chrome-cutepdf-dropbox-essentials-firefox-flash-flashie-java-klitecodecs-quicktime-reader-safari-shockwave-silverlight-vlc/ninite.exe", u"c:\\MGinstall\\MGinstall.exe")
subprocess.call (u"c:\\MGinstall\\MGinstall.exe")
subprocess.call (u"w:\\printers\\installer\\printer.exe")
app = Tk()
w = Label(app, text="CompanyName IT Automatic Installer")
w.pack()
text = ["Snagit", "Camtasia", "GotoMeeting"]
variables = []
for name in text:
variables.append(IntVar())
Checkbutton(text=name, variable=variables[-1]).pack()
b = Button(text="OK", command=installFunc)
b.pack()
app.mainloop()
checks = [variable.get() for variable in variables]
I think this is because checks gets set after the mainloop (the last line of the posted code). the function installFunc gets called from the mainloop via a button press, but checks hasn't been defined yet.
Using the global data in this case isn't a good idea anyway. You should probably do something like:
def installFunc(checks):
...
checks = [variable.get() for variable in variables]
b = Button(text="OK", command=lambda : installFunc(checks))
Or, even better, wrap all this up in a class... that way you can do:
self.b=Button(..., command=self.installFunc)
Replace the first 'global checks' (the one at the global level) with 'global = ...', initializing it appropriately. Using 'global' is really relevant only within a function/method. Per the Python docs: 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. It would be impossible to assign to a global variable without global, although free variables may refer to globals without being declared global. You may want to read this as well - has lots of relevant info: Using global variables in a function other than the one that created them
The problem is not the first 'global checks'. What causes the error is that checks is accessed before it is initialized.
You must initialize checks before calling the main loop of the application.
First of all, Python is not PHP.
You need to use keyword global if an only if, you're going to assign to global variable within scope of a function.
global checks at top level makes no sense at all, and more importantly does not define that variable.
global checks in your installFunc() does not make sense, as you do not assign anything to that variable, in fact you don't even modify it.
In Python variables from outer scope are visible in local scope, unless of course you try to assign something, which would be interpreted as redefining that variable within local scope.
Problem with your code is that you only define checks after exiting main loop. Correct code would look like this
import urllib
import subprocess
from Tkinter import *
#no global definition here...
def installFunc():
#no global definition here...
subprocess.call("md c:\MGInstall", shell=True)
...
...
#define checks before starting main loop
checks = [variable.get() for variable in variables]
app.mainloop()

Categories

Resources