赞
踩
在使用python3.7的时候遇到了导入的疑问,在项目中会使用到from .. import XXX 或者from . import XXX 的情况,但是有时使用相对路径导入又会报错,我们来详细的了解一下其中的原理。
1.导入包和模块时的相对路径和绝对路径
举例:
绝对导入:import t1.a 或者 from t1 import a 等。
相对导入:from . import a 或者 from .. import a 或者 from ..t1 import a 或者 from .t1 import a等。
2.import导入模块
首先,我们来看一下导入包和导入模块的不同。
包:通常是一个目录,包目录下为首的一个文件便是 __init__.py,然后是一些模块文件和子目录,假如子目录中也有 __init__.py 那么它就是这个包的子包了。
模块:可理解为对应于一个.py文件。在创建了一个脚本文件后,定义了某些函数和变量。在其他需要这些功能的文件中,导入这模块,就可重用这些函数和变量。
假设我们现有有一个项目结构如下图:
其中没有__init__.py文件,全部当作模块来处理。我们尝试在t1_a.py中导入t1_b.py模块,运行t1_a.py查看结果:
# t1_a.py
import sys
print("t1_a的path:", sys.path)
print("t1_a的__name__:", __name__)
import t1_b
# t1_b.py
import sys
print("t1_b的path:", sys.path)
print("t1_b的__name__:", __name__)
#运行结果
t1_a的path: ['C:\\Users\\wyqsy\\PycharmProjects\\untitled\\test\\t1', 'C:\\Users\\wyqsy\\PycharmProjects\\untitled', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_display', 'D:\\software\\Anaconda\\python37.zip', 'D:\\software\\Anaconda\\DLLs', 'D:\\software\\Anaconda\\lib', 'D:\\software\\Anaconda', 'D:\\software\\Anaconda\\lib\\site-packages', 'D:\\software\\Anaconda\\lib\\site-packages\\win32', 'D:\\software\\Anaconda\\lib\\site-packages\\win32\\lib', 'D:\\software\\Anaconda\\lib\\site-packages\\Pythonwin', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_matplotlib_backend']
t1_a的__name__: __main__
t1_b的path: ['C:\\Users\\wyqsy\\PycharmProjects\\untitled\\test\\t1', 'C:\\Users\\wyqsy\\PycharmProjects\\untitled', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_display', 'D:\\software\\Anaconda\\python37.zip', 'D:\\software\\Anaconda\\DLLs', 'D:\\software\\Anaconda\\lib', 'D:\\software\\Anaconda', 'D:\\software\\Anaconda\\lib\\site-packages', 'D:\\software\\Anaconda\\lib\\site-packages\\win32', 'D:\\software\\Anaconda\\lib\\site-packages\\win32\\lib', 'D:\\software\\Anaconda\\lib\\site-packages\\Pythonwin', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_matplotlib_backend']
t1_b的__name__: t1_b
可以看到t1_a.py在导入的时候会搜索的路径包括了t1所在的路径,也包括了untitled这个项目所在的根目录。所以我们在导入t1_b的时候可以使用import t1_b就可以在和t1_a平级的路径中找到t1_b。再去思考,既然路径中也包含了untitled根目录的路径,可不可以使用import test.t1.t1_b的方式导入模块呢?
# t1_a.py
import sys
print("t1_a的path:", sys.path)
print("t1_a的__name__:", __name__)
import test.t1.t1_b
Traceback (most recent call last):
File "C:/Users/wyqsy/PycharmProjects/untitled/test/t1/t1_a.py", line 5, in
import test.t1.t1_b
ModuleNotFoundError: No module named 'test.t1'
使用import test.t1.t1_b的方式导入结果报错没有模块‘test.t1’。此处test和t1都是文件夹,不是.py文件,不能做作为模块使用这种导入。以上是t1_a.py导入同级目录下的文件,如果要导入父级目录呢?在t1_a.py中直接使用import t2或者import test.t2都是显然不正确的,那么from .. import t2是否成立呢?结果如下图报错找不到父级包,此处我们就应该意识到只有包可以使用..或者.这样相对路径的方式导入,在下一小节再去详细了解。
这里还应该注意到的是,运行t1_a.py的时候,t1_a.py的__name__即木块的名字是__main__,说明它是主模块,t1_b.py的__name__是t1_b,即这个模块本身的名字。这也说明了为什么不可以用..或.这样的相对路径导入,python没有办法从__main__或者t1_b这样的模块名里取判断它的父级目录是谁。
如果想在t1_a中导入t2,可以采用sys.path.append("父级目录绝对路径")的方式把父级目录添加到查找包的路径里,再去import t2就可以了。
Traceback (most recent call last):
File "C:/Users/wyqsy/PycharmProjects/untitled/test/t1/t1_a.py", line 5, in
from .. import t2
ImportError: attempted relative import with no known parent package
t1_a的path: ['C:\\Users\\wyqsy\\PycharmProjects\\untitled\\test\\t1', 'C:\\Users\\wyqsy\\PycharmProjects\\untitled', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_display', 'D:\\software\\Anaconda\\python37.zip', 'D:\\software\\Anaconda\\DLLs', 'D:\\software\\Anaconda\\lib', 'D:\\software\\Anaconda', 'D:\\software\\Anaconda\\lib\\site-packages', 'D:\\software\\Anaconda\\lib\\site-packages\\win32', 'D:\\software\\Anaconda\\lib\\site-packages\\win32\\lib', 'D:\\software\\Anaconda\\lib\\site-packages\\Pythonwin', 'D:\\software\\pycharm\\PyCharm 2019.1.2\\helpers\\pycharm_matplotlib_backend']
t1_a的__name__: __main__
3.import导入包
添加__init__.py文件,使test变成一个包,包含子包和子模块。__init__.py文件可以是空的。
此时在t1_a.py尝试用两种方式导入t1_b.py。
使用import t1_b和import test.t1.t1_b的方法都可以导入t1_b.py。区别注意看t1_b的__name__,会发现import t1_b直接导入的时候t1_b的__name__是本身的模块名,import test.t1.t1_b导入的时候t1_b的__name__是test.t1.t1_b这样完整的路径。
__name__在t1_a中没有区别,都是被执行的顶层模块,但是在t1_b中导入其他包时就体现出了区别。
a)第一种情况:在t1_a中,import t1_b方法,t1_b的__name__是t1_b,运行t1_a为主模块
在t1_b中分别尝试下图三种导入方法都是正确的。方法一,直接导入同级目录下的模块,方法二和三,使用绝对路径导入包中的其他模块,可以是同级目录当然也可以不同级。
import t1_a # 方法一
import test.t1.t1_a # 方法er
import test.t2 # 方法三
但是在t1_b使用下图from .. import t2,相对路径的方法导入就会报错,因为此时在t1_b中的__name__是t1_b,无法找到父级目录。
b)第二种情况:在t1_a中,import test.t1.t1_b方法,t1_b的__name__是test.t1.t1_b,运行t1_a为主模块
此时在t1_b中使用相对路径
from .. import t2
是可以成功导入的,需要注意的是此时t1_b中的__name__是test.t1.t1_b,有完整的层级结构,所以此时可以使用相对路径来找到它的父级目录是谁。当然此时用绝对路径导入也是可以的。
4.总结
a) 导入模块,只能在sys.path中包含的路径下去找要导入的模块。
b)导入包,可以采用直接路径或者相对路径。顶层模块(被运行的模块)可以直接导入同级目录下的其他子包或子模块,或者通过绝对路径导入其他子包或子模块。被顶层模块通过绝对路径调用的子包或子模块,可以在子包或子模块内部通过相对路径的方式导入其他包或者模块。
c)简单好记版:一个包中的某个模块的__name__如果包含了完整结构,如test.t1.t1_b,则在这个模块里可以使用相对路径,否则不可以。顶层模块的__name__一直是__main__,所以都不可以使用相对路径导入。
原文链接:https://blog.csdn.net/qq_31334901/article/details/107632488
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。