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).
Related
I've run into a bit of a wall importing modules in a Python script. I'll do my best to describe the error, why I run into it, and why I'm tying this particular approach to solve my problem (which I will describe in a second):
Let's suppose I have a module in which I've defined some utility functions/classes, which refer to entities defined in the namespace into which this auxiliary module will be imported (let "a" be such an entity):
module1:
def f():
print a
And then I have the main program, where "a" is defined, into which I want to import those utilities:
import module1
a=3
module1.f()
Executing the program will trigger the following error:
Traceback (most recent call last):
File "Z:\Python\main.py", line 10, in <module>
module1.f()
File "Z:\Python\module1.py", line 3, in f
print a
NameError: global name 'a' is not defined
Similar questions have been asked in the past (two days ago, d'uh) and several solutions have been suggested, however I don't really think these fit my requirements. Here's my particular context:
I'm trying to make a Python program which connects to a MySQL database server and displays/modifies data with a GUI. For cleanliness sake, I've defined the bunch of auxiliary/utility MySQL-related functions in a separate file. However they all have a common variable, which I had originally defined inside the utilities module, and which is the cursor object from MySQLdb module.
I later realised that the cursor object (which is used to communicate with the db server) should be defined in the main module, so that both the main module and anything that is imported into it can access that object.
End result would be something like this:
utilities_module.py:
def utility_1(args):
code which references a variable named "cur"
def utility_n(args):
etcetera
And my main module:
program.py:
import MySQLdb, Tkinter
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
And then, as soon as I try to call any of the utilities functions, it triggers the aforementioned "global name not defined" error.
A particular suggestion was to have a "from program import cur" statement in the utilities file, such as this:
utilities_module.py:
from program import cur
#rest of function definitions
program.py:
import Tkinter, MySQLdb
db=MySQLdb.connect(#blahblah) ; cur=db.cursor() #cur is defined!
from utilities_module import *
But that's cyclic import or something like that and, bottom line, it crashes too. So my question is:
How in hell can I make the "cur" object, defined in the main module, visible to those auxiliary functions which are imported into it?
Thanks for your time and my deepest apologies if the solution has been posted elsewhere. I just can't find the answer myself and I've got no more tricks in my book.
Globals in Python are global to a module, not across all modules. (Many people are confused by this, because in, say, C, a global is the same across all implementation files unless you explicitly make it static.)
There are different ways to solve this, depending on your actual use case.
Before even going down this path, ask yourself whether this really needs to be global. Maybe you really want a class, with f as an instance method, rather than just a free function? Then you could do something like this:
import module1
thingy1 = module1.Thingy(a=3)
thingy1.f()
If you really do want a global, but it's just there to be used by module1, set it in that module.
import module1
module1.a=3
module1.f()
On the other hand, if a is shared by a whole lot of modules, put it somewhere else, and have everyone import it:
import shared_stuff
import module1
shared_stuff.a = 3
module1.f()
… and, in module1.py:
import shared_stuff
def f():
print shared_stuff.a
Don't use a from import unless the variable is intended to be a constant. from shared_stuff import a would create a new a variable initialized to whatever shared_stuff.a referred to at the time of the import, and this new a variable would not be affected by assignments to shared_stuff.a.
Or, in the rare case that you really do need it to be truly global everywhere, like a builtin, add it to the builtin module. The exact details differ between Python 2.x and 3.x. In 3.x, it works like this:
import builtins
import module1
builtins.a = 3
module1.f()
As a workaround, you could consider setting environment variables in the outer layer, like this.
main.py:
import os
os.environ['MYVAL'] = str(myintvariable)
mymodule.py:
import os
myval = None
if 'MYVAL' in os.environ:
myval = os.environ['MYVAL']
As an extra precaution, handle the case when MYVAL is not defined inside the module.
This post is just an observation for Python behaviour I encountered. Maybe the advices you read above don't work for you if you made the same thing I did below.
Namely, I have a module which contains global/shared variables (as suggested above):
#sharedstuff.py
globaltimes_randomnode=[]
globalist_randomnode=[]
Then I had the main module which imports the shared stuff with:
import sharedstuff as shared
and some other modules that actually populated these arrays. These are called by the main module. When exiting these other modules I can clearly see that the arrays are populated. But when reading them back in the main module, they were empty. This was rather strange for me (well, I am new to Python). However, when I change the way I import the sharedstuff.py in the main module to:
from globals import *
it worked (the arrays were populated).
Just sayin'
A function uses the globals of the module it's defined in. Instead of setting a = 3, for example, you should be setting module1.a = 3. So, if you want cur available as a global in utilities_module, set utilities_module.cur.
A better solution: don't use globals. Pass the variables you need into the functions that need it, or create a class to bundle all the data together, and pass it when initializing the instance.
The easiest solution to this particular problem would have been to add another function within the module that would have stored the cursor in a variable global to the module. Then all the other functions could use it as well.
module1:
cursor = None
def setCursor(cur):
global cursor
cursor = cur
def method(some, args):
global cursor
do_stuff(cursor, some, args)
main program:
import module1
cursor = get_a_cursor()
module1.setCursor(cursor)
module1.method()
Since globals are module specific, you can add the following function to all imported modules, and then use it to:
Add singular variables (in dictionary format) as globals for those
Transfer your main module globals to it
.
addglobals = lambda x: globals().update(x)
Then all you need to pass on current globals is:
import module
module.addglobals(globals())
Since I haven't seen it in the answers above, I thought I would add my simple workaround, which is just to add a global_dict argument to the function requiring the calling module's globals, and then pass the dict into the function when calling; e.g:
# external_module
def imported_function(global_dict=None):
print(global_dict["a"])
# calling_module
a = 12
from external_module import imported_function
imported_function(global_dict=globals())
>>> 12
The OOP way of doing this would be to make your module a class instead of a set of unbound methods. Then you could use __init__ or a setter method to set the variables from the caller for use in the module methods.
Update
To test the theory, I created a module and put it on pypi. It all worked perfectly.
pip install superglobals
Short answer
This works fine in Python 2 or 3:
import inspect
def superglobals():
_globals = dict(inspect.getmembers(
inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
return _globals
save as superglobals.py and employ in another module thusly:
from superglobals import *
superglobals()['var'] = value
Extended Answer
You can add some extra functions to make things more attractive.
def superglobals():
_globals = dict(inspect.getmembers(
inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
return _globals
def getglobal(key, default=None):
"""
getglobal(key[, default]) -> value
Return the value for key if key is in the global dictionary, else default.
"""
_globals = dict(inspect.getmembers(
inspect.stack()[len(inspect.stack()) - 1][0]))["f_globals"]
return _globals.get(key, default)
def setglobal(key, value):
_globals = superglobals()
_globals[key] = value
def defaultglobal(key, value):
"""
defaultglobal(key, value)
Set the value of global variable `key` if it is not otherwise st
"""
_globals = superglobals()
if key not in _globals:
_globals[key] = value
Then use thusly:
from superglobals import *
setglobal('test', 123)
defaultglobal('test', 456)
assert(getglobal('test') == 123)
Justification
The "python purity league" answers that litter this question are perfectly correct, but in some environments (such as IDAPython) which is basically single threaded with a large globally instantiated API, it just doesn't matter as much.
It's still bad form and a bad practice to encourage, but sometimes it's just easier. Especially when the code you are writing isn't going to have a very long life.
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"
Inside a function, I have to import a variable (dict) from a module dynamically:
exec("from ctrl_%s import default_settings" % get_version_id(iid))
which doesnt work. When referencing this variable later, it says: UnboundLocalError: local variable 'default_settings' referenced before assignment
The variable is in the global scope of the module to import.
But:
This all works, if I hardcode this statement without exec(). The string is correctly formed, I can print it out.
Someone knows what to do?
I highly would discourage to use exec in the first place, it often does not do what you want especially if some special syntax is involved like here.
But fortunately there are some tricks:
e.g. you can import the module and use the dict or getattr:
import math
getattr(math,"sin")
math.__dict__['sin']
Edit just checked my answer and I saw you wanted to import a module ...
But there is also a trick for this:
https://docs.python.org/3/library/functions.html#__import__
Look also at this question for some examples:
How to import a module given its name as string?
Here's my code:
import numpy as np
import matplotlib.pyplot as plt
import astropy
import matplotlib
%matplotlib ipympl
import scatterplot_with_hist as sc
badx=[]
bady=[]
import badcomp as bc
#things like data5 and list2 are defined in here--I know that code is functional so I'll omit it for brevity
bc.getlist(start = 2000, end = 2200)
The module code is as follows:
def getlist(start, end):
for f in range(1):
for i in range(1238):
for n in range(int(start),int(end)):
if ((data[n]['col1'] - list2[i]) == 0):
badx.append(data[n]['col2'])
bady.append(data[n]['col3'])
If I run this code in the regular space (instead of importing it and running it as a function) it works fine. When I run it as an imported function, it won't recognize variables like data5, list2, and badx and bady.
Why?
Each Python module has it's own global namespace. That means that code in different modules that each try to access global variables will see separate ones. You can access another module's global variables by importing the module and interacting with the attributes of the module object.
In your code, the getlist function in the badcomp module is trying to interact with several global variables, including badx and bady for output, and data and list2 for input. It's not working because you've defined those in the interactive session, which uses the namespace of a module with the special name __main__.
While you could import __main__ from badcomp and interact with the global variables defined there via the module's attributes, that would be a really bad design, since it won't work if the module gets imported in any other way (e.g. by a different module you write later). Instead, the function should probably use variables defined in its own global namespace. The __main__ module is already importing badcomp (as bc), and can access things like badx and bady as bc.badx and bc.bady if the definitions are moved into the module.
Or you might reconsider if global variables are the best way for this function to work. It's often much better to use arguments and return values to pass data in and out of a function, rather than global variables. Maybe badx and bady should be defined within getlist and returned at the end. Meanwhile, data and list2 could be added as arguments to the function.
When a module is imported, it does NOT have access to the global or local namespace of the module that called it. You can get around this by creating a function that creates a variable in the global namespace inside the imported module and run the function from the calling module with each of the variables you need.
Example code (really bad design, but it'll teach you hopefully):
Put THIS in the imported module:
def putVarsInNamespace(variable, variableNameToInject)
exec("global %s" % variableName)
exec("%s = variable" % variableName)
Put THIS in the calling module:
test = 5
from <MODULENAME> import putVarsInNamespace
putVarsInNamespace(test, "test")
How this works: variableNameToInject is the name that you want the injected variable to be called. It then runs global variableNameToInject but it uses the VALUE of variableNameToInject which is the name that the injected variable should be called. This is useful when you want to inject multiple variables without using multiple functions. It then sets the variable name (the value of variableNameToInject) to the value of variable, and just like that it's injected.
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"