Global variable from multiple file in python [duplicate] - python
I'm bit confused about how the global variables work. I have a large project, with around 50 files, and I need to define global variables for all those files.
What I did was define them in my projects main.py file, as following:
# ../myproject/main.py
# Define global myList
global myList
myList = []
# Imports
import subfile
# Do something
subfile.stuff()
print(myList[0])
I'm trying to use myList in subfile.py, as following
# ../myproject/subfile.py
# Save "hey" into myList
def stuff():
globals()["myList"].append("hey")
An other way I tried, but didn't work either
# ../myproject/main.py
# Import globfile
import globfile
# Save myList into globfile
globfile.myList = []
# Import subfile
import subfile
# Do something
subfile.stuff()
print(globfile.myList[0])
And inside subfile.py I had this:
# ../myproject/subfile.py
# Import globfile
import globfile
# Save "hey" into myList
def stuff():
globfile.myList.append("hey")
But again, it didn't work. How should I implement this? I understand that it cannot work like that, when the two files don't really know each other (well subfile doesn't know main), but I can't think of how to do it, without using io writing or pickle, which I don't want to do.
The problem is you defined myList from main.py, but subfile.py needs to use it. Here is a clean way to solve this problem: move all globals to a file, I call this file settings.py. This file is responsible for defining globals and initializing them:
# settings.py
def init():
global myList
myList = []
Next, your subfile can import globals:
# subfile.py
import settings
def stuff():
settings.myList.append('hey')
Note that subfile does not call init()— that task belongs to main.py:
# main.py
import settings
import subfile
settings.init() # Call only once
subfile.stuff() # Do stuff with global var
print settings.myList[0] # Check the result
This way, you achieve your objective while avoid initializing global variables more than once.
See Python's document on sharing global variables across modules:
The canonical way to share information across modules within a single program is to create a special module (often called config or cfg).
config.py:
x = 0 # Default value of the 'x' configuration setting
Import the config module in all modules of your application; the module then becomes available as a global name.
main.py:
import config
print (config.x)
In general, don’t use from modulename import *. Doing so clutters the importer’s namespace, and makes it much harder for linters to detect undefined names.
You can think of Python global variables as "module" variables - and as such they are much more useful than the traditional "global variables" from C.
A global variable is actually defined in a module's __dict__ and can be accessed from outside that module as a module attribute.
So, in your example:
# ../myproject/main.py
# Define global myList
# global myList - there is no "global" declaration at module level. Just inside
# function and methods
myList = []
# Imports
import subfile
# Do something
subfile.stuff()
print(myList[0])
And:
# ../myproject/subfile.py
# Save "hey" into myList
def stuff():
# You have to make the module main available for the
# code here.
# Placing the import inside the function body will
# usually avoid import cycles -
# unless you happen to call this function from
# either main or subfile's body (i.e. not from inside a function or method)
import main
main.mylist.append("hey")
Using from your_file import * should fix your problems. It defines everything so that it is globally available (with the exception of local variables in the imports of course).
for example:
##test.py:
from pytest import *
print hello_world
and:
##pytest.py
hello_world="hello world!"
Hai Vu answer works great, just one comment:
In case you are using the global in other module and you want to set the global dynamically, pay attention to import the other modules after you set the global variables, for example:
# settings.py
def init(arg):
global myList
myList = []
mylist.append(arg)
# subfile.py
import settings
def print():
settings.myList[0]
# main.py
import settings
settings.init("1st") # global init before used in other imported modules
# Or else they will be undefined
import subfile
subfile.print() # global usage
Your 2nd attempt will work perfectly, and is actually a really good way to handle variable names that you want to have available globally. But you have a name error in the last line. Here is how it should be:
# ../myproject/main.py
# Import globfile
import globfile
# Save myList into globfile
globfile.myList = []
# Import subfile
import subfile
# Do something
subfile.stuff()
print(globfile.myList[0])
See the last line? myList is an attr of globfile, not subfile. This will work as you want.
Mike
I just came across this post and thought of posting my solution, just in case of anyone being in the same situation as me, where there are quite some files in the developed program, and you don't have the time to think through the whole import sequence of your modules (if you didn't think of that properly right from the start, such as I did).
In such cases, in the script where you initiate your global(s), simply code a class which says like:
class My_Globals:
def __init__(self):
self.global1 = "initial_value_1"
self.global2 = "initial_value_2"
...
and then use, instead of the line in the script where you initiated your globals, instead of
global1 = "initial_value_1"
use
globals = My_Globals()
I was then able to retrieve / change the values of any of these globals via
globals.desired_global
in any script, and these changes were automatically also applied to all the other scripts using them. All worked now, by using the exact same import statements which previously failed, due to the problems mentioned in this post / discussion here. I simply thought of global object's properties being changing dynamically without the need of considering / changing any import logic, in comparison to simple importing of global variables, and that definitely was the quickest and easiest (for later access) approach to solve this kind of problem for me.
Based on above answers and links within I created a new module called global_variables.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ==============================================================================
#
# global_variables.py - Global variables shared by all modules.
#
# ==============================================================================
USER = None # User ID, Name, GUID varies by platform
def init():
""" This should only be called once by the main module
Child modules will inherit values. For example if they contain
import global_variables as g
Later on they can reference 'g.USER' to get the user ID.
"""
global USER
import getpass
USER = getpass.getuser()
# End of global_variables.py
Then in my main module I use this:
import global_variables as g
g.init()
In another child imported module I can use:
import global_variables as g
# hundreds of lines later....
print(g.USER)
I've only spent a few minutes testing in two different python multiple-module programs but so far it's working perfectly.
Namespace nightmares arise when you do from config import mySharedThing. That can't be stressed enough.
It's OK to use from in other places.
You can even have a config module that's totally empty.
# my_config.py
pass
# my_other_module.py
import my_config
def doSomething():
print(my_config.mySharedThing.message)
# main.py
from dataclasses import dataclass
from my_other_module import doSomething
import my_config
#dataclass
class Thing:
message: str
my_config.mySharedThing = Thing('Hey everybody!')
doSomething()
result:
$ python3 main.py
Hey everybody!
But using objects you pulled in with from will take you down a path of frustration.
# my_other_module.py
from my_config import mySharedThing
def doSomething():
print(mySharedThing.message)
result:
$ python3 main.py
ImportError: cannot import name 'mySharedThing' from 'my_config' (my_config.py)
And maybe you'll try to fix it like this:
# my_config.py
mySharedThing = None
result:
$ python3 main.py
AttributeError: 'NoneType' object has no attribute 'message'
And then maybe you'll find this page and try to solve it by adding an init() method.
But the whole problem is the from.
Related
How to import variable from another function and another directory?
I have two files. first.py Class Repo: def my_function: variable = [] Now, I've second file in different directory and want to create something like: second.py def my_second_function(variable): print(variable) # or some other operations on variable What can I do to make my above code works? I tried something like: import sys sys.path.insert(1, 'path') from first import * but it doesn't work. Do you have any ideas?
Although it is not entirely clear to me what you are aiming at, here is a way to define a global variable within one file, which then can be imported via the sys.path approach you tried: test_dir/file1.py: global_var1 = 'global_var1' # This variable is available upon import of file1.py def fun(): global global_var2 global_var2 = 'global_var2' # This variable will be available after having called fun() file2.py: import sys sys.path.insert(1, 'test_dir') import file1 print(file1.global_var1) #print(file1.global_var2) # This would fail file1.fun() print(file1.global_var2) # This works
Global variable with imports between 3 files
I come from this thread that was helpful but not enough. My case is slightly different but most important is my situation: I'm refactoring a code of 1000+ lines (in one file) and so making a change in the name of something can quite quickly become painful. So I've split my file into 3: settings.py in which I've defined all the global variables (there are a lot - mostly booleans and strings) synchro.py which can change their values groups.py which can also change them I'll simplify the stuff here. settings.py: MY_VAR = "plane" synchro.py: from settings import * import groups def main(): global MY_VAR MY_VAR = "boat" groups.update() groups.py: from settings import * def update(): print MY_VAR # will print "plane" instead of "boat" I also tried to write import settings instead of from settings import * but in that case python tells me global settings.MY_VAR is an error, and it does not recognize settings.MY_VAR if I remove the global line. So what can be the quickest/easiest way to get this work? Thanks for your help
you should do like below, its not so recommend to do from settings import * , it cause namespace pollution and make things harder to debug later. i usually only use global within the same file though i try to minimize the global usage if possible settings.py MY_VAR = "plane" synchro.py: import settings import groups def main(): # no need to declare global here, access trough settings namespace instead settings.MY_VAR = "boat" groups.update() groups.py: import settings def update(): print settings.MY_VAR # will print "plane" at first, after synchro main, MY_VAR should carry 'boat'
using import inside class
I am completely new to the python class concept. After searching for a solution for some days, I hope I will get help here: I want a python class where I import a function and use it there. The main code should be able to call the function from the class. for that I have two files in the same folder. Thanks to #cdarke, #DeepSpace and #MosesKoledoye, I edited the mistake, but sadly that wasn't it. I still get the Error: test 0 Traceback (most recent call last): File "run.py", line 3, in <module> foo.doit() File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 8, in doit self.timer(5) File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 6, in timer zeit.sleep(2) NameError: global name 'zeit' is not defined #wombatz got the right tip: it must be self.zeit.sleep(2) or Test.zeit.sleep(2). the import could be also done above the class declaration. Test.Py class Test: import time as zeit def timer(self, count): for i in range(count): print("test "+str(i)) self.zeit.sleep(2) <-- self is importent, otherwise, move the import above the class declaration def doit(self): self.timer(5) and run.py from test import Test foo = Test() foo.doit() when I try to python run.py I get this error: test 0 Traceback (most recent call last): File "run.py", line 3, in <module> foo.doit() File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 8, in doit self.timer(5) File "/Users/ls/Documents/Entwicklung/RaspberryPi/test/test.py", line 6, in timer sleep(2) NameError: global name 'sleep' is not defined What I understand from the error is that the import in the class is not recognized. But how can I achive that the import in the class is recognized?
Everything defined inside the namespace of a class has to be accessed from that class. That holds for methods, variables, nested classes and everything else including modules. If you really want to import a module inside a class you must access it from that class: class Test: import time as zeit def timer(self): self.zeit.sleep(2) # or Test.zeit.sleep(2) But why would you import the module inside the class anyway? I can't think of a use case for that despite from wanting it to put into that namespace. You really should move the import to the top of the module. Then you can call zeit.sleep(2) inside the class without prefixing self or Test. Also you should not use non-english identifiers like zeit. People who only speak english should be able to read your code.
sleep is not a python builtin, and the name as is, does not reference any object. So Python has rightly raised a NameEror. You intend to: import time as zeit zeit.sleep(2) And move import time as zeit to the top of the module. The time module aliased as zeit is probably not appearing in your module's global symbol table because it was imported inside a class.
You want time.sleep. You can also use; from time import sleep Edit: Importing within class scope issues explained here.
You're almost there! sleep is a function within the time module. This means that the name sleep doesn't exist unless its understood within the context of time, unless you define it on your own. Since you didn't define it on your own, you can access it by running time.sleep(2). In your specific example, you used: import time as zeit you'll have to run: zeit.sleep(2) Alternatively, you can import sleep directly from time, by running: from time import sleep sleep(2) Good luck! You can read more about the time module here: https://docs.python.org/2/library/time.html You can learn more about imports here: https://docs.python.org/3/reference/import.html and I highly recommend learning about namespace in python, here: https://bytebaker.com/2008/07/30/python-namespaces/
I agree with #Wombatz on his solution, but I do not have enough reputation to comment on his question One use case that I have found for importing a module within a class is when I want to initialize a class from a config file. Say my config file is config.py __all__ = ['logfile', ... ] logfile = 'myevent.log' ... And in my main module do_something.py class event(): from config import * def __init__(self): try : self.logfile except NameError: self.logfile = './generic_event.log' Now the advantage of this scheme is that we do not need to import logfile in the global namespace if it is not needed Whereas, importing at the beginning of do_something.py, I will have to use globals inside the class, which is a little ugly in my opinion.
It's probably a bit late, but I agree with idea of not polluting the module-level namespace (of course this can probably be remedied with a better design of a module, plus 'explicit is better than implicit' anyways). Here is what I would do. The basic idea is this: import is an implicit assignment in which an entire module object gets assigned to a single name. Thus: class Test: import time as zeit self.zeit = zeit # This line binds the module object to an attribute of an instance created from the class def timer(self, count): for i in range(count): print("test "+str(i)) self.zeit.sleep(2) # This necessitates the `zeit` attribute within the instance created from the class def doit(self): self.timer(5)
import importlib class importery(): def __init__(self, y,z): self.my_name = y self.pathy = z self.spec = importlib.util.spec_from_file_location(self.my_name, self.pathy) x = importlib.util.module_from_spec(self.spec) self.spec.loader.exec_module(x) print(dir(x)) root = x.Tk() root.mainloop() pathy = r'C:\Users\mine\Desktop\python310\Lib\tkinter\__init__.py' importery('tk', pathy) There is a 'time and a place' to do this type of black magic, thankfully very rare times and places. Of the few I've found I've normally been able to use subprocess to get some other flavor of python to do my dirty work, but that is not always an option. Now, I have 'used' this in blender when I've needed to have conflicting versions of a module loaded at the same time. This is not a good way to do things and really should be a last resort. If you are a blender user and you happen to decide to commit this sin, I suggest doing so in a clean version of blender, install a like version of python next to it to use that to do your pip installs with, and please make sure you have added your config folder to your blender folder, else this black magic may come back to bite you in the arse later.
Is there a way to get importer's variable in importee?
Say that I have two python file # importer.py parameter = 4 import importee and # importee.py print parameter Can I or how can I in importee.py access importer.py's parameter? I'm using an ugly work around, borrow (and pollute) the sys # importer.py import sys sys.parameter = 4 import importee and # importee.py print sys.parameter Too ugly. Looking for better solution.
The recommended way to achieve what I think you want to achieve is to declare function in importee, and call it, e.g.: # importer.py import importee importee.call_me(4) and: # importee.py def call_me(parameter): print(parameter) It is preferable to avoid performing any operations in global scope. And especially print()ing anything but I suppose your minimal example doesn't match your real use case :). By the way, the ugly work around you have mentioned is practically equivalent to using a separate configuration module. For example: # importer.py import config config.param = 4 import importee + # importee.py import config print(config.param) + # config.py param = 7 # some default It's still nowhere close to pretty but at least avoids mangling with system modules.
python package structure and global variables
I created a package in python. In the init file I placed a variable that must be available in all the modules of this package. Example: # main script from _Framework.STB import DB DB.environment = 'lab' channel = DB.Channels.ChannelStatic.getChannelByNumber(416) ... # Channels.py module in the package from _Framework.DB.__init__ import cursor from _Framework.DB.__init__ import environment from time import * ... The problem is that everdy call to from _Framework.DB.__init__ import xy overwrites my "global" variables How can I solve? Thanks EDIT: init.py: all = [ 'Events', 'Channels', 'Genres', 'Subgenres','EPGSections'] try: conn = MySQLdb.connect(host,user,passwd,db) cursor = conn.cursor(MySQLdb.cursors.DictCursor) except: cursor = None environment = 'live' I import the DB package and I set the "environment" variable, but when the Channels module is imported, I think, it makes a new call to init.py and reexecutes its code so "environment" is overwritten. I'm looking for a clean way to share a variable between modules of the same package
Importing from module __init__ like from package.__init__ import X runs the module code every time. That's why your variable gets overwritten. This is because the __init__ module is getting added twice(with different names) in sys.modules. First, as your package's name when it is importing it then as package.__init__ while importing it explicitly. As the source is loaded twice, the code is executed twice. However you don't need to do it like this, you only need to do: from _Framework.DB import cursor from _Framework.DB import environment Then your code would only run once.
I'm still not sure but if I understand correctly. YOu should just use the edit on your question and post correctly formatted code: __init__.py: all = [ 'Events', 'Channels', 'Genres', 'Subgenres','EPGSections'] try: conn = MySQLdb.connect(host,user,passwd,db) cursor = conn.cursor(MySQLdb.cursors.DictCursor) except: cursor = None environment = 'live Now in Channels.py you want to change _Framework.DB.__init__.cursor and _Framework.DB.__init__.environment and this changes should be seen all around your program? If this is what you want you should know that when you do in a import: from _Framework.DB.__init__ import cursor from _Framework.DB.__init__ import environment What happens is that you get two variables cursor and environment in your Channels.py module namespace and any change you do will only be visible in that module. If you want to achieve your result try this: import _Framework.DB _Framework.DB.cursor = new_cursor _Framework.DB.environment = new_environment This way you actually change the module variables. Now in the rest of your program, when you want to see the changes took effect you could either: Acces the varibles in the same way you changed them: import _Framework.DB _Framework.DB.cursor #changes made in other module will have effect _Framework.DB.environment #changes made in other module will have effect import like you did, BUT make sure the changes you want are done BEFORE you do any imports. As an negative example for the second case: #module Vars a = 1 -------------------------- #module Do_change import Vars Vars.a = 2 -------------------------- #module A from Vars import a -------------------------- #module B from Vars import a -------------------------- #some random module import A import Do_change import B Now if you are in this order of imports when you import A it will create a variable a in A's namespace with the value of Vars.a. The Do_change will edit the variable accordingly, but A will have no knowledge of this. B however who is imported after will see the correct Vars.a = 2. That's why I really reccomend the first option as it is the correct approach in my opinion.