python3导入模块原理_import导入包和模块,相对导入和绝对导入

在使用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文件。在创建了一个脚本文件后,定义了某些函数和变量。在其他需要这些功能的文件中,导入这模块,就可重用这些函数和变量。

假设我们现有有一个项目结构如下图:

20200728115335141.png

其中没有__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文件可以是空的。

20200728142306706.png

此时在t1_a.py尝试用两种方式导入t1_b.py。

20200728142854267.png

20200728142903303.png

使用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,无法找到父级目录。

20200728144824889.png

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,有完整的层级结构,所以此时可以使用相对路径来找到它的父级目录是谁。当然此时用绝对路径导入也是可以的。

20200728145105774.png

4.总结

a) 导入模块,只能在sys.path中包含的路径下去找要导入的模块。

b)导入包,可以采用直接路径或者相对路径。顶层模块(被运行的模块)可以直接导入同级目录下的其他子包或子模块,或者通过绝对路径导入其他子包或子模块。被顶层模块通过绝对路径调用的子包或子模块,可以在子包或子模块内部通过相对路径的方式导入其他包或者模块。

c)简单好记版:一个包中的某个模块的__name__如果包含了完整结构,如test.t1.t1_b,则在这个模块里可以使用相对路径,否则不可以。顶层模块的__name__一直是__main__,所以都不可以使用相对路径导入。

原文链接:https://blog.csdn.net/qq_31334901/article/details/107632488

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值