【问题标题】:ImportError : Attempted relative import with no known parent packageImportError : 在没有已知父包的情况下尝试相对导入
【发布时间】:2020-06-20 22:23:08
【问题描述】:

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

My Project Directory

.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

【问题讨论】:

    标签: python pylint


    【解决方案1】:

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

    使用任一:

    from database import Database
    #Database is the class
    

    或尝试:

    import database.Database
    

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

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

    【讨论】:

    • "导入的工作方式略有不同" 以什么方式?我无法重现 OP 的问题(导入工作正常)并且您的解决方案不起作用,导致ModuleNotFoundError,而我使用的是 Python 3.8.3。我没看到什么?在尝试了解导入系统时,这篇文章让我很困惑。顺便说一句,我的__init__.py 是空的。
    • docs.python.org/3/reference/… 表明 OP 正在尝试做的事情应该有效。
    • @Jupiter,它应该可以工作,但在 3.8.5 上它不适合我。有趣的是,在 VSCode 中运行的 pylance 确实解决了在最后两个 sn-ps 中编写的导入,所以我得到了代码完成,但是当我尝试运行代码时,它抱怨,ImportError: attempted relative import with no known parent package。只有第一个 sn-p 对我有用,但是 pylance 失败了
    • 为什么在 python 社区中这个问题没有“正确”的答案?
    【解决方案2】:

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

    1. 导入模块有一个__name__,而不是__main__,此外,
    2. 导入模块的__name__ 是pkg.module_name,即,它必须从目录层次结构中的上方导入(要将父pkg 作为__name__ 的一部分。)

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

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

    所以,继续@AmitTendulkar 的示例,如果您从项目根目录以> python main.py> python -m main> python -m ecommerce.products 运行它,或者从该根目录和import main 或@ 输入交互式python 987654345@ products.py 中的相对导入将起作用。

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

    添加有帮助

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

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

    更新:

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

    > python -m sub.mod 具有 sys.path__package__/home/jjsub - sys.path 中模块的绝对导入工作,相对导入工作相对于 sys.path + __package__

    加起来更有帮助

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

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

    【讨论】:

    • 确实,我发现我需要 all of 1) 将 __init__.py 放在我的代码的顶部目录 2) 添加我的PYTHONPATH 的顶级目录的父目录 3)将我的Python程序中的__package__变量设置为包含__init__.py的目录的名称。
    • 听起来类似于“更多此处”链接的建议更改下第四段中描述的样板。如果您调用 as > python code.py,那么调用 as > python -m dir.code 是否像我的 UPDATE 那样消除对 2) 和 3) 的需要?
    • 排序;我需要稍微调整相对导入,在看起来成功运行之后,我得到python3: Error while finding module specification for 'dir.code.py' (ModuleNotFoundError: __path__ attribute not found on 'dir.code' while trying to find 'dir.code.py')
    【解决方案3】:

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

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

    【讨论】:

    • 这似乎是一种很老套的做法,但它确实有效且最有意义。
    • @TannerDavis 我同意它不是最便携的,但解决了 OP 的问题。当我们开发的环境与脚本运行的环境明显不同时,我不得不使用它,因此必须对位置进行硬编码。对于可移植性,pathlib 可能更好,声明一个根文件夹并附加到路径,使用如下内容:root_folder = r'{}'.format(pathlib.Path(pathlib.Path(__file__).parent.absolute().parent)),在这里你必须弄清楚你的模块到底在哪里。
    【解决方案4】:

    考虑以下基本文件结构

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

    我假设您正在从项目根目录运行 python main.py

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

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

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

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

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

    # ecommerce/products.py
    from .database import Database
    
    def my_product():
        p = Database(3, 2)
    

    database.py 如下所示,

    # ecommerce/database.py
    
    class Database():
        def __init__(self, x, y):
            self._x = x
            self._y = y
            print('Inside DB init')
    
        # Rest of the methods...
    

    你现在会得到,

    > python main.py
    Inside DB init
    

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

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

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

    【讨论】:

      【解决方案5】:

      要允许使用相对导入,您需要“将代码转换为包”。这意味着所有 1)将__init__.py 放在代码的顶级目录中(在您的示例中为.vscode 2)添加完整(绝对)路径到顶级目录的父目录(即,您的目录.vscode)到您的PYTHONPATH 3)在您的Python程序到包含__init__.py的目录的名称,在你的例子中是“.vscode”。

      你应该可以在ecommerce/products.py中使用:

      from .ecommerce.database import Database
      

      我不知道为什么点在名称 .vscode 中 - 这是目录名称的一部分,还是目录树中行的一部分?如果是后者,请将上面的.vscode 替换为vscode

      【讨论】:

        【解决方案6】:

        这可能看起来像一个 hack,但它确实有效,这是因为包结构不好

        在你的情况下尝试导入

        from .vs_code.ecommerce import database

        现在,无论您想调用类构造函数,请执行以下操作:

        database.Database()

        您可以将其分配给变量并使用它。

        【讨论】:

          【解决方案7】:

          试试这个...

          from ecommerce.database import Database
          

          【讨论】:

            【解决方案8】:

            大概,您使文件“products.py”可执行,这违反了包的概念,包不再是包,并且相对导入不起作用。您必须在包外调用它。

            这里我也不确定根目录名称是否可以以“.vscode”之类的点开头。

            【讨论】:

              【解决方案9】:

              试试下面的

              from ecommerce import database
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-11-24
                • 2020-10-13
                • 2021-10-31
                • 2021-10-20
                • 2021-12-19
                • 2020-11-03
                • 2019-07-31
                • 2020-09-22
                相关资源
                最近更新 更多