Python global variables don't seem to work across modules - python

Code
I'd like to use a global variable in other modules with having changes to its value "propagated" to the other modules.
a.py:
x="fail"
def changeX():
global x
x="ok"
b.py:
from a import x, changeX
changeX()
print x
If I run b.py, I'd want it to print "ok", but it really prints "fail".
Questions
Why is that?
How can I make it print "ok" instead?
(Running python-2.7)

In short: you can't make it print "ok" without modifying the code.
from a import x, changeX is equivalent to:
import a
x = a.x
changeX = a.changeX
In other words, from a import x doesn't create an x that indirects to a.x, it creates a new global variable x in the b module with the current value of a.x. From that it follows that later changes to a.x do not affect b.x.
To make your code work as intended, simply change the code in b.py to import a:
import a
a.changeX()
print a.x
You will have less cluttered imports, easier to read code (because it's clear what identifier comes from where without looking at the list of imports), less problems with circular imports (because not all identifiers are needed at once), and a better chance for tools like reload to work.

You can also add another import statement after changeX. This would turn the code from b.py into
from a import x, changeX
changeX()
from a import x
print x
This illustrates that by calling changeX, only x in module a is changed. Importing it again, binds the updated value again to the identifier x.

Also you can use mutable container, for example list:
a.py
x = ['fail']
def changeX():
x[0] = 'ok'
b.py
from a import changeX, x
changeX()
print x[0]

Related

good program design for global variables

I have a master program, in which I would like to use a global variable, accross all other programs. I can't find the right way to do it (I can only think of overcomplicated solutions).
So for instance I tried this:
#master_prgm py
from global_vars import x,update
global_vars.update(1)
#global_vars
x=0
def update(y):
x+=y
I thought x would be defined at the import of global_vars, but apparently it does not because I have the message "local variable 'x' referenced before assignment"
Would you see a nice way?
A variable in python is a name in some namespace bound to an object. If I were to write
x = "foo"
y = x
x = "bar"
we wouldn't expect y to be "bar". After all, it is a unique variable assigned the object from x.
from global_vars import x
is similar. We now have two variables (1) global_vars.x in the global_vars namespace and (2) a separate and unique x in the local module namespace. An equivalent import shows this more dramatically
from global_vars import x as a_different_x
If you want to share x, even when updated, keep referring to it in the original namespace
import global_vars
#global_vars
global_vars.x=0
def update(y):
global_vars.x+=y
print

Imported function can print value of variable without the variable being imported but i can't, why does this happen?

I have recently tried experimenting with importing files, this time I imported a function and I dont understand how the function can print the variable x's value but if I try to print x an error happens, can you explain why?
First.py
x=5
def a():
print(x)
Second.py
from First import a
a()
print(x)
>>>5
>>>NameError: name 'x' is not defined
I thought that either:
a() and print(x) both wouldn't work because I didn't import x as well
or that they would both work because variables inside a() would also be imported, I didn't expect this though
In First.py, you have defined two things: x and a.
When you run from First import a in Second.py, you explicitly stated that you want to import a and nothing else in the file.
Therefore, x cannot be accessed in Second.py, as it was not imported.
As mentioned in inspectorG4dget's comment, you could use from First import x as well, or you could do from First import * to import all defined values in First.py. There are other options as well, depending on your use case.

How does importing 1 function only that depends on another work?

What if we have a module that contains two functions and we import only one of them, will the other work?For instance:
file test.py
def a(x):print(x)
def b():a(12)
At the interpreter:
from test import b
b()
It prints 12.How is this possible?Please pardon my bad formatting that's my first question :).
Technically there is no such thing as importing a single name from a module; the entire module is imported and then one or more names are copied to the local namespace. Your import is roughly the equivalent of:
import test
b = test.b
del test
Except that at no point is test ever actually in the local namespace (and subsequently is not actually deleted).

Override globals in function imported from another module

Let's say I have two modules:
a.py
value = 3
def x()
return value
b.py
from a import x
value = 4
My goal is to use the functionality of a.x in b, but change the value returned by the function. Specifically, value will be looked up with a as the source of global names even when I run b.x(). I am basically trying to create a copy of the function object in b.x that is identical to a.x but uses b to get its globals. Is there a reasonably straightforward way to do that?
Here is an example:
import a, b
print(a.x(), b.x())
The result is currently 3 3, but I want it to be 3 4.
I have come up with two convoluted methods that work, but I am not happy with either one:
Re-define x in module b using copy-and paste. The real function is much more complex than shown, so this doesn't sit right with me.
Define a parameter that can be passed in to x and just use the module's value:
def x(value):
return value
This adds a burden on the user that I want to avoid, and does not really solve the problem.
Is there a way to modify where the function gets its globals somehow?
I've come up with a solution through a mixture of guess-and-check and research. You can do pretty much exactly what I proposed in the question: copy a function object and replace its __globals__ attribute.
I am using Python 3, so here is a modified version of the answer to the question linked above, with an added option to override the globals:
import copy
import types
import functools
def copy_func(f, globals=None, module=None):
"""Based on https://stackoverflow.com/a/13503277/2988730 (#unutbu)"""
if globals is None:
globals = f.__globals__
g = types.FunctionType(f.__code__, globals, name=f.__name__,
argdefs=f.__defaults__, closure=f.__closure__)
g = functools.update_wrapper(g, f)
if module is not None:
g.__module__ = module
g.__kwdefaults__ = copy.copy(f.__kwdefaults__)
return g
b.py
from a import x
value = 4
x = copy_func(x, globals(), __name__)
The __globals__ attribute is read-only, which is why it must be passed to the constructor of FunctionType. The __globals__ reference of an existing function object can not be changed.
Postscript
I've used this enough times now that it's implemented in a utility library I wrote and maintain called haggis. See haggis.objects.copy_func.
So I found a way to (sort of) do this, although I don't think it entirely solves your problems. Using inspect, you can access the global variables of the file calling your function. So if you set up your files like so:
a.py
import inspect
value = 3
def a():
return inspect.stack()[1][0].f_globals['value']
b.py
from a import a
value = 5
print(a())
The output is 5, instead of 3. However, if you imported both of these into a third file, it would look for the globals of the third file. Just wanted to share this snippet however.
I had the same problem. But then I remembered eval was a thing.
Here's a much shorter version(if you don't need arguments):
b.py:
from a import x as xx
# Define globals for the function here
glob = {'value': 4}
def x():
return eval(xx.__code__, glob)
Hopefully after 2 years it'll still be helpful

Changing Global Variables in Python

I want to modify a global variable from a function in Python 2.7
x = 0
def func():
global x
x = 2
If I load this code in the interpreter, and then run func(), x remains 0. How do I modify the value of x from within a function?
EDIT: Here's a screenshot of the interpreter and source code. I'm not sure why it works for others and not for me.
http://img18.imageshack.us/img18/9567/screenshotfrom201304222.png
This is a very interesting situation. When I ran your code from an interpreter with from mytest import * I encountered the same issue:
>>> from mytest import *
>>> x
0
>>> func()
>>> x
0
However, when I just did import mytest and ran it from there:
>>> import mytest
>>> mytest.x
0
>>> mytest.func()
>>> mytest.x
2
It turned out fine! The reason, I believe, comes from a line in http://docs.python.org/2/reference/simple_stmts.html#the-global-statement:
Names listed in a global statement must not be defined as formal
parameters or in a for loop control target, class definition, function
definition, or import statement.
Looks like because it is a parameter in your import statement (by importing all), global is having trouble with it. Do you need to import *, or can you simply import the module whole?

Categories