python中的relative import使用起來卻有不少問題。
比如下面這樣一個結構
crawler |_src_ | |_spiders_ | |_test.py | |_local_settings.py
test.py中想import local_settings這個模塊,那在test.py中加上
import ...local_settings
運行 python test.py。 結果就是報了標題的錯誤。
查閱了一些資料,原因如下:
在python文檔https://docs.python.org/3.9/tutorial/modules.html#intra-package-references中有段話:
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.
還有在PEP 328(https://peps.python.org/pep-0328/#relative-imports-and-name)有:
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.
也就是說由於test.py中的__name__
attribute沒有設置,__name__
== __main__
,所以把test.py當成了頂層,這時reletive import就沒法使用了。
解決方法有一:
sys.path.append("../..") from local_setting import *
二:
不是直接運行test.py這個script,而是當作module來運行
python -m crawler.src.spiders.test
三:
不能從頂級模塊中的py中使用relieve import,在 https://docs.python.org/3/tutorial/modules.html#packages中有
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.請注意,相對導入是基於當前模塊的名稱。由於主模塊的名稱始終是"__main__",所以要作為 Python 應用程序主模塊使用的模塊必須始終使用絕對導入。
具體的分析過程參照:
https://stackoverflow.com/questions/30669474/beyond-top-level-package-error-in-relative-import
https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912
https://stackoverflow.com/questions/16981921/relative-imports-in-python-3
https://stackoverflow.com/questions/68960171/python-error-importerror-attempted-relative-import-with-no-known-parent-package
https://napuzba.com/a/import-error-relative-no-parent/
==============
在bypy項目下直接運行python bypy.py,也直接報這個錯
(venv) (base) gaoxianghu@gaoxiangs-MacBook-Pro bypy % python ./bypy/bypy.py __file__=/Users/gaoxianghu/git/bypy/bypy/struct.py | __name__=struct | __package__= Traceback (most recent call last): File "./bypy/bypy.py", line 65, in <module> import pickle File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/pickle.py", line 33, in <module> from struct import pack, unpack File "/Users/gaoxianghu/git/bypy/bypy/struct.py", line 11, in <module> from .util import iswindows ImportError: attempted relative import with no known parent package
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
可以看到__name__=struct,__package__沒有信息,因為name里沒有包含‘.’,表示不在package里,relative import只能用在package里,所以報:
attempted relative import with no known parent package
However, if your module's name is __main__
, it is not considered to be in a package. Its name has no dots, and therefore you cannot use from .. import
statements inside it. If you try to do so, you will get the "relative-import in non-package" error.
Scripts can't import relative
What you probably did is you tried to run moduleX
or the like from the command line. When you did this, its name was set to __main__
, which means that relative imports within it will fail, because its name does not reveal that it is in a package.
讓我們看看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__ ),那么相對引用會認為這個模塊就是頂級模塊,而不管模塊在文件系統上的實際位置。