I'm new to python, but skilled on java. Now I'm facing a annoying question when training on python3.5, I have such a src structure:
/project-root
--main.py
--ModelImpl.py #subclass of BaseModel
--ActionImpl.py #subclass of BaseAction
--/base
----BaseModel.py #base class of all
----BaseAction.py #subclass of BaseModel
----modulescript.py
in main.py:
from ModelImpl import ModelImpl
from ActionImpl import ActionImpl
modelImpl = ModelImpl()
actionImpl = ActionImpl()
in modulescript.py:
from BaseAction import BaseAction
baseAction = BaseAction()
in BaseModel.py:
class BaseModel:
def __init__(self):
print("BaseModel")
in BaseAction.py:
from .BaseModel import BaseModel
class BaseAction(BaseModel):
def __init__(self):
super().__init__()
print("BaseAction")
in ModelImpl.py:
from base.BaseModel import BaseModel
class ModelImpl(BaseModel):
def __init__(self):
super().__init__()
print("ModelImpl")
in ActionImpl.py:
from base.BaseAction import BaseAction
class ActionImpl(BaseAction):
def __init__(self):
super().__init__()
print("ActionImpl")
Now when I input python3 main.py in terminal, I got printed result:
BaseModel
ModelImpl
BaseModel
BaseAction
ActionImpl
but if I input python3 module function.py, I got error:
Traceback (most recent call last):
File "modulescript.py", line 1, in (module) from BaseAction import BaseAction
File "/home/jerry/projects/test/python/base/BaseAction.py", line 1, in (module) from .BaseModel import BaseModel SystemError: Parent module '' not loaded, cannot perform relative import
I found that It's cause by import statement in BaseAction.py:
from .BaseModel import BaseModel
If I change to from BaseModel import BaseModel, the modulescript.py runs ok, but the main.py will got a error:
File "main.py", line 225, in (module) from ActionImpl import ActionImpl
File "/home/jerry/projects/test/python/ActionImpl.py", line 1, in (module) from base.BaseAction import BaseAction
File "/home/jerry/projects/test/python/base/BaseAction.py", line 1, in (module) from BaseModel import BaseModel
ImportError: No module named 'BaseModel'
Oops. annoying!
In java, you can import any Class from any where if you provide the right path of the class like import com.domain.MyClass;
so, what's the right way to import in python?
This is Python, not Java, you group modules related to eachother in a single package, which is a folder.
To be able to import from a package, you must do a few things first.
Create __init__.py in your package to let interpreter know that it's a package. If there's no such file in a folder, it won't even budge whether you want to import from there or not, it's not a package to it and that's all.
Additionally, if you want (and you should when you're creating a package for others to use) import classes functions and etc in your __init__.py to allow to import them from the package itself directly.
I made a simple example:
project tree:
/root
test.py
/examplepkg
__init__.py
somemodule.py
EDIT: if you want examplepkg to have a "nested" package, as in, package it depends on in some way, you create another folder, and put another __init__.py in it and do the same thing, then in __init__.py of examplepkg you can further "export" it for the top level modules to see. How you do it is up to you, just be consistent.
somemodule.py:
class SomeClass:
def __init__(self):
print("New Instance of SomeClass!")
__init__.py:
from .somemodule import SomeClass # you could see it as exporting
test.py:
from examplepkg import SomeClass
SomeClass()
For more information, read this.
I got the same error but the error got fixed when I changed
from .BaseModel import BaseModel
to
from base.BaseModel import BaseModel
Related
I have the following project and module setup:
class2.py:
from mod1.class1 import Class1
class Class2(object):
def __init__(self, c1: Class1):
self.c1 = c1
class2.py:
from mod2.class2 import Class2
class Class1(object):
def __init__(self):
self.c2 = Class2(self)
main.py:
from mod1.class1 import Class1
from mod2.class2 import Class2
c1 = Class1()
c2 = Class2(c1)
The idea is that when a parent class initializing a child class, it introduces itself to the child class. This way the child class can call parent class methods.
This pattern works fine in strongly typed languages like C#.
When I run main.py, I get the following error:
Traceback (most recent call last):
File "C:\Projects.Personal\exp02\main.py", line 2, in <module>
from mod2.class2 import Class2
File "C:\Projects.Personal\exp02\mod2\class2.py", line 2, in <module>
from mod1.class1 import Class1
File "C:\Projects.Personal\exp02\mod1\class1.py", line 1, in <module>
from mod2.class2 import Class2
ImportError: cannot import name 'Class2' from partially initialized module 'mod2.class2' (most likely due to a circular import) (C:\Projects.Personal\exp02\mod2\class2.py)
Process finished with exit code 1
I just need to let Class2 know about the type of Class1.
Why am I getting such error?
What is the proper pattern to do this in Python?
Update #1
I understand that this is a circular import as it is specifically stated in the error message.
The idea explained in this article (https://stackabuse.com/python-circular-imports/) is not applicable here. Here I have Classes not module functions. I just need to annotate a variable. My code sample is very much simplified. The actual classes code is large. I don't want to combine them in one file.
IMO, This is a valid pattern and I can do it in Java or C#.
How can I implement the idea in Python?
Update #2: TYPE_CHECKING did not work
Per #RedKnite, I updated the class2.py to the following code:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mod2.class2 import Class2
class Class1(object):
def __init__(self):
self.c2 = Class2(self)
I am getting the the following error:
Traceback (most recent call last):
File "C:\Projects.Personal\exp02\main.py", line 6, in <module>
c1 = Class1()
File "C:\Projects.Personal\exp02\mod1\class1.py", line 8, in __init__
self.c2 = Class2(self)
NameError: name 'Class2' is not defined. Did you mean: 'Class1'?
Use the TYPE_CHECKING flag from typing and make the class a string.
At runtime TYPE_CHECKING is False so the import will not execute, but a type checker should find this acceptable.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from mod1.class1 import Class1
class Class2(object):
def __init__(self, c1: "Class1"):
self.c1 = c1
You have a circular import. Your Class1 and Class2 files are attempting to import each other- i'd suggest using one file that imports both classes.
EDIT: Turns out the inheritance is circular. Read on!
I am using Python 3.6. I tried to produce a minimal code sample to debug an import error I was having in a code project which was caused by the way I have set up inheritance. I suspect that I have done something wrong, but I don't understand what principle of coding I have violated.
The file structure looks like this:
temp
|-- __init__.py
|-- superclass_file.py
|-- subclass_file.py
|-- tests
|-- __init__.py
|-- test_subclass_file.py
These are the contents of the Python scripts:
# temp/__init__.py
from .subclass_file import *
from .superclass_file import *
# temp/superclass_file.py
class Parent:
def __init__(self):
print('I exist.')
# temp/subclass_file.py
from temp import Parent
class Child(Parent):
def __init__(self):
super().__init__()
print('And I exist because of it.')
def print_something(self):
print('Yes, I\'m here.')
if __name__ == '__main__':
Child()
# temp/tests/__init__.py
from .test_subclass_file import *
# temp/tests/test_subclass_file.py
from unittest import TestCase
from temp import Child
class TestChild(TestCase):
def check_inheritance(self):
c = Child()
c.print_something()
When I run check_inheritance(), it fails and throws this error:
/usr/local/bin/python3.6 ~/temp/subclass_file.py
Traceback (most recent call last):
File "~/temp/subclass_file.py", line 1, in <module>
from temp import Parent
File "~/temp/__init__.py", line 1, in <module>
from .subclass_file import *
File "~/temp/subclass_file.py", line 1, in <module>
from temp import Parent
ImportError: cannot import name 'Parent'
I obtain a similar error when I run the main() method in subclass_file.py.
However, the error disappears completely when I change the import of Parent in subclass_file.py from from temp import Parent to from temp.superclass_file import Parent.
Why does this happen? Why can't I use the same import signature I would use anywhere else—from temp import Parent—in the case of inheritance? Have I configured the __init__.py file incorrectly?
Right now you are importing subclass_file.py before superclass_file.py.
So when you try to do from temp import Parent, because subclass_file.py was imported before, it doesn't know what Parent is. Furthermore, you may create a circular import issue.
The best way should be to use from temp.superclass_file import Parent
If you really want / need to use the other way, then you must import superclass_file.py before subclass_file.py
Here is is the directory structure:
->src/dir/classes.py
->src/run.py
# classes.py
class A():
def methA():
# class A
class B():
def MethB():
# class B
class C():
def methC():
# class C
then i need to import Class A in run.py file.
from dir.classes import A
A.methA()
i already tried with using from dir.classes import A but it gives me
ModuleNotFoundError: No module named 'dir.classes'; 'classes' is not a package error
So how can i do that?
You need to put__init__.py file on your dir folder.
This way dir will be recognized as python package.
First, you must have __init__.py in each directory for Python to recognize them as packages.
Then, you should use from dir.classes import A. A is the name of the class, you shouldn't use Class A
This is my folder structure:
storage/
__init__.py - contains class StorageAbstract, class DummyStorage, class STORE_TYPE
file.py - contains class FileStorage
db/
__init__.py - contains class DbStorage, class DbStorageAbstract
pg.py - contains class PgStorage
sqlite.py - contains class SQLiteStorage
Classes PgStorage and SQLiteStorage inherit from DbStorageAbstract so I need to import it. I do it like this (in pg.py and sqlite.py) :
from . import DbStorageAbstract
And that throws the following error:
ImportError: cannot import name 'DbStorageAbstract'
However, when I move DbStorageAbstract from storage/db/__init__py. to storage/__init__py and import it like this:
from .. import DbStorageAbstract
then it works fine. I have read this, this and many other resources but still can't figure out what is causing the problem. If it was circular dependence then moving the class to another file would not help I think.
If there is any more information needed, let me know in the comments and I will edit the question.
I am using Python 3.5
EDIT:
Although this question has been identified as a possible duplicate of this question I don't see how it answers my question. Unlike the other question, I already have init files in each folder. If I am wrong please point me to where I can find the answer.
EDIT 2:
This is the db/init.py file:
##################################################################
# Copyright 2018 Open Source Geospatial Foundation and others #
# licensed under MIT, Please consult LICENSE.txt for details #
##################################################################
#import sys
#sys.path.append("/mnt/c/Users/Jan/Documents/GitHub/pywps")
import logging
from abc import ABCMeta, abstractmethod
from pywps import configuration as config
from .. import StorageAbstract
from . import sqlite
from . import pg
LOGGER = logging.getLogger('PYWPS')
class DbStorageAbstract(StorageAbstract):
"""Database storage abstract class
"""
__metaclass__ = ABCMeta
#abstractmethod
def store(self, output):
pass
#abstractmethod
def store_output(self, file_name, identifier):
pass
class DbStorage(StorageAbstract):
def __init__(self):
self.storage = self.get_db_type()
def store(self, output):
assert(self.storage is not None)
self.storage.store(output)
def get_db_type(self):
# get db_type from configuration
try:
db_type = config.get_config_value('db', 'db_type')
except KeyError:
raise exception("Database type has not been specified")
# create an instance of the appropriate class
if db_type == "PG":
storage = pg.PgStorage()
elif db_type == "SQLITE":
storage = sqlite.SQLiteStorage()
else:
raise exception("Unknown database type: '{}'".format(db_type))
return storage
Imports happens before class definition. In db/__init__.py you are importing pg.py, sqlite.py which are depends on classes defined here. To solve it move DbStorageAbstract to another file and if you are going to use it in the same file with pg.py, sqlite.py then make sure import is going to be like this:
from . import dbstorage # this file is the new one contains DbStorageAbstract class
from . import pg, sqlite
Note that if two modules are depends on each other try to create a third module so solve this problem.
I believe you should create another file in your subdirectory
storage/
__init__.py - contains class StorageAbstract, class DummyStorage, class STORE_TYPE
file.py - contains class FileStorage
db/
__init__.py -
pg.py - contains class PgStorage
sqlite.py - contains class SQLiteStorage
base.py contains class DbStorage, class DbStorageAbstract
Then, the pg.py file and sqlite.py file will import the classes like:
from base import DbStorageAbstract
And the __init__.py file will also look like this
from base import DbStorageAbstract, DbStorageAbstract
I'm writing a python package (python 3.6) and have the following directory structure:
package/
| __init__.py
| fileA.py
| fileB.py
| tests/
| | __init__.py
| | test_classA.py
| | test_classB.py
Setup
My files have the following contents:
# package/fileA.py
from package import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
-
# package/fileB.py
def ClassB:
def __init__(self):
self.foo = "bar"
-
# package/tests/test_classB.py
from package import ClassB
# <performs some unit tests here>
-
# package/tests/test_classA.py
from package import ClassA
# <performs some unit tests here>
-
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
Circular importing
When I ran python test_classB.py, I get the following traceback error showing that I have circular import statements, which are not allowed by python. Note - the package is not literally called package and I have edited the Traceback to match the toy example above.
Traceback (most recent call last):
File "package/tests/test_classB.py", line 2, in <module>
from package import ClassB
File "/anaconda/lib/python3.5/site-packages/package/__init__.py", line 2, in <module>
from .fileA import ClassA
File "/anaconda/lib/python3.5/site-packages/package/merparse.py", line 2, in <module>
from package import ClassB
ImportError: cannot import name 'ClassB'
Correcting the error
However, when I remove those two lines in my package/__init__.py file:
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
...and I change the import method for package/fileA.py:
# package/fileA.py
from package.fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
... package/tests/test_classB.py runs correctly.
My Question
My question is: How can I keep the one file: one class structure in my files and import with from package import ClassA instead of having to import with from package.fileA import ClassA?
In my package I would like to import classes from other files, but don't know how to get around circular importing.
Edit: Solution
Thanks to #mirandak and #martin-kalcok below for their help.
The only file that I had to edit was fileA.py to not refer to the package name in the import statement.
# package/fileA.py
from .fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
The package/__init__.py file still contains import statements in case I want to import the package from other scripts in the future that I don't couple with the package.
# package/__init__.py
from .fileA import ClassA
from .fileB import ClassB
The problem is with package/fileA.py. It's both a part of package and calling the __init__.py file of package as it's imported - creating a circular dependency.
Can you change fileA.py to look like this?
# package/fileA.py
from .fileB import ClassB
def ClassA:
def __init__(self):
self.my_ClassB = ClassB()
First of all, renaming your package to literary "package" really confuses this whole situation, but here's my take on it :D.
I don't think you are dealing with circular dependency, python just states that it cannot import classB from package and that is because there is no file named classB in package directory. Changing import statement to
from package.fileB import ClassB
works because there is directory package which contains file fileB which contains object ClassB.
To answer your question whether you can import classes with statement
from package import ClassB
you can't, unless your package is file and classB is object inside this file. However i don't see how this different import statement would be a dealbraker
Side Note: Your objects ClassB and ClassA are not classes, they are functions. Python uses keyword class to create classes and def to define functions.
Side note 2: Are you sure your __init__.py file needs to hold any code at all?
Personal Note: I know that keeping 1 File / 1 Class structure is matter of personal preferences, it is not required in python and I myself find it much more comfortable to group multiple related classes and functions into one file