Proper Python Global Variable Use - python

I believe I have some fundamental misunderstanding of Python global variables and their scope, and I was hoping someone can educate me.
Say I have two Python files.
#"GlobalSet.py"
global myVar
myVar = True
print "myVar" in globals()
import GlobalCheck
and
#"GlobalCheck.py"
print "myVar" in globals()
Running "GlobalSet.py" surprisingly results in
True
False
Why isn't "myVar" in the global scope within "GlobalCheck"?

Global in Python means global to the current module. To share variables between modules you need to import them.
Note that the global keyword in your code is doing nothing at all, since myVar is already defined at module level. You would only need to use that keyword if you were modifying the value of myVar inside a function in that module.

The global is within the context of the module. In GlobalCheck.py, if you put
import GlobalSet
print GlobalSet.myVar
that will work. (globals() doesn't appear to work across modules.)

Related

How come python functions can access variables that are not defined in the arguments?

For example if I write
a=1
def func():
return a
func()
It will return 1. Is this normal behavior? Are variables supposed to act in a global way in python?
If you create a variable with the same name inside a function, this variable will be local, and can only be used inside the function. The global variable with the same name will remain as it was, global and with the original value.
That is the way python language works !

Why does python behave this way with variables? [duplicate]

This question already has answers here:
UnboundLocalError trying to use a variable (supposed to be global) that is (re)assigned (even after first use)
(14 answers)
Closed 5 months ago.
I have been trying to understand why python behaves this way, in the block of code below. I have done my research but couldn't find a good answer so I came here to see if anyone can point me in the right direction or provide a good clarification.
I understand that it has to do with some old ALGOL principle, but I don't fully understand it.
var = 5
def func1():
print(var)
func1()
def func2():
var = 8
print(var)
func2()
def func3():
print(var)
var = 8
func3()
The output of this code is as follows:
5
8
UnboundLocalError: local variable 'var' referenced before assignment
I understand why we get the outputs '5' and '8'. But with 'func3()', I was expecting an output of '5'. As it seems, the interpreter thinks that I want to print the local 'var' in the function instead of the global 'var'. So it throws this error.
Or maybe if a variable is defined somewhere inside of the function, then the function will default to the local variable, instead of a global one with the same name.
But why exactly does python behave this way ? I am not complaining, I am just trying to understand something...
How could I use a predefined global variable in a function, then define a local variable with the same name inside of the same function, without changing the value of the global variable ? ( in python of course )
Thanks in advance to everyone here. You are amazing people ! :)
Edit_1: Thanks every one for the great answers. I totally understand that it is a bad and unpractical idea to use a predefined global variable in a function, then define a local variable with the same name inside of the same function. I was just thinking about it from a theoretical perspective, because I saw it in a college lecture. XD
I can't find a single use case, in which it would be optimal to do that either !
Edit_2: I already read the PEP8 and I know that being explicit is better than being implicit. :)
It's true. Otherwise the code will be confusing and lead to bugs.
That question was just about some useless and impractical college theory that I was trying to understand.
Edit_3:
Now I fully understand why it happens and what is going on here. Thanks to Randall Valenciano for providing this link to a blog that explains it very well.
What happens is that the function is interpreted as a whole, and not line by line. So when the function is being interpreted, the variable declarations of any defined variables, are moved to the top of the function. So when we are printing 'var', the function is using the locally declared variable that doesn't have any value assigned to it yet, and then the interpreter complains about it and throws and error.
Thanks to all of you again ! :)
You have been of great help to me ! Now I finally understand what is going on there under the hood.
Your var is defined as a global variable. In each function when you're only reading the var you're accessing the global var, but the moment there's an assigned value to var somewhere in the function, python treats every var within the function as a local variable. Thus why your last function failed, because print(var) (the local varaible) was called before var = 8 was assigned.
You can read up a bit about more in these threads:
How bad is shadowing names defined in outer scopes?
Python nonlocal statement
The best thing to do is be explicit about your code so it's no longer confusing if you're trying to reference a local, nonlocal or global variable.
In this case, assuming your intention is to keep using the global var, do this:
var = 5
def func1():
print(var)
func1()
def func2():
global var
var = 8
print(var)
func2()
def func3():
global var
print(var)
var = 8 # technically this is not necessary any more var = 8 was already assigned when func2() is called
func3()
The output is thus:
5
8
8
Edit: Thanks to juanpa.arrivillaga's comment - I missed your original question.
How could I use a predefined global variable in a function, then
define a local variable with the same name inside of the same
function, without changing the value of the global variable ? ( in
python of course )
The short answer is - define the local var first like you did in func2() and you're good. The longer answer though is - why would you want to do that? It creates confusion and becomes a headache to track which is when you have variables of the same name in different scope. A better approach would be name your local var to be local_var or something so it's distinctly different and easily traced.
Here's a rule for Python Scope Resolution from this answer
LEGB Rule.
L, Local — Names assigned in any way within a function (def or lambda)), and not declared global in that function.
E, Enclosing-function locals — Name in the local scope of any and all statically enclosing functions (def or lambda), from inner to outer.
G, Global (module) — Names assigned at the top-level of a module file, or by executing a global statement in a def within the file.
B, Built-in (Python) — Names preassigned in the built-in names module : open,range,SyntaxError,...
So basically in your question, the scope resolution is "from inside out" and since you aren't using the global keyword, the interpreter doesn't know to look outside the local function scope to find that variable var. All the interpreter sees is that you are using a variable before declaring and defining it, thus throwing the error. Global variables are often dangerous, and so Python wants to make sure you know you want to use a global variable by forcing you to be explicit about it.
See this other answer for an explanation of the global keyword
Hope it helps.

importing other files in python and global variables

I cannot quite find a good description on how import works when importing your own files.
I was having trouble importing a file with a global variable and managed to get it to work when I put the global variable just before the files main function.
Can someone explain why it works this way?
A quick run down on how import actually works.
It did not work when I did this (pseudocode):
file1:
import file2
file2.main()
file2:
main():
glob_var = 0
def add():
global glob_var
glob_var += 1
add()
But worked if I put the variable first like this:
file1:
import file2
file2.main()
file2:
glob_var = 0
main():
def add():
global glob_var
glob_var += 1
add()
'main' is just a method. Variable inside a method is local by definition. Thats why 2nd way is working.
The reason it didn't work is because you are declaring the global variable inside of main. (you did miss the colon after definition of main which makes it confusing but looking at the indentation I suppose it's a definition). Global variables have to be defined outside the scope of any local function. This is a case of nested function definition.
You can do without global variables as well if that's what you are looking for. If however you want to use the variable defined in main inside a nested function then you can do the following:
In python 3 there is a way to get this thing done however using the nonlocal keyword
def main():
var = 10
def nested_fun():
nonlocal var
var = var + 1
As you see we do not need a global variable here.
Edit: In case of python 2 this does not work. However you can use a list in the main and modify that list inside nested function.
def main():
var = [10]
def nested_fun():
nonlocal var
var[0] = var[0] + 1
If I understand the question correctly, it's regarding the global variable and have nothing to do with importing.
If you want to define a global variable, it has to be defined in module level. The main function is not the global scope, that's why the first code does not work as expected. By moving the variable declaration outside of main, it would be defined in global scope, so the add method can access it using as a global variable.
I think we have to begin with what global statement means. From the docs:
It means that the listed identifiers are to be interpreted as globals.
The important point is it's only about interpretation, i.e. global does not create anything. This is the reason, why your first example does not work. You are trying to add +1 to something not existing. OTOH, global_var = 100 would work there and it would create a new global variable.

UnboundLocalError local variable referenced before assignment

When I run this code, I get an error that says UnboundLocalError: local variable 'rootent' referenced before assignment.
class calculator():
def __init__(self):
def options():
fetch=float(rootent.get()) #Location of error
if fetch=='1':
def IEntry():
fetch=float(rootent.get())
fetch1=float(rootent1.get())
answer=fetch,'+',fetch1,'=',fetch1+fetch2
ansLabel=Label(root,text=answer).pack()
root=Tk()
root.title('Addition')
root.geometry('450x450+200+200')
rootlabel=Label(root,text='Enter first number').pack()
rootent=Entry()
rootent.pack()
rootlabel1=Label(root,text='Enter second number').pack()
rootent1=Entry()
rootent1.pack()
return
root=Tk()
root.title('Calculator Menu')
root.geometry('450x450+200+200')
rootlabel=Label(root,text='1.Addition').pack()
rootlabel1=Label(root,text='2.Subtraction').pack()
rootlabel2=Label(root,text='3.Multiplication').pack()
rootlabel3=Label(root,text='4.Division').pack()
rootent=Entry(root) #This is what i am trying to input into 'def options()'
rootent.pack()
rootbutton=Button(root,text='Enter option',command=options).pack()
I have tried making rootent global in the function, and I've tried passing it as a para with no luck.
What is wrong, and how do I fix it?
The problem is that python scoping rules are a bit strange. If a function has an assignment to a variable, that variable is assumed local to the function and python won't look in enclosing scopes. In your case, the offending line is rootent=Entry(). your call to rootent.get() is trying to access this rootent variable before it has been assigned. Since you are in python 3.x you can use the nonlocal declaration to make python access the outer scope. Just put nonlocal rootent at the beginning of options() and I think it will work correctly.
There is more discussion of the use of the nonlocal operator here: Python nonlocal statement
Basically, global tells python that the variable name in question resides at the module (file) level. nonlocal tells python to search enclosing scopes for the named variable and use that version, which is more like the behavior you get 'by default' in other languages where you have to explicitly declare all variables.
You are trying to get something from a variable that has not been previously declared. Where is the rootent variable declared in your code?. You need to show us that. It is hard to guess what the type of rootent is.
Thanks for the help, it's now fixed, added nonlocal and changed float(rootent.get()) to rootent.get() and it seemed to fix everything.

Assigning an argument to a global variable with the same name

How can I assign a function argument to a global variable with the exact same name?
Note:
I can't do self.myVariable = myVariable because my function is not inside of a class.
When write the following code, I get an error saying that "argument is both local and global."
myVar = 1
def myFunction(myVar):
global myVar
Is this impossible? and if so is it uncommon in other languages? Coming from java I'm used to this.myVar = myVar
Edit I already know that I can rename the variable. That's the easy way out.
Best solution: refactor your code. Rename something, or use an object with object properties instead of a global variable.
Alternative, hacky solution: modify the global variable dict directly:
my_var = 1
def my_function(my_var):
globals()['my_var'] = my_var
The answer is to trick Python into doing it. In your code:
myVar = 1
def myFunction(myVar):
global myVar
myFunction is never run (you never call it). So, the script never gets to the global myVar point. Yet, Python still blows up for some reason. I believe that that is because it has been told "Don't allow this". Regardless, you can trick it into doing what you want. One way that hasn't been suggested is to use a simple function in myFunction:
myVar = 1
def myFunction(myVar):
def trick(arg):
global myVar
myVar = arg
trick(myVar)
print myVar # Comes back with 1
myFunction(22)
print myVar # Comes back with 22
May not be on one line, but you don't have to import anything and it is easier to read.
The best (and fastest) thing to do would be to take the easy way out and rename the variable(s). But if you insist, you could get around the naming conflict with this:
import sys
_global = sys.modules[__name__]
# or _globals = sys.modules[__name__].__dict__
myVar = 1
def myFunction(myVar):
_global.myVar = myVar # or _globals['myVar'] = myVar
print myVar # 1
myFunction(42)
print myVar # 42
Explanation:
Within a module, the module’s name (as a string) is available as the value of the global variable__name__. The namesys.modulesrefers to a dictionary that maps module names to modules which have already been loaded. IfmyFunction()is running, its module has been loaded, so at that timesys.modules[__name__]is the module it was defined within. Since the global variableMyVaris also defined in the module, it can be accessed using sys.modules[__name__].myVar. (To make its usage similar to java, you could name itthis-- but personally I think_globalis a better.)
In addition, since a module’s read-only__dict__attribute is its namespace -- aka the global namespace -- represented as a dictionary object,sys.modules[__name__].__dict__ is another valid way to refer to it.
Note that in either case, new global variables will be created if assignments to non-existent names are made -- just like they would be with the global keyword.

Categories

Resources