confusing behavior with python import - python

Hi, there.
I have two files:
a.py:
print('in a')
import b
print('var')
VAR = 1
def p():
print('{}, {}'.format(VAR, id(VAR)))
if __name__ == '__main__':
VAR = -1
p()
b.p() # Where does this VAR come from?
b.py:
print('in b')
import a
def p():
a.p()
I don't understand why there're two different VARs, which is supposed to the same.
If I move the 'main' block to another file, everything works well, i.e, there is only one VAR.
c.py:
import a
import b
if __name__ == '__main__':
a.VAR = -1
a.p()
b.p()
So my question is:
Why do the last two lines of a.py print different results?
Don't they print the same VAR variable in a.py?
BTW, I'm using python 2.7 on win7.
Thanks.

You might want to read up on global variables? To quote:
If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’.
Edit: to elaborate, here's what happens (leaving out c.py for clarity):
File a.py is executed.
File b.py is imported, and in turn imports a.py again.
Via b's import, VAR is defined in with a value of 1. This ends b`s import.
The __main__ part in a.py is executed in its own scope; VAR is set to -1 there, and p() is ran: this displays -1, as VAR was just set to.
Then b.p() is executed, which in turn runs a.p(). Because VAR from the perspective of b.py (different scope) still has a value of 1, the print statement just outputs 1.
You'll also notice that the id's are different in both print statements: this is because the VAR's live in a different scope, and are not connected in any way. They are disconnected because __main__ lives in a different, anonymous scope: the scope in which the Python interpreter executes. This is briefly discussed in the docs.

Related

Visibility of global variable declared in imported module

I just bumped into an unexpected (at least, for me!) behavior, and I'm trying to understand it.
Let's say I have a main file:
main.py
from my_packages.module_00 import my_function
def main():
my_function()
if __name__ == "__main__":
main()
and, in the folder "my_packages", the module module_00 containing the function definition for "my_function" and a global variable:
module_00.py
global_var = 'global variable'
def my_function():
print(f'Do I know {global_var}???')
When I run main.py, it outputs:
Do I know global variable???
And I'm trying to figure out why it's working.
I would expect the variable global_var to have a scope limited only to the module where it's defined (the answer to this question seems to confirm it).
Basically, I assumed that importing my_function by
from my_packages.module_00 import my_function
was equivalent to copy/pasting the function definition in main.py. However, it seems that...the imported function somehow keeps track of the global variables declared in the module where the function itself has been defined?
Or am I missing something?
However, it seems that...the imported function somehow keeps track of the global variables declared in the module where the function itself has been defined?
That's exactly what it is doing.
>>> from module_00 import my_func
>>> my_func.__globals__['global_var']
'global variable'
>>> module_00.global_var is my_func.__globals__['global_var']
True
>>> module_00.global_var = 3
>>> my_func.__globals__['global_var']
3
__globals__ is a reference to the global namespace of the module where my_func was defined.
Fix a couple of typos and your code works as expected. Each module in Python has its own private symbol table for the module's global variables.
import in main.py must be my_func not my_function matching the function name as defined in module_00.py.
ref in f-string in module_00.py must be {global_var} not {global variable}.
main.py
from my_packages.module_00 import my_func
^^^^^
def main():
my_func() # my_func not my_function
if __name__ == "__main__":
main()
module_00.py
global_var = 'global variable'
def my_func():
print(f'Do I know {global_var}???')
^^^^^^^^^^
Output:
Do I know global variable???
If you want to access a module's global variable then you can import the module and access the variable with the syntax: package.module_name.variable_name; e.g. my_packages.module_00.global_var

How to share global variable across the files in python?

I am trying to find a way to share a variable between multiple python scripts. I have the following code:
b.py
my_variable = []
a.py
from b import my_variable # import the value
def run():
global x
x = my_variable.append("and another string")
print(my_variable)
if __name__ == '__main__':
run()
c.py
import a
print(a.x)
a.py runs just fine without giving any error. However, when I run the c.py file, it gives off the following error:
Traceback (most recent call last):
File "E:/MOmarFarooq/Programming/Projects/Python Projects/Variables across files/c.py", line 2, in
<module>
print(a.x)
AttributeError: module 'a' has no attribute 'x'
What I want the code to do is, print the new value of my_variable after it has been changed in a.py . Is there any way I can do that?
the error occurred because you never called the run function from a.py. The if __name__=='__main__': statement is only satisfied if you are running a.py as a program, not importing it as a module.
So a.py should be
from b import my_variable # import the value
def run():
global x
x = my_variable.append("and another string")
print(my_variable)
run()
Note that x will be set to None because the append function does not return anything. It just appends a value to a list.
You need to call the run() function in your c.py file.
Here's how the code should be:
import a
a.run()
Well, module 'a' does have no attribute 'x'. Module 'a' has a function that creates a variable 'x', but as long as the method isn't called, the attribute isn't there.
You could change file c.py to:
import a
a.run()
print(a.x)
Another solution would be to make sure that the run() function is always called when importing module 'a'. This is currently not the case, because of the line if __name__ == '__main__':.
If you don't want to run the code but only want to make sure the variable exists, just define it in the module. Before the definition of your run() method, just add x = None (or use any other initial value you prefer).
Note, however, that there other problems with your code and that using globals in this way is a really bad programming pattern, which will likely lead to other problems later on. I wonder what you want to achieve. It probably would be a better solution if you could pass x as argument to the run() function instead of referring to a global variable. But that's outside the scope of this question and difficult to answer without more information.

Get variable from file which is importing my function in python

I have 2 files:
Main.py
from test import test
def main():
sol = 'hello'
if test() == sol:
print('Yes')
else:
print('No')
test.py
def test():
return 'hello'
Is there a way to access the sol variable in my test function which is in another file? I'd like to do something like this:
def test():
return sol
This way it's always the same as the variable in Main.py. I tried a solution which is mentioned on Python extract variables from an imported file but it didn't work for me. Thank you for any help
Edit: I'd like to do it without changing the Main.py file
Since sol isn't defined in the function you will need to declare it as a global function so it can be used in the function. Change test.pyto the following...
test():
global sol
return sol
Hope that helps.
If sol is a constant (i.e. a variable which will not change during the course of the program), then you can put it into a separate file, say var.py, and import it into both main.py and test.py:
from var import sol
BUT what you will be importing is a copy of the variable with the value it had at the time it was imported - any subsequent reassignments will not update the value of sol in test.py and main.py. Because a string is immutable, when you reassign a value to it what you are actually doing is you are reusing the variable name for a new entity.
What you need to do is have your variable in a mutable structure, such as a list or a class, so your var.py will look like this:
class v(object):
sol = 'Hello'
and then in main.py and test.py you can refer to sol using:
from var import v
print(v.sol)
This way, any changes to v.sol will be correctly reflected anywhere class v is imported. A bit cumbersome, but that's how it is in Python.
You can use inspect module to get the sol variable from main function without change anything of main.py. Of course, you need to call main function.
Main.py:
from test import test
def main():
sol = 'hello'
if test() == sol:
print('Yes')
else:
print('No')
main()
test.py:
import inspect
def test():
frame = inspect.currentframe()
sol_from_main = 'default'
try:
sol_from_main = frame.f_back.f_locals['sol'] # line A
except:
print('No sol found')
print(sol_from_main) # this print 'hello'
return sol_from_main
Output:
hello
Yes
Explanation:
From the python doc, we can see next:
frame
f_back | next outer frame object (this frame’s caller)
f_locals | local namespace seen by this frame
So line A get the caller of current function, that is main function, and use f_locals to get all local variables of main function, so it can get the sol value from main. FYI in case you still need it.

Functions carry over from modules...why don't variables?

Say you define a function (def func) and a variable (var = ) in a module.
When you import that module to your main.py, the function carries over (ie is able to be used) but the variable does not carry over (ie you have to reassign the var in main.py in order to use it in main.py).
Is there a reason why this happens? and is there a workaround?
All Functions AND variables in module scope will be imported. If the variable is initialized or assigned to inside a function, you may not see that value until that function has been executed. To make sure you get the most current value of the variable, you may have to use module_name.variable_name rather than just variable_name.
Let us say you have two source files t1.py and t2.py
t1.py
v1 = 10
def f1():
global v1
v1 = 11
t2.py
from t1 import f1, v1
f1()
print(v1)
If you run t2.py you will still see 10. But if you change t2.py as follows:
import t1
t1.f1()
print(t1.v1)
You will see 11.
There is an added twist. If v1 were a list or dictionary (or object), you don't need the additional module_name qualifier. They are accessed by reference so you will see the value even without it.

Global variable with imports

first.py
myGlobal = "hello"
def changeGlobal():
myGlobal="bye"
second.py
from first import *
changeGlobal()
print myGlobal
The output I get is
hello
although I thought it should be
bye
Why doesn't the global variable myGlobal changes after the call to the changeGlobal() function?
Try:
def changeGlobal():
global myGlobal
myGlobal = "bye"
Actually, that doesn't work either. When you import *, you create a new local module global myGlobal that is immune to the change you intend (as long as you're not mutating the variable, see below). You can use this instead:
import nice
nice.changeGlobal()
print nice.myGlobal
Or:
myGlobal = "hello"
def changeGlobal():
global myGlobal
myGlobal="bye"
changeGlobal()
However, if your global is a mutable container, you're now holding a reference to a mutable and are able to see changes done to it:
myGlobal = ["hello"]
def changeGlobal():
myGlobal[0] = "bye"
I had once the same concern as yours and reading the following section from Norman Matloff's Quick and Painless Python Tutorial was really a good help. Here is what you need to understand (copied from Matloff's book):
Python does not truly allow global variables in the sense that C/C++ do. An imported Python module will not have direct access to the globals in the module which imports it, nor vice versa.
For instance, consider these two files, x.py,
# x.py
import y
def f():
global x
x = 6
def main():
global x
x = 3
f()
y.g()
if __name__ == ’__main__’:
main()
and y.py:
# y.py
def g():
global x
x += 1
The variable x in x.py is visible throughout the module x.py, but not in y.py. In fact, execution of the line
x += 1
in the latter will cause an error message to appear, “global name ’x’ is not defined.”
Indeed, a global variable in a module is merely an attribute (i.e. a member entity) of that module, similar to a class variable’s role within a class. When module B is imported by module A, B’s namespace is copied to A’s. If module B has a global variable X, then module A will create a variable of that name, whose initial value is whatever module B had for its variable of that name at the time of importing. But changes to X in one of the modules will NOT be reflected in the other.
Say X does change in B, but we want code in A to be able to get the latest value of X in B. We can do that by including a function, say named GetX() in B. Assuming that A imported everything from B, then A will get a function GetX() which is a copy of B’s function of that name, and whose sole purpose is to return the value of X. Unless B changes that function (which is possible, e.g. functions may be assigned), the functions in the two modules will always be the same, and thus A can use its function to get the value of X in B.
Python global variables are not global
As wassimans points out above they are essentially attributes within the scope of the module they are defined in (or the module that contains the function that defined them).
The first confusion(bug) people run into is not realizing that functions have a local name space and that setting a variable in a function makes it a local to the function even when they intended for it to change a (global) variable of the same name in the enclosing module. (declaring the name
in a 'global' statement in the function, or accessing the (global) variable before setting it.)
The second confusion(bug) people run into is that each module (ie imported file) contains its own so called 'global' name space. I guess python things the world(globe) is the module -- perhaps we are looking for 'universal' variables that span more than one globe.
The third confusion (that I'm starting to understand now) is where are the 'globals' in the __main__ module? Ie if you start python from the command line in interactive mode, or if you invoke python script (type the name of the foo.py from the command shell) -- there is no import of a module whose name you can use.
The contents of 'globals()' or globals().keys() -- which gives you a list of the globals -- seems to be accessible as: dir(sys.modules['__main__'])
It seems that the module for the loaded python script (or the interactive session with no loaded script), the one named in: __name__, has no global name, but is accessible as the module whose name is '__main__' in the system's list of all active modules, sys.modules

Categories

Resources