ImportError : 在没有已知父包的情况下尝试相对导入

Isa*_*lio 55 python pylint

我正在学习使用 python 编程,但在从包中的模块导入时遇到问题。我正在使用带有 Python 3.8.2 64 位的视觉工作室代码。

我的项目目录

.vscode
??? ecommerce
?   ??? __init__.py
?   ??? database.py
?   ??? products.py
?   ??? payments
?       ??? __init__.py
?       ??? authorizenet.py
?       ??? paypal.py
??? __init__.py
??? main.py
Run Code Online (Sandbox Code Playgroud)

ecommerce/products.py我的文件中:

.vscode
??? ecommerce
?   ??? __init__.py
?   ??? database.py
?   ??? products.py
?   ??? payments
?       ??? __init__.py
?       ??? authorizenet.py
?       ??? paypal.py
??? __init__.py
??? main.py
Run Code Online (Sandbox Code Playgroud)

这样我就可以Databaseecommerce/database.py文件中导入类。但我得到错误

ImportError : Attempted relative import with no known parent package
Run Code Online (Sandbox Code Playgroud)

Fis*_*ode 13

由于您使用的是 Python 3.8 版本,因此导入的工作方式略有不同,但我认为这应该有效:

使用:

from database import Database
#Database is the class
Run Code Online (Sandbox Code Playgroud)

或尝试:

import database.Database
Run Code Online (Sandbox Code Playgroud)

最后,这可能是非常安全的最佳实践:

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

  • 为什么 python 社区中这个问题没有“正确”的答案? (12认同)
  • “进口的工作方式略有不同”以什么方式?当我使用 Python 3.8.3 时,我无法重现 OP 的问题(导入工作正常)并且您的解决方案不起作用,导致“ModuleNotFoundError”。我没看到什么?这篇文章在尝试理解导入系统时让我很困惑。顺便说一句,我的 `__init__.py` 是空的。 (6认同)
  • @Jupiter,它应该可以工作,但它在 3.8.5 上不适合我。有趣的是,在 VSCode 中运行的 pylance 确实在按照最后两个片段编写时解析了导入,因此我得到了代码完成,但是当我尝试运行代码时,它会抱怨“ImportError:尝试在没有已知父包的情况下进行相对导入”。只有第一个片段对我有用,但随后 pylance 失败了 (4认同)
  • https://docs.python.org/3/reference/import.html#package-relative-imports 显示OP尝试做的事情应该有效。 (3认同)
  • @BeyhanGul,Python 导入非常复杂,并且随着时间的推移会变得更糟。在这一点上,C `#include` 更好...... (2认同)

Ami*_*kar 9

考虑以下基本文件结构

\n
\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ecommerce\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82   \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 database.py\n|   \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 products.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 main.py\n
Run Code Online (Sandbox Code Playgroud)\n

我假设您是python main.py从项目根目录运行的。

\n

以下是从 Python 教程复制的文本,解释了相对导入的基本规则,

\n
\n

请注意,相对导入基于当前模块的名称。由于主模块的名称始终为__main__,因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。

\n
\n

所以下面的代码可以工作,

\n
# main.py\nimport ecommerce.products \n# or to use it directly \n# from ecommerce.products import my_product\n\necommerce.products.my_product()\n
Run Code Online (Sandbox Code Playgroud)\n

你的product.py可能看起来像,

\n
# ecommerce/products.py\nfrom .database import Database\n\ndef my_product():\n    p = Database(3, 2)\n
Run Code Online (Sandbox Code Playgroud)\n

database.py 如下所示,

\n
# ecommerce/database.py\n\nclass Database():\n    def __init__(self, x, y):\n        self._x = x\n        self._y = y\n        print('Inside DB init')\n\n    # Rest of the methods...\n
Run Code Online (Sandbox Code Playgroud)\n

你现在会得到,

\n
> python main.py\nInside DB init\n
Run Code Online (Sandbox Code Playgroud)\n

理想情况下,__init__.py不需要根目录下的文件,因为包名称是从 ecommerce 开始的。

\n

您也可以运行python -m ecommerce.products命令直接调用products.py。但这不会产生任何输出,因为我们没有调用 my_product() 函数(仅定义它)。

\n

调用python ecommerce/products.py将不起作用,因为当前模块的名称将变为__main__而不是ecommerce。相对导入仅在当前包中使用时才有效(因此在主脚本中您始终需要导入ecommerce包)。

\n


Don*_*wik 8

Python 文档和实验看来,相对导入(涉及 .、.. 等)仅在以下情况下才有效

  1. 导入模块有一个__name__除了__main__,更进一步,
  2. __name__进口模块是pkg.module_name,即,它已经从上方在目录层次结构中导入(具有父PKG作为它的一部分__name__。)

或者

导入模块是通过包含父 pkg as 的模块语法指定的python -m pkg.module,在这种情况下它__name__仍然是__main__,因此它作为脚本运行,但相对导入将起作用。这里__package__设置并用于查找父包 while __name__is __main__更多在这里

[之后,似乎所有__package__sys.path的关键是确定是否/如何相对导入工作。__name__表示脚本或模块(即,__main__module_name)。__package__指示相对导入发生在包中的哪个位置,并且顶部__package__需要位于sys.path.]

所以,用@AmitTendulkar的例子继续,如果您运行此作为> python main.py> python -m main> python -m ecommerce.products从项目的根目录,或从根目录进入交互Python和import main,或import ecommerce.products在products.py相对进口将正常工作。

但是,如果您> python products.py> python -m products从电子商务目录中,或从该电子商务目录输入交互式 python,import products它们将失败。

添加是有帮助的

print("In module products __package__, __name__ ==", __package__, __name__)
Run Code Online (Sandbox Code Playgroud)

等在每个文件中进行调试。

更新:

导入的工作方式取决于sys.path__package__,而不是__name__。从/home/jj,> python sub/mod.py发出sys.path, __package__of /home/jj/sub, -工作None模块的绝对导入sys.path,相对导入失败。

> python -m sub.modsys.path, __package__of /home/jj, sub- 模块的绝对导入sys.path工作,相对导入工作相对于sys.path+ __package__

添加更有帮助

import sys    
print("In module products sys.path[0], __package__ ==", sys.path[0], __package__)
Run Code Online (Sandbox Code Playgroud)

等在每个文件中进行调试。


小智 7

我在 Windows 上遇到了类似的问题,对我有帮助的是(适应您的目录):

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

from ecommerce.database import Database
Run Code Online (Sandbox Code Playgroud)

  • 这看起来是一种很老套的做法,但它有效并且最有意义。 (3认同)
  • 更简单并且对我有用:`sys.path.append(os.getcwd())`,python 3.7.6 (3认同)