Problems trying to access to a global var - python

I know how to use the global variables when they are defined in a class, but I have a global variable in a main.
If I want to use it inside a class, which would be the import to access it?
My main is something like this
Main.py:
from EvolutionaryAlgorithm import EvolutionaryAlgorithm
initialTimeMain = 0
if __name__ == '__main__':
evolutionaryAlgorithm= EvolutionaryAlgorithm()
.
.
and my EvolutionaryAlgorithm class has a method which uses the initialTimeMain variable.
the problem is when I add this import in the EvolutionaryAlgorithm:
EvolutionaryAlgorithm.py
import Main
because when I run the script, an error appears
from EvolutionaryAlgorithm import
EvolutionaryAlgorithm ImportError:
cannot import name
EvolutionaryAlgorithm
the import isn't recognized anymore

You have a case of circular imports, short-term solution is to move import statement inside the if clause:
initialTimeMain = 0
if __name__ == '__main__':
from EvolutionaryAlgorithm import EvolutionaryAlgorithm
evolutionaryAlgorithm= EvolutionaryAlgorithm()
A better, long-term solution would be to refactor your code so that you don't have circular imports or initialTimeMain is defined in the EvolutionaryAlgorithm.py, which of course would be available in Main.py with your existing import strategy.
Old answer:
a.py:
globalvar = 1
print(globalvar) # prints 1
if __name__ == '__main__':
print(globalvar) # prints 1
b.py:
import a
print(a.globalvar) # prints 1

Related

"from module import class" importing other classes from same module

Given the following files:
a.py
-----
class CommonClass(object):
def do_thing(self):
pass
b.py
-----
from a import CommonClass
class SubClassA(CommonClass):
def do_thing(self):
print("I am A")
class SubClassB(CommonClass):
def do_thing(self):
print("I am B")
c.py
-----
from a import CommonClass
from b import SubClassA
if __name__ == "__main__":
for member in CommonClass.__subclasses__():
member().do_thing()
I would expect only SubClassA is imported, and visible when looping through subclasses of CommonClass, but it seems SubClassB is imported as well.
I am running Python 3.8.5 and this is the output of python3 c.py:
$ python3 c.py
I am A
I am B
How can I only import the classes I want?
You did import only SubClassA to c.py. This can be tested by doing
x = SubClassB()
or
x = b.SubClassB()
Both will result in a NameError.
The thing is, that when you import a file, it is actually being ran, even when using from x import y!
This can be easily seen by adding a print("I'm from b.py") at the end of b.py and then running c.py.
This makes both SubClassA and SubClassB be subclasses of CommonClass, which you imported. So, while you don't have access to the SubClassB name, it is still a subclass of CommonClass and you can access it from there.
In general you don't have to import a module to be able to use its objects. Importing expands your namespace to include that module so you can create an object directly. You can still use this module's objects even without importing it if you acquired them in some other way (like importing a third module which returns objects from the second one).
Anyway right now, you are not really using even the imported SubClassA. If you want to "allow" certain classes to be considered only from an external source, you can create an allowed set of classes:
from a import CommonClass
from b import SubClassA
allowed_classes = {SubClassA}
if __name__ == "__main__":
for member in CommonClass.__subclasses__():
if member in allowed_classes:
member().do_thing()
Which only prints I am A
from a import CommonClass
from b import SubClassA
if __name__ == "__main__":
h = CommonClass.__subclasses__()[0]()
h.do_thing()
You can do this.

How to import and get variables from another python file?

The main file is aaa.py, code as below:
import os,time
from get_newest_folder import *
def main_test():
print(newest_folder)
print(path)
if __name__ == '__main__':
get_n_folder()
main_test()
The another file is get_newest_folder.py, code as below:
import os
def get_n_folder():
path = 'D:\\Test'
os.chdir(path)
files = sorted(os.listdir(os.getcwd()), key=os.path.getmtime)
newest_folder = files[-1]
return (path, newest_folder)
if __name__ == '__main__':
get_n_folder()
I'm trying to use variable "path" and "newest_folder" from get_newest_folder.py in aaa.py, so I import "get_newest_folder". But I can't get(print) the 2 variables in aaa.py. It displays the error message as below when I run aaa.py:
NameError: name 'newest_folder' is not defined
Does anyone can correct my code or give me some suggestion?
Many thanks.
modify your aaa.py
import os,time
from get_newest_folder import *
def main_test():
path,newest_folder = get_n_folder()
print(newest_folder)
print(path)
if __name__ == '__main__':
main_test()
Inorder to access newest_folder and path, you have to declare them as global in get_newest_folder.py and import these variables in aaa.py. They are local variables currently.
But this is not an ideal way to get about things. May be you need something like this
import os,time
from get_newest_folder import *
def main_test():
_path, _newest_folder = get_n_folder()
print(_path)
print(_newest_folder)
if __name__ == '__main__':
main_test()
First of all, to answer your question, you need to modify your module:
import os
newest_folder = '' # <<<<
path = '' # <<<<
def get_n_folder():
global newest_folder # <<<<
global path # <<<<
....
# The rest is unchanged
The variables are attributes of the module now, previously they were only attributes of the function.
So that got rid of the error messages, but it just prints two blank lines!!! Why? Because newest_folder and path have not been setup, you don't call the function to populate the values!
That's one problem, the other is from get_newest_folder import *. Don't do that - it pollutes your own namespace. It gives the opportunity for a module to overwrite your own variables (namespace collision). Fine, you know this module, but will you always? Do you know very variable in every module that you import?
So:
import get_newest_folder
def main_test():
print(get_newest_folder.newest_folder)
print(get_newest_folder.path)
if __name__ == '__main__':
get_newest_folder.get_n_folder()
main_test()
But, as others have said, it is better not to share variables, if you do it means that the module implementation cannot change - it breaks encapsulation. There are a few occasions where it might be justified, but not here.
Instead, using your original get_newest_folder.py:
import os,time
import get_newest_folder
def main_test():
print(newest_folder)
print(path)
if __name__ == '__main__':
print(dir())
newest_folder, path = get_newest_folder.get_n_folder()
main_test()

python import circular dependency (and perhaps function declaration)

Hello I did got into circular dependency what is not refactori-zable other than doubling code.
I have something like this (only much more complex):
myParser.py:
import sys
import main #comment this to make it runnable
def parseEvnt():
sys.stdout.write("evnt:")
main.parseCmd(1) #comment this to make it runnable
tbl.py:
import myParser
tblx = {
1:("cmd",),
2:("evnt",myParser.parseEvnt),
}
main.py:
import tbl
def parseCmd(d):
print(tbl.tblx[d][0])
data=[1,2]
for d in data:
if(d<2):
parseCmd(d)
else:
fce = tbl.tblx[d][1]
fce()
Obvious error I'm getting is:
File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 1, in <module>
import tbl
File "D:\Data\vbe\workspace\marsPython\testCircular2\tbl.py", line 1, in <module>
import myParser
File "D:\Data\vbe\workspace\marsPython\testCircular2\myParser.py", line 2, in <module>
import main
File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 7, in <module>
parseCmd(d)
File "D:\Data\vbe\workspace\marsPython\testCircular2\main.py", line 3, in parseCmd
print(tbl.tblx[d][0])
AttributeError: module 'tbl' has no attribute 'tblx'
In C I think I would just tell by declaration in tbl.py hey there is function parseEvnt(). I would not need to include myParser and there would be no circular include.
In python I do not know how to do it.
I read few threads and there is always some wise guy recommending refactorizing. But in this case parseCmd() needs to see tblx which needs to see parseEvnt() (unless function declaration) and parseEvnt() need to call parseCmd() (cos evnt contains triggering cmd and I do not want to double the decoding cmd code).
Is there way how to make it working in python?
You can frequently get away with circular dependencies as long as the modules don't try to use each other's data until all importing is complete - as a practical matter, that means referencing with namespace (from module import something is forbidden) and only using the other modules inside functions and methods (no mystuff = module.mystuff in the global space). That's because when importing starts, python puts the module name in sys.modules and won't try to import that module again.
You ran into trouble because when you run main.py, python adds __main__ to sys.modules. When the code finally came around to import main, there was no "main" in the module list and so main.py was imported again... and its top level code tried to run.
Lets rearrange your test case and throw in a few print statements to tell when import happens.
myParser.py
print(' + importing myParser')
import sys
print('import parsecmd')
import parsecmd
def parseEvnt():
sys.stdout.write("evnt:")
parsecmd.parseCmd(1)
tbl.py
print(' + importing tbl')
print('import myParser')
import myParser
tblx = {
1:("cmd",),
2:("evnt",myParser.parseEvnt),
}
Parsecmd.py (new)
print(' + importing parsecmd')
print('import tbl')
import tbl
def parseCmd(d):
print(tbl.tblx[d][0])
main.py
print('running main.py')
print('import parsecmd')
import parsecmd
if __name__ == "__main__":
data=[1,2]
for d in data:
if(d<2):
parsecmd.parseCmd(d)
else:
fce = parsecmd.tbl.tblx[d][1]
fce()
When I run it I get
running main.py
import parsecmd
+ importing parsecmd
import tbl
+ importing tbl
import myParser
+ importing myParser
import parsecmd <-- didn't reimport parsecmd
cmd
evnt:cmd
If you're insistent on not refactoring (which is the real solution to this - not being a wise guy), you could move your problematic import into your function in myParser.py
import sys
def parseEvnt():
import main ## import moved into function
sys.stdout.write("evnt:")
main.parseCmd(1)
Again, see if you can redesign your code so such interdependencies are avoided.
The above solution is sort of a hack and won't solve future problems you might run into due to this dependency.
Circular imports should be avoided. Refactoring is required, any workaround that still requires a circular import is not a good solution.
That being said, the refactoring doesn't have to be extensive. There are at least a couple of fairly simple solutions.
Solution 1: move shared functions to a shared module
Since you want to use parseCmd from more than one place, move it to a separate file. That way both main.py and myParser.py can import the function.
Solution 2: have main pass parseCmd to parseEvnt.
First, make parseEvnt accept an argument to tell it which function to run:
# myParser.py
import sys
def parseEvnt(parseCmd):
sys.stdout.write("evnt:")
parseCmd(1)
Next, when you call myParser.parseEvnt, pass in a reference to main.parseCmd:
# main.py:
...
else:
fce = tbl.tblx[d][1]
fce(parseCmd)
There are other ways to accomplish the same thing. For example, you could add a "configure" method in myParser, and then have main.py call the configure method and pass in a reference to its parseCmd. The configure method can then store this reference in a global variable.
Another choice is to import main in the function that uses it:
main.py
import sys
def parseEvnt():
import main
sys.stdout.write("evnt:")
main.parseCmd(1)

How do I execute a module's code from another?

I know it's a greenhorn question. But. I have a very simple module that contains a class, and I want to call the module to run from another. Like so:
#module a, to be imported
import statements
if __name__ == '__main__':
class a1:
def __init__(self, stuff):
do stuff
def run_proc():
do stuff involving 'a1' when called from another module
#Module that I'll run, that imports and uses 'a':
if __name__ == '__main__':
import a
a.run_proc()
However, for reasons that are likely obvious to others, I get the error Attribute Error: 'Module' object has no attribute 'run_proc' Do I need a static method for this class, or to have my run_proc() method within a class, that I initialize an instance of?
Move the
if __name__ == '__main__':
in module a to the end of the file and add pass or some test code.
Your problems are that:
Any thing in the scope of if __name__ == '__main__': is only considered in the top level file.
You are defining a class but not creating a class instance.
module a, to be imported
import statements
class a1:
def __init__(self, stuff):
do stuff
def run_proc():
#do stuff involving 'a1' when called from another module
if __name__ == '__main__':
pass # Replace with test code!
Module that I'll run, that imports and uses 'a':
import a
def do_a():
A = a.a1() # Create an instance
A.run_proc() # Use it
if __name__ == '__main__':
do_a()

What are the rules for __all__ and relative imports?

Inside a package I am using a few checks inside each module directory's __init__.py to see whether the environment is sane and then use from . import mod1 for nested modules (mod1 here). Each of the module directories in turn contains an __init__.py (obviously) which will import the next lower level (if any).
Some of the __init__.py contain __all__ arrays to only expose certain names upon import.
Let's say I have the following "main" script (spam.py):
import os, sys
if (sys.version_info[0] != 2) or (sys.version_info < (2,7)):
sys.exit("...")
else:
import pkgname
def main():
pass
if __name__ == '__main__':
main()
and the following pkgname/__init__.py:
import sys
if __name__ == '__main__':
sys.exit(0)
else:
from . import db
from os.path import dirname, realpath
sys.modules[__name__].__all__ = ['inipath']
sys.modules[__name__].__dict__['inipath'] = dirname(realpath(__file__)) + '.ini'
and the following pkgname/db/__init__.py:
import sys
if __name__ == '__main__':
sys.exit(0)
else:
import sqlite3
# ...
foobar = { 'spam' : 'egg' }
__all__ = ["foobar"]
will the symbol pkgname.db.foobar be visible despite the __all__ array in pkgname/__init__.py? I.e. does the __all__ array only affect the immediate module or also any lower level?
Which rules govern Python's behavior here? I tried some searches but came up empty-handed. Possibly I used the wrong searches (__all__, python, relative import)?
__all__ has no effect in the case you're describing. It only affects what happens when you do from package import *. See the documentation as well as previous questions (which I found by googling python __all__).

Categories