good program design for global variables - python

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

Related

Do `from foo.bar import baz` programmatically [duplicate]

I want to import some package depending on which value the user chooses.
The default is file1.py:
from files import file1
If user chooses file2, it should be :
from files import file2
In PHP, I can do this using variable variables:
$file_name = 'file1';
include($$file_name);
$file_name = 'file2';
include($$file_name);
How can I do this in Python?
Python doesn't have a feature that's directly equivalent to PHP's "variable variables". To get a "variable variable"'s value (or the value of any other expression) you can use the eval function.
foo = "Hello World"
print eval("foo")
However, this can't be used in an import statement.
It is possible to use the __import__ function to import using a variable.
package = "os"
name = "path"
imported = getattr(__import__(package, fromlist=[name]), name)
is equivalent to
from os import path as imported
Old thread, but I needed the answer, so someone else still might...
There's a cleaner way to do this in Python 2.7+:
import importlib
my_module = importlib.import_module("package.path.%s" % module_name)
As Fredrik Lundh states:
Anyway, here’s how these statements and functions work:
import X imports the module X, and creates a reference to that module
in the current namespace. Or in other words, after you’ve run this
statement, you can use X.name to refer to things defined in module X.
from X import * imports the module X, and creates references in the
current namespace to all public objects defined by that module (that
is, everything that doesn’t have a name starting with “_”). Or in
other words, after you’ve run this statement, you can simply use a
plain name to refer to things defined in module X. But X itself is not
defined, so X.name doesn’t work. And if name was already defined, it
is replaced by the new version. And if name in X is changed to point
to some other object, your module won’t notice.
from X import a, b, c imports the module X, and creates references in
the current namespace to the given objects. Or in other words, you can
now use a and b and c in your program.
Finally, X = __import__(‘X’) works like import X, with the difference
that you 1) pass the module name as a string, and 2) explicitly assign
it to a variable in your current namespace.
And by the way that's the last one method that you're intrested in.
Simply write (for example):
var = "datetime"
module = __import__(var)
Basing myself on mattjbray's answer:
from importlib import import_module
# lookup in a set is in constant time
safe_names = {"file1.py", "file2.py", "file3.py", ...}
user_input = ...
if user_input in safe_names:
file = import_module(user_input)
else:
print("Nope, not doing this.")
Saves a few lines of code, and allows you to set safe_names programmatically, or load multiple modules and assign them to a dict, for example.
It's probably a very bad idea to let the user choose what to import. Packages can execute code on import, so you're effectively allowing a user to arbitrarily execute code on your system! Much safer to do something like
if user_input == 'file1.py':
from files import file1 as file
elif user_input == 'file2.py':
from files import file2 as file
else:
file = None
print "Sorry, you can't import that file"

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.

Best way to share global variables between files in Python

I was wondering what the best way is to use global variables in a multi-script python project.
I've seen this question: Using global variables between files? - and while the accepted answer works, the solution seems clunky.
See the below set of scripts. Only main.py is ever called directly; the rest are imported.
First, I've declared my global variables in a separate file:
#global_vars.py
my_string = "hello world"
The main.py prints the value of the string using a custom function, changes the value of the global variable, and then prints the new value
#main.py
import global_vars
import do_things_module
#Print the instantiated value of the global string
do_things_module.my_function()
#Change the global variable globally
global_vars.my_string = "goodbye"
#Print the new value of the global string
do_things_module.my_function()
do_things_module.py contains our custom print function, and gets the string straight from the global
#do_things_module.py
import global_vars
def my_function():
print(global_vars.my_string)
Having to keep referencing global_vars.my_string rather than just my_string to ensure I'm always reading/writing to the global scoped variable seems long-winded and not very 'pythonic'. Is there a better/neater way?
If your goal is to use my_string instead of global_vars.my_string, you could import the module like this:
from global_vars import *
You should be able to use my_string directly.
I would go with
import global_vars as g
Then you can refer to my_string from global_vars module as g.my_string in your code.
It doesn't use a lot of space, but it is still clear, that my_string came from global_vars and namespace isn't polluted
If you need only a few global_vars variables in your current module you can import only them
from global_vars import my_string, my_int
and reference to them as my_string and my_int
Best of all ("explicit is better than implicit") use
from module import name [as name] ...
but don't then expect to be able to modify the values seen by other modules (though you can mutate mutable objects, should you choose).

Python global variables don't seem to work across modules

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]

How do I import variable packages in Python like using variable variables ($$) in PHP?

I want to import some package depending on which value the user chooses.
The default is file1.py:
from files import file1
If user chooses file2, it should be :
from files import file2
In PHP, I can do this using variable variables:
$file_name = 'file1';
include($$file_name);
$file_name = 'file2';
include($$file_name);
How can I do this in Python?
Python doesn't have a feature that's directly equivalent to PHP's "variable variables". To get a "variable variable"'s value (or the value of any other expression) you can use the eval function.
foo = "Hello World"
print eval("foo")
However, this can't be used in an import statement.
It is possible to use the __import__ function to import using a variable.
package = "os"
name = "path"
imported = getattr(__import__(package, fromlist=[name]), name)
is equivalent to
from os import path as imported
Old thread, but I needed the answer, so someone else still might...
There's a cleaner way to do this in Python 2.7+:
import importlib
my_module = importlib.import_module("package.path.%s" % module_name)
As Fredrik Lundh states:
Anyway, here’s how these statements and functions work:
import X imports the module X, and creates a reference to that module
in the current namespace. Or in other words, after you’ve run this
statement, you can use X.name to refer to things defined in module X.
from X import * imports the module X, and creates references in the
current namespace to all public objects defined by that module (that
is, everything that doesn’t have a name starting with “_”). Or in
other words, after you’ve run this statement, you can simply use a
plain name to refer to things defined in module X. But X itself is not
defined, so X.name doesn’t work. And if name was already defined, it
is replaced by the new version. And if name in X is changed to point
to some other object, your module won’t notice.
from X import a, b, c imports the module X, and creates references in
the current namespace to the given objects. Or in other words, you can
now use a and b and c in your program.
Finally, X = __import__(‘X’) works like import X, with the difference
that you 1) pass the module name as a string, and 2) explicitly assign
it to a variable in your current namespace.
And by the way that's the last one method that you're intrested in.
Simply write (for example):
var = "datetime"
module = __import__(var)
Basing myself on mattjbray's answer:
from importlib import import_module
# lookup in a set is in constant time
safe_names = {"file1.py", "file2.py", "file3.py", ...}
user_input = ...
if user_input in safe_names:
file = import_module(user_input)
else:
print("Nope, not doing this.")
Saves a few lines of code, and allows you to set safe_names programmatically, or load multiple modules and assign them to a dict, for example.
It's probably a very bad idea to let the user choose what to import. Packages can execute code on import, so you're effectively allowing a user to arbitrarily execute code on your system! Much safer to do something like
if user_input == 'file1.py':
from files import file1 as file
elif user_input == 'file2.py':
from files import file2 as file
else:
file = None
print "Sorry, you can't import that file"

Categories