[譯][python]ImportError:attempted relative import with no known parent package


前言

在這篇文章中,我將會解析 ImportError: attempted relative import with no known parent package 這個異常的原因。當你在運行的python腳本。使用了相對引用方式 (類似import ..module) 去引用包時,可能會出現這個異常。

讓我們來看看發生這個異常的例子。

問題

假設你有以下目錄結構:

project
├── config.py
└── package
    ├── __init__.py
    └── demo.py

config.py 中包含一些應該在 demo.py 中使用的變量

  • project/config.py
count = 5
  • project/package/demo.py
from .. import config
print("The value of config.count is {0}".format(config.count))

當我們嘗試運行demo.py時,會遇到以下錯誤:

E:\project> python demos/demo.py
Traceback (most recent call last):
  File "demos/demo.py", line 1, in <module>
    from .. import config
ImportError: attempted relative import with no known parent package

python解釋器拋出了沒有父級包的異常。為什么?

讓我們看看python解釋器是如何解析相關模塊。從 PEP 328 中,我們找到了關於 the relative imports(相對引用)的介紹:

Relative imports use a module’s __name__ attribute to determine that module’s position in the package hierarchy. If the module’s name does not contain any package information (e.g. it is set to __main__ ) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

相對導入通過使用模塊的 __name__ 屬性來確定模塊在包層次結構中的位置。如果該模塊的名稱不包含任何包信息(例如,它被設置為 __main__ ),那么相對引用會認為這個模塊就是頂級模塊,而不管模塊在文件系統上的實際位置。

換句話說,解決模塊的算法是基於__name____package__變量的值。大部分時候,這些變量不包含任何包信息 —- 比如:當 __name__ = __main__ 和 __package__ = None 時,python解釋器不知道模塊所屬的包。在這種情況下,相對引用會認為這個模塊就是頂級模塊,而不管模塊在文件系統上的實際位置。

為了演示這個原理,我們來更新一下代碼:

  • project/config.py
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
count = 5
  • project/package/demo.py
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
from .. import config
print("The value of config.count is {0}".format(config.count))

再次嘗試運行一下,會得到以下輸出:

E:\project> python demos/demo.py
__file__=demos/demo.py      | __name__=__main__    | __package__=None
Traceback (most recent call last):
  File "demos/demo.py", line 3, in <module>
    from .. import config
ImportError: attempted relative import with no known parent package

正如我們所看到的,python解釋器沒有關於模塊所屬的包的任何信息( __name__ = __main__ 和 __package__ = None ),因此它拋出了找不到父級包的異常。

解決方案一

  • 我們通過在其中創建一個新的空 __init__.py 文件來將項目目錄轉換為一個包。

  • 我們在項目目錄的父目錄中創建一個文件 main.py

toplevel
├── main.py
└── project
  ├── __init__.py
  ├── config.py
  └── package
      ├── __init__.py
      └── demo.py
  • toplevel/main.py
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
import project.demos.demo

執行一下新的示例,輸出如下:

E:\toplevel>python main.py
__file__=main.py                             | __name__=__main__             | __package__=None
__file__=E:\toplevel\project\demos\demo.py   | __name__=project.demos.demo   | __package__=project.demos
__file__=E:\toplevel\project\config.py       | __name__=project.config       | __package__=project
The value of config.count is 5

在 main.py 中導入 project.demos.demo 會設置相對引用的包信息( __name__ 和 __package__ 變量)。現在,python解釋器可以成功解析 project\demos\demo.py 中的相對引用了。

解決方案二

  • 我們通過在 project 文件夾中創建一個新的空 __init__.py 來將 project 目錄轉換為一個包。

  • 在 toplevel 目錄下通過 -m 參數來調用python解釋器,去執行 project.demos.demo[1]

toplevel
└── project
  ├── __init__.py
  ├── config.py
  └── package
      ├── __init__.py
      └── demo.py

再次執行:

E:\toplevel>python -m project.demos.demo
__file__=E:\toplevel\project\demos\demo.py   | __name__=__main__        | __package__=project.demos
__file__=E:\toplevel\project\config.py       | __name__=project.config  | __package__=project
The value of config.count is 5

運行該命令將自動設置包信息(__package__變量)。現在,python解釋器可以成功解析 project\ demos\demo.py 中的相對引用了(甚至認為 __name __ = __ main__ )。

[1] 譯者注:注意使用 -m 參數的時候,后面指定的執行文件沒有 .py 后綴

import-error-relative-no-parent


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM