Python global variables and callbacks - python

I'm writing a program that involves callbacks called from another module, and which need to access a global variable.
It seems that changes assigned to the global variable are not seen in the callback function, which only sees the original assignment. I'm guessing due to the import from the other module.
What is the proper way to write this pattern?
First module:
# a.py
from b import runb
myGlobal=None
def init():
global myGlobal
myGlobal=1
def callback():
print myGlobal
def main():
init()
runb()
if __name__=='__main__':
main()
Second module:
#b.py
def runb():
from a import callback
callback()
I would expect this program to print '1', but instead it prints 'None'
EDIT:
init can only be called once (it is a simplification of a complex program)

Python imports the main module as __main__. When b.py imports a by its actual name, a new instance of the module is loaded under the name a. Each instance has its own myGlobal.
One solution is this:
#b.py
def runb():
from __main__ import callback
callback()
Another solution is to create a new main module. Import a there and make an explicit call to a.main().

If you do this, "from a import callback", then again "myGlobal=None" will be executed, making it to print "None"

The main() function is not called when you import the file as a module. __name__ == "main" is true only when a.py is executed directly.

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.

how to create top-level module function from another function?

Given a module mymodule with an __init__.py file how can I define a functions of mymodule from another function within that file?
__init__.py code outline:
def a():
THISMODULE.__dict__["b"] = lambda: print("this is not weird")
a()
b() # Expect "this is not weird" output
mymodule comes as a replacement for anothermodule and I want to make the code change as simple as replacing import anothermodule as thething with import mymodule as thething. Since I don't implement all the functions of anothermodule just yet, I would like to send the user a notification about it instead of crashing with "not defined".
__name__ is a module attribute set by the import machinery, which evaluates to the name of the current module. Adding this to the top of your file:
import sys
THISMODULE = sys.modules[__name__]
And the rest of what you already have should work correctly.

Making function variables from imported module available in iPython interactive namespace

I want to know how to make variables from functions in imported modules available in the IPython interactive namespace.
I have some example code which I want to run from another script so I set it up as run_test.py:
def run_test():
a = 5
if __name__ == "__main__":
run_test()
I import the module as follows and call the function:
import run_test
run_test.run_test()
How do I make variable 'a' available in the interactive namespace? The only way I can do it is to make 'a' a global and run run_test.py directly rather than importing it and calling the function.
Any pointers appreciated.
I believe this can be accomplished by returning locals and then updating locals.
def main():
# do things
return locals()
if __name__ == '__main__':
new_locals = main()
locals().update(new_locals)
This seems to work, though it feels like a hack, so perhaps it won't always.
A reasonable example of where you want this: If you want access to all the variables in a function's namespace, but don't want that function's variables accessible to other parts of a script (i.e., other function definitions).

How to import another external python module?

this is probably a dumb question, but wasnt too sure what else to do.
main.py
import module2
global x
hello="Hello"
x=module2.message()
x.say()
module2.py
class message:
def say():
print hello
When I print hello, i am referring to the hello variable in main.py however this method will return an error. Does anyone know the solution? (i would prefer not to pipe the hello variable into the function)
The only reliable solution is called encapsulation.
So, basically, you could change your code to look like that:
main.py
import module2
global x
hello="Hello"
x=module2.message(hello)
x.say()
module2.py
class message:
def __init__(self, hello):
self.hello = hello
def say():
print self.hello
Plus try to follow coding style of Python - life of you and future developers of your code will be easier.
Multiple options, but do note that one module cannot ever access the calling module directly.
Simply pass hello as a variable (def say(msg): print msg)
Pass all variables in main.py to module2: def say(g): print g['hello'] and say(globals())
Store it somewhere, then extract it when you need it.
Since main.py imports module2.py, you can access the globals defined in moule2 in main.
In your case since module2 is not importing main, so the globals in main is not accessed in module2.
one solution is that defined by #Tadeck
In this particular example, it's totally OK for module2.py to import main.py, there are some gotcha's though.
The most obvious is that main.py is probably being run from the command line, like python main.py, which has the effect of making that file think it's called __main__. You could import that module in module2, but that's sort of unusual; it's called "main.py" and you want to import main. Doing so will cause the module to be imported a second time.
For that to be OK, you have to arrange for importing the file to have no side effects unless it's imported as __main__. A very common idiom in python is to test that condition at the end of a module.
import module2
global x
hello="Hello"
def main():
x=module2.message()
x.say()
if __name__ == '__main__':
main()
And now it's just fine for module2.py to actually import main. On the other hand, importing variables from one module into another gets hard to predict when the imports can be recursive, you may not have that variable yet because the module is already trying to import you. On the other hand, it's always safe to refer to a variable in a module using dotted syntax. So your module2.py should be:
import main
class message:
def say():
print main.hello
which also makes it more obvious just where hello came from.

Categories

Resources