Import Error: 未知父包的相对导入尝试

我正在学习使用 python 编程,并且在从包中的模块导入时遇到了问题。我使用的是 Python 3.8.264位的可视化工作室代码。

我的项目目录

.vscode
├── ecommerce
│   ├── __init__.py
│   ├── database.py
│   ├── products.py
│   └── payments
│       ├── __init__.py
│       ├── authorizenet.py
│       └── paypal.py
├── __init__.py
└── main.py

ecommerce/products.py文件中我有:

#products.py
from .database import Database
p = Database(3,2)

这样我就可以从 ecommerce/database.py文件中导入 Database类,但是我得到了错误

ImportError : Attempted relative import with no known parent package
134512 次浏览

Since you are using Python 3.8 version, the imports work a little differently, but I think this should work:

Use either:

from database import Database
#Database is the class

or try:

import database.Database

lastly, this one is very secure and best practice possibly:

from . import Database
# The '.' (dot) means from within the same directory as this __init__.py module grab the Database class.

This may seem like a hack but it actually work, this happens because of bad package structure

In you case try importing

from .vs_code.ecommerce import database

Now wherever you want to call the class constructor do this:

database.Database()

you can assign this to a variable and use it.

Try this...

from ecommerce.database import Database

Presumably, you make executable the file "products.py", which violates the very concept of packages, the package ceases to be a package and relative import does not work. You must call this outside the package.

Here I am also not sure whether the root directory name can start with a dot like ".vscode".

i had a similar issue on Windows, and what helped me was (adapted to your directory):

# local imports
import sys
sys.path.append(r"C:\path\to\your\project")


from ecommerce.database import Database

Considering the below basic file structure

├── ecommerce
│   ├── __init__.py
│   ├── database.py
|   └── products.py
└── main.py

I am assuming you are running python main.py from the project root.

Here is the text copied from the Python tutorial that explains the basic rule around relative import,

Note that relative imports are based on the name of the current module. Since the name of the main module is always __main__, modules intended for use as the main module of a Python application must always use absolute imports.

So the below code will work,

# main.py
import ecommerce.products
# or to use it directly
# from ecommerce.products import my_product


ecommerce.products.my_product()

Your product.py might look like,

# ecommerce/products.py
from .database import Database


def my_product():
p = Database(3, 2)

And database.py will look like below,

# ecommerce/database.py


class Database():
def __init__(self, x, y):
self._x = x
self._y = y
print('Inside DB init')


# Rest of the methods...

You will now get,

> python main.py
Inside DB init

Ideally the __init__.py file at root is not required as the package name is starting from ecommerce.

You can also run python -m ecommerce.products command to directly call products.py. But that won't yield any output as we are not calling the my_product() function (only defining it).

Calling python ecommerce/products.py will not work as the name of the current module will then become __main__ and not ecommerce. The relative importing only works when used within the current package (so in your main script you always need to import your ecommerce package).

It seems, from Python docs and experimenting, that relative imports (involving ., .. etc) only work if

  1. the importing module has a __name__ other than __main__, and further,
  2. the __name__ of the importing module is pkg.module_name, i.e., it has to be imported from above in the directory hierarchy (to have a parent pkg as part of it's __name__.)

OR

the importing module is being specified via module syntax that includes a parent pkg as python -m pkg.module, in which case it's __name__ is still __main__, so it is being run as a script, yet relative imports will work. Here __package__ is set and used to find the parent package while __name__ is __main__; more here.

[After all that, it appears that __package__ and sys.path are key to determining if/how relative imports work. __name__ indicates script or module(i.e., __main__ or module_name). __package__ indicates where in the package the relative imports occur with respect to, and the top of __package__ needs to be in sys.path.]

So, continuing with @AmitTendulkar 's example, if you run this as > python main.py or > python -m main or > python -m ecommerce.products from the project root directory, or enter interactive python from that root directory and import main, or import ecommerce.products the relative imports in products.py will work.

But if you > python products.py or > python -m products from within ecommerce directory, or enter interactive python from that ecommerce directory and import products they will fail.

It is helpful to add

print("In module products __package__, __name__ ==", __package__, __name__)

etc. in each file to debug.

UPDATE:

How imports work depend on sys.path and __package__, not on __name__. Issued from /home/jj, > python sub/mod.py has a sys.path, __package__ of /home/jj/sub, None -absolute imports of modules in sys.path work, relative imports fail.

> python -m sub.mod has sys.path, __package__ of /home/jj, sub -absolute imports of modules in sys.path work, relative imports work relative to sys.path + __package__.

It is more helpful to add

import sys
print("In module products sys.path[0], __package__ ==", sys.path[0], __package__)

etc. in each file to debug.

To allow the use of relative imports, you need to "turn your code into a package". This means all of 1) putting an __init__.py in the top directory of your code (in your example .vscode) and 2) adding the full (absolute) path to the parent directory of the top directory (i.e., the parent of your directory .vscode) to your PYTHONPATH and 3) setting the __package__ variable in your Python program to the name of the directory that contains __init__.py, in your case ".vscode".

You should then be able to use in ecommerce/products.py:

from .ecommerce.database import Database

I'm not sure why the dot is in the name .vscode - is that part of the directory name, or part of the lines in your directory tree? If the latter, replace .vscode with vscode above.