Global variable with imports between 3 files - python

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'

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

Check name of running script file in module

I have 2 app files with import same module:
#app1.py
import settings as s
another code
#app2.py
import settings as s
another code
I need in module check if running first or second app:
#settings.py
#pseudocode
if running app1.py:
print ('app1')
elif:
print ('app2')
I check module inspect but no idea.
Also I am open for all better solutions.
EDIT: I feel a bit foolish (I guess it is easy)
I try:
var = None
def foo(a):
var = a
print (var)
but still None.
I'm not sure it is possible for an importee to know who imported it. Even if it was, it sounds like code smell to me.
Instead, what you can do is delegate the decision of what actions are to be taken by app1 and app2, instead of having settings make that decision.
For example:
settings.py
def foo(value):
if value == 'app1':
# do something
else:
# do something else
app1.py
from settings import foo
foo('app1')
And so on.
To assign within the function and have it reflect on a global variable. Example:
A.py
var = None
def foo(a):
global var
var = a
def print_var():
print(var)
test.py
import A
A.print_var()
A.foo(123)
A.print_var()
Output:
None
123
Note that globals aren't recommended in general as a programming practice, so use them as little as possible.
I think your current approach is not the best way do solve your issue. You can solve this, too, by modifying settings.py slightly. You have two possible ways to go: either the solution of coldspeed, or using delegates. Either way, you have to store the code of your module inside functions.
Another way to solve this issue would be (depending on the amount of code lines which depend on the app name) to pass a function/delegate to the function as a parameter like this:
#settings.py
def theFunction(otherParemters, callback):
#do something
callback()
#app1.py
from settings import theFunction
def clb():
print("called from settings.py")
#do something app specific here
theFunction(otherParameter, clb)
This appears to be a cleaner solution compared to the inspect solution, as it allows a better separation of the two modules.
It depends highly on the range of application, whether you should choose the first or the second version; maybe you could provide us with more information about the broader issue you are trying to solve.
As others have said, perhaps this is not the best way to achieve it. If you do want to though, how about using sys.argv to identify the calling module?
app:
import settings as s
settings:
import sys
import os
print sys.argv[0]
# \\path\\to\\app.py
print os.path.split(sys.argv[0])[-1]
# app.py
of course, this gives you the file that was originally run from the command line, so if this is part of a further nested set of imports this won't work for you.
This works for me.
import inspect
import os
curframe = inspect.currentframe()
calframe = inspect.getouterframes(curframe, 1)
if os.path.basename(calframe[1][1]) == 'app1.py':
print ('app1')
else:
print ('app2')

Global variable from multiple file in python [duplicate]

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.

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.

How to include external Python code to use in other files?

If you have a collection of methods in a file, is there a way to include those files in another file, but call them without any prefix (i.e. file prefix)?
So if I have:
[Math.py]
def Calculate ( num )
How do I call it like this:
[Tool.py]
using Math.py
for i in range ( 5 ) :
Calculate ( i )
You will need to import the other file as a module like this:
import Math
If you don't want to prefix your Calculate function with the module name then do this:
from Math import Calculate
If you want to import all members of a module then do this:
from Math import *
Edit: Here is a good chapter from Dive Into Python that goes a bit more in depth on this topic.
Just write the "include" command :
import os
def include(filename):
if os.path.exists(filename):
execfile(filename)
include('myfile.py')
#Deleet :
#bfieck remark is correct, for python 2 and 3 compatibility, you need either :
Python 2 and 3: alternative 1
from past.builtins import execfile
execfile('myfile.py')
Python 2 and 3: alternative 2
exec(compile(open('myfile.py').read()))
If you use:
import Math
then that will allow you to use Math's functions, but you must do Math.Calculate, so that is obviously what you don't want.
If you want to import a module's functions without having to prefix them, you must explicitly name them, like:
from Math import Calculate, Add, Subtract
Now, you can reference Calculate, Add, and Subtract just by their names. If you wanted to import ALL functions from Math, do:
from Math import *
However, you should be very careful when doing this with modules whose contents you are unsure of. If you import two modules who contain definitions for the same function name, one function will overwrite the other, with you none the wiser.
I've found the python inspect module to be very useful
For example with teststuff.py
import inspect
def dostuff():
return __name__
DOSTUFF_SOURCE = inspect.getsource(dostuff)
if __name__ == "__main__":
dostuff()
And from the another script or the python console
import teststuff
exec(DOSTUFF_SOURCE)
dostuff()
And now dostuff should be in the local scope and dostuff() will return the console or scripts _name_ whereas executing test.dostuff() will return the python modules name.
It's easy and simple:
you can just do this:
def run_file(path):
return exec(open(path).read());
run_file("myfile.py");
I would like to emphasize an answer that was in the comments that is working well for me. As mikey has said, this will work if you want to have variables in the included file in scope in the caller of 'include', just insert it as normal python. It works like an include statement in PHP. Works in Python 3.8.5.
Alternative #1
import textwrap
from pathlib import Path
exec(textwrap.dedent(Path('myfile.py').read_text()))
Alternative #2
with open('myfile.py') as f: exec(f.read())
I prefer Alternative #2 and have been using it in my website development.

Categories

Resources