046.Python包和模块_导入相关
我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
导入相关
包和模块的导入
理论基础
- 导入模块就可以使用这个模块里面的内容
常规导入import
- 作用
- 导入一个模块中所有资源到当前位置
- 导入方式
- 方式一:导入单个模块
import 模块名 import 包名.模块名 import 包名.子包名.模块名 # 如果是某个包里面的模块,可以通过点语法来定位 # 导入多层级包里面的模块,会分别执行每一级包里面的__init__.py文件
- 方式二:导入多个模块
# 方式1 import 模块名1 import 模块名2 # 方式2 import 模块名1, 模块名2 import 模块名1, 包名.子包名.模块名2
- 方式三:给导入的包和模块起别名
import 模块名 as 别名 import 包名.子包名.模块名 as 别名 # 后续使用可以直接通过 别名.方法名() / 别名.属性 使用
- 起别名的好处
- 1、简化资源访问前缀
- 2、增加程序的扩展性
- 好处1示例
# 不起别名 import 包名.子包名.模块名 print(包名.子包名.模块名.属性1) # 不起别名,使用时,前缀太长太复杂 print(包名.子包名.模块名.属性2) print(包名.子包名.模块名.属性3) # 起别名 import 包名.子包名.模块名 as m print(m.属性1) # 起别名后,使用时,简单 print(m.属性2) print(m.属性3)
- 好处2示例
# 根据文件后缀调用不同模块,对文件进行打开、读取、关闭操作 # 不起别名 if file_extension == 'txt': import txt_parse txt_parse.open() txt_parse.read() txt_parse.close() elif file_extension == 'doc': import doc_parse doc_parse.open() doc_parse.read() doc_parse.close() # 这里不同的文件类型,对文件的打开、读取、关闭操作都是一样的,仅仅是使用的模块不同 # 起别名 if file_extension == 'txt': import txt_parse as p elif file_extension == 'doc': import doc_parse as p p.open() p.read() p.close() # 整个代码就可以将文件的打开、读取、关闭操作进行提取整合,减少代码冗余
- 注意
- 通过
import
导入包和模块,在使用包和模块内部资源时,需要指明资源的包和模块名称 - 如:
包名.子包名.模块.属性
、包名.子包名.模块.方法()
- 通过
- 补充
- 如果通过
import 包名
的方式导入一个包,默认不会导入任何模块
- 如果通过
- 解决方案
- 1、在导入包的时候会自动执行包里面的
__init__.py
文件,所以可以在__init__.py
文件中再次导入需要的模块-
这里导入模块的时候需要写明模块的绝对路径,是因为模块检索路径的问题,后续会讲
-
- 2、应该以
from....import....
的形式导入
- 1、在导入包的时候会自动执行包里面的
from导入
-
作用
- 导入一个模块或包里面某一部分资源
-
语法
from A import B[ as 别名] # 从A导入B资源到当前位置
- 理解
- 导入顺序:从大的地方找小的东西
- 按照大小资源排序:包 > 模块 > 资源
- 注意面向关系
- 包里面只能看到模块,看不到模块资源
- 模块里面只能看到模块资源
- 导入顺序:从大的地方找小的东西
- 理解
-
最终组合
- 1、从包中导入模块
- 2、从模块中导入模块资源
-
语法
- 语法1:从包中导入模块
# 导入单个模块 from 包 import 模块 # 导入多个模块 from 包 import 模块1, 模块2 # 起别名 from 包 import 模块 as 别名 from 包 import 模块1 as 别名1, 模块2 as 别名2 # 包有多层级 from 包.子包 import 模块 # 使用模块资源 模块.资源
- 语法2:从模块中导入模块资源
# 导入单个资源 from 模块 import 资源 # 导入多个资源 from 模块 import 资源1, 资源2 # 起别名 from 模块 import 资源 as 别名 from 模块 import 资源1 as 别名1, 资源2 as 别名2 # 模块有多层级 from 包.模块 import 资源 # 使用资源 资源
- 语法1:从包中导入模块
-
示例
-
先创建几个简单的模块
# host_module1.py h_m1_num1 = 100 h_m1_num2 = 199 # host_module2.py h_m1_num1 = 200 h_m1_num2 = 299 # sub_module1.py s_m1_num1 = 1100 s_m1_num2 = 1199 # sub_module2.py s_m2_num1 = 2100 s_m2_num2 = 2199
-
通过
test.py
分别导入这些模块- 示例1:从包中导入模块
# 导入单个模块 from host_package import host_module1 print(host_module1.h_m1_num1) # 100 print(host_module1.h_m1_num2) # 199 # 导入多个模块 from host_package import host_module1, host_module2 print(host_module1.h_m1_num1) # 100 print(host_module1.h_m1_num2) # 199 print(host_module2.h_m2_num1) # 200 print(host_module2.h_m2_num2) # 299 # 起别名1 from host_package import host_module1 as hm1 print(hm1.h_m1_num1) # 100 print(hm1.h_m1_num2) # 199 # 起别名2 from host_package import host_module1 as hm1, host_module2 as hm2 print(hm1.h_m1_num1) # 100 print(hm1.h_m1_num2) # 199 print(hm2.h_m2_num1) # 200 print(hm2.h_m2_num2) # 299 # 包有多层级 from host_package.sub_package import sub_module1 print(sub_module1.s_m1_num1) # 1100 print(sub_module1.s_m1_num2) # 1199 from host_package.sub_package import sub_module1, sub_module2 print(sub_module1.s_m1_num1) # 1100 print(sub_module1.s_m1_num2) # 1199 print(sub_module2.s_m2_num1) # 2100 print(sub_module2.s_m2_num2) # 2199 from host_package.sub_package import sub_module1 as sm1, sub_module2 as sm2 print(sm1.s_m1_num1) # 1100 print(sm1.s_m1_num2) # 1199 print(sm2.s_m2_num1) # 2100 print(sm2.s_m2_num2) # 2199 # 错误演示 from host_package import sub_package.sub_module1 print(sub_package.sub_module1.s_m1_num1) # ========== 输出结果 ========== # File "E:\StudyCode\Python\15-包和模块\test.py", line 44 # from host_package import sub_package.sub_module1 # ^ # SyntaxError: invalid syntax
- 示例2:从模块中导入资源
- 调整
test.py
文件位置,与host_module1.py
模块在同一级目录
# 导入单个资源 from host_module1 import h_m1_num1 print(h_m1_num1) # 100 # print(h_m1_num2) # 报错:NameError: name 'h_m1_num2' is not defined # 导入多个资源 from host_module1 import h_m1_num1, h_m1_num2 print(h_m1_num1) # 100 print(h_m1_num2) # 199 # 起别名1 from host_module1 import h_m1_num1 as hm1 print(hm1) # 100 # 起别名2 from host_module1 import h_m1_num1 as hm1, h_m1_num2 as hm2 print(hm1) # 100 print(hm2) # 199 # 模块有多层级 from sub_package.sub_module1 import s_m1_num1 print(s_m1_num1) # 1100 from sub_package.sub_module1 import s_m1_num1, s_m1_num2 print(s_m1_num1) # 1100 print(s_m1_num2) # 1199 # 错误演示 from sub_package import sub_module1.s_m1_num1 print(sub_module1.s_m1_num1) # ========== 输出结果 ========== # File "E:\StudyCode\Python\15-包和模块\host_package\test.py", line 85 # from sub_package import sub_module1.s_m1_num1 # ^ # SyntaxError: invalid syntax
- 调整
- 示例1:从包中导入模块
-
注意
from A import B
保证B部分路径最简,从哪里能看到谁才能导入谁- 错误示例
from 大包 import 小包.模块
- 1、
import
后面没有做到路径最简化 - 2、在 大包 层级并不能直接看到 模块
- 修改:
from 大包.小包 import 模块
- 1、
from 包 import 模块.资源
- 1、
import
后面没有做到路径最简化 - 2、在 包 层级并不能直接看到 模块内资源
- 修改:
from 包.模块 import 资源
- 1、
-
导入特例
- 批量导入模块中的资源
from 模块 import *
- 这里
import *
并不是导入模块中的所有资源,而是导入模块中__all__
变量指定的资源# other.py num1 = 100 num2 = 200 num3 = 300 num4 = 400 __all__ = ['num1', 'num2', 'num4'] # test.py文件 from other import * # 这里 import * 仅仅只是导入了__all__变量指定的num1、num2和num4 print(num1) print(num2) print(num4)
- 批量导入包中的资源
from 包 import *
- 这里
import *
并不是导入包中所有的模块资源,而是导入包中__init__.py
文件中__all__
变量指定的模块
- 这里
import *
这种方式慎用- 因为通过这种方式导入,容易出现资源名称与模块资源名称重名问题
- 注意:
- 如果模块和包中没有没有写
__all__
变量指明资源,那么就会导入包里面所有模块和模块中所有公有资源
- 如果模块和包中没有没有写
- 批量导入模块中的资源
导入模块底层原理
导入模块后具体做了什么事?
-
第一次导入时
- 1、在所导入模块的命名空间中,执行所有代码
- 2、创建一个模块对象,并将模块内所有顶级变量以属性的形式绑定在模块对象上
- 3、将
import
后面的变量名称引入到import
语句所在位置的当前命名空间
-
示例讲解
other.py
文件num1 = 100 num2 = 200 def run(): num3 = 300 print(num1)
test.py
文件import other
-
第二次导入时
- 直接执行上述第三步
-
结论
import
和from
两种导入方式都会大致执行以上的步骤- 多次导入模块时,并不会多次执行该模块,只有第一次导入时才执行
- 两种导入不存在哪一种更省内存,区别在于把哪一部分内容拿到当前位置来用
import other # other-模块 ————begin # other-模块 ————end from other import num1 # other-模块 ————begin # other-模块 ————end
- 两种导入方式,均会执行整个
other.py
文件 - 区别在于:
import
会将other
对象的所有资源都拿到当前命名空间中来用from
仅仅是将other
对象的num1
资源拿到当前命名空间中来用
模块检索路径
- 模块检索路径顺序(从哪个位置找到需要导入的模块?)
- 第一次导入时先到内置模块中搜索,如果没有,再按照
sys.path
中记录的路径按顺序中去搜索 - 第二次导入时候,直接到已加载的模块中搜索
sys.modules
- 第一次导入时
- 第一级:内置模块
- 第二级:
sys.path
中的路径sys.path
路径构成1、当前目录 2、环境变量PYTHONPATH中指定的路径列表 3、Python安装路径 4、Python安装路径下的.pth文件中的文件路径列表 5、Python安装路径的lib库 6、Python安装路径的lib库中.pth文件中的文件路径列表
sys.path
追加路径的方式- 1、直接修改
sys.path
(仅作用于本次)import sys sys.path.append('指定路径') # 将指定路径添加到sys.path列表的最后 sys.path.insert(index, '指定路径') # 将指定路径添加到列表的指定索引位置 import 指定模块 # 模块的查找顺序最终根据sys.path列表中路径的位置而定
- 2、修改环境变量PYTHONPATH(仅在shell中有效,PyCharm需要另外一种设置方式)
# 添加环境变量 PYTHONPATH(仅能在cmd中通过python环境执行) # 此电脑 —— 属性 —— 高级系统设置 —— 环境变量 # 添加到 用户变量, 仅当前用户可用 # 添加到 系统变量, 所有用户均可用 # 配置PyCharm # PyCharm —— File —— Settings —— Project:xxxx —— Python Interpreter —— 右侧设置按钮 —— Show All... —— Show paths for the selected interpreter —— “+” 把路径添加进去
- 3、添加.pth文件
# 查看.pth文件存放目录 import site print(site.getsitepackages()) # ['E:\\StudyCode\\Python\\.venv', 'E:\\StudyCode\\Python\\.venv\\lib\\site-packages'] # 创建.pth文件:新建文本文档——将.txt后缀改成.pth # 将路径添加到.pth文件中 # 再将.pth文件添加到上面查询到的.pth文件存放目录
- 1、直接修改
- 第二次导入时
- 从已经加载过的模块中去查找
# 查看已加载模块 import sys print(sys.modules)
- 从已经加载过的模块中去查找
导入模块的常见场景
局部导入
- 在某个局部范围(命名空间)内导入模块,在其他范围无法使用
def
、class
、模块会产生单独的命名空间,if
、for
语句不会- 如果想要全局范围都能使用,则再文件顶部导入相关模块
- 使用场景
- 模块仅仅在某个函数内部或者某个类内部需要使用,其他地方统统用不到时
- 我们就可以使用局部导入,在函数或者类内部导入,只有执行函数或者使用类的时候才会加载模块
覆盖导入
- 重名的模块导入时,会根据检索路径的优先级发生覆盖情况
- 自定义模块和非内置的标准模块重名时,根据牵着存储位置,有可能牵着会覆盖后者
- 注意:自定义模块命名不要与非内置的标准模块重名
- 自定义模块和内置模块重名时,内置模块肯定会覆盖自定义模块
- 如果非要使用自定义模块,使用
from...import...
指明绝对路径进行导入
- 如果非要使用自定义模块,使用
循环导入
-
加入有两个模块A和B,模块A内导入了模块B,模块B内又导入了模块A,这样就造成了循环导入
-
场景
-
test.py
文件t1 = 'T1' t2 = 'T2' import other print(other.o1) print(other.o2)
-
other.py
文件o1 = 'O1' o2 = 'O2' import test print(test.t1) print(test.t2)
-
输出结果
O1 O2 T1 T2 O1 O2
-
图解
*
循环导入
执行test.py文件 1:增加属性t1、t2 2:导入模块other 2-1:先到sys.modules中查找,未找到 2-2:再根据sys.path路径进行查找 2-3:找到other模块,将other添加到sys.modules中 2-4:执行other模块,给other增加属性o1、o2 2-5:导入test模块 2-5-1:先到sys.modules中查找,未找到 2-5-2:再根据sys.path路径进行查找 2-5-3:找到test模块,将other添加到sys.modules中 2-5-4:执行test模块,给other增加属性t1、t2 2-5-5:导入other模块 2-5-5-1:先到sys.modules中查找,找到了 2-5-5-2:第二次导入other模块,不再执行文件 2-5-6:导入other模块完成,继续往下执行 2-5-7:输出o1、o2,打印O1、O2 2-6:导入test模块完成,继续往下执行 2-7:输出t1、t2,打印T1、T2 3:导入other模块完成,继续往下执行 4:输出o1、o2,打印O1、O2
可选导入
- 两个功能相近的包,根据需求优先选择其中一个导入
- 场景
- 有两个包 A和B,都可以实现相同的功能
- 想优先使用A,而且需要做到:在没有A的情况下,使用B做备选
- 实现
try: import other2 as o except ModuleNotFoundError: import other as o print(o.name)
包内导入
-
python相对导入与绝对导入,这两个概念是相对于包内导入而言额
-
包内导入即是包内的模块导入包内部的模块
-
绝对导入
- 导入时参照
sys.path
路径进行检索 - 例如:
import a from b import a
- 注意:以上结论是基于Python3.x版本之后的
- 导入时参照
-
相对导入
- 使用
.
来替代相对路径. # 根据模块名称获取的当前目录 .. # 根据模块名称获取的上级目录
- 例如
from . import a from .. import a
- 注意
.
和..
并不是简单的指代当前文件的当前目录和上级目录
- 使用
-
通过示例演示
-
文件结构
-
sub_module2.py
文件s2_m1 = 'sub2-1' s2_m2 = 'sub2-2'
-
sub_module1.py
文件import sub_module2 print(sub_module2.s2_m1) print(sub_module2.s2_m2)
-
执行
sub_module1.py
文件时,能正常导入sub_module2
模块,并执行# ========== 输出结果 ========== sub2-1 sub2-2
-
当我们在上一层目录的
module1.py
文件中导入sub_module1
模块时,执行居然会报错…import sub_package.sub_module1 # from sub_package import sub_module1 # 这两种导入方式是一样的效果,都是绝对导入 # ========== 输出结果 ========== Traceback (most recent call last): File "E:\StudyCode\Python\15-包和模块\包内导入\module1.py", line 1, in <module> import sub_package.sub_module1 File "E:\StudyCode\Python\15-包和模块\包内导入\sub_package\sub_module1.py", line 2, in <module> import sub_module2 ModuleNotFoundError: No module named 'sub_module2'
-
这里报错:没有名称是
sub_module2
的模块,为什么直接执行sub_module1
的时候能正常导入sub_module2
模块,现在执行module1.py
文件导入sub_module1
模块,再在sub_module1
模块内导入sub_module2
模块就会报错呢?
-
-
报错原因
-
1、
import a
和from b import a
两种导入语法都是绝对导入,模块检索路径由sys.path
觉得 -
2、当执行
module1.py
文件时,就确定了sys.path
里面的路径了,后面基本不会再改变> 执行 module1.py 文件时会将文件当前目录添加到 sys.path 路径中 > 导入 sub_module1 模块时,会执行内部的全部代码,内部需要再导入 sub_module2 模块 > 此时,需要到 sys.path 路径中去搜索 sub_module2 模块 > 但是,在执行 module1 文件时,就确定了 sys.path 路径,仅仅是将 包内导入 目录添加到了路径中 > 并没有将 sub_package 目录添加到路径中,所以通过 import sub_module2 导入模块会因为搜索不到模块而报错
-
修改文件代码,跟踪
sys.path
数据-
sub_module1.py
文件import sys print('sub_module1:', sys.path) # import sub_module2 # # print(sub_module2.s2_m1) # print(sub_module2.s2_m2)
-
module1.py
文件import sub_package.sub_module1 # from sub_package import sub_module1 import sys print('') print('module1:', sys.path)
-
-
通过跟踪
sys.path
的情况
-
-
解决办法:使用相对导入
- 只需要修改
sub_module1.py
文件代码from . import sub_module2 print(sub_module2.s2_m1) print(sub_module2.s2_m2)
- 只需要修改
-
关于
.
和..
的说明- 这里的
.
和..
并不是简单的获取当前文件的目录以及当前文件的上一级目录 - 而是根据模块名称来获取的,接下来我们来看一下模块名称的变化。我们先对文件代码进行调整
sub_module1.py
文件代码# from . import sub_module2 # print(sub_module2.s2_m1) # print(sub_module2.s2_m2) print('sub_module1:', __name__)
- 直接执行该文件,输出结果如下
# ========== 输出结果 ========== sub_module1: __main__
- 这里我们可以看到,直接执行,输出的文件名称是
__main__
- 修改
module1.py
文件代码import sub_package.sub_module1 # from sub_package import sub_module1 print('module1:', __name__)
- 执行
module1.py
文件,输出结果如下# ========== 输出结果 ========== sub_module1: sub_package.sub_module1 module1: __main__
- 额…大家发现了没有。当我们执行
module1.py
文件时,通过导入sub_module1
模块的方式执行模块内部的代码时 sub_module1
模块的名称变成了sub_package.sub_module1
- 这里的
-
关于模块名称的问题
- 当前模块是主程序,即直接由Python解释器运行当前模块时,当前模块的名称
__name__
则是main
- 当前模块通过
import
语句导入执行时,当前模块的名称__name__
则是由加载路径决定:包名.子包名.模块名
(这个名称最前面部分则被称为顶级名称)
- 当前模块是主程序,即直接由Python解释器运行当前模块时,当前模块的名称
-
当我们执行
module1.py
文件时,通过导入sub_module1
模块的方式执行模块内部的代码时sub_module1
模块的名称变成了sub_package.sub_module1
-
我们来解析一下
sub_module1
模块中的导入语句from . import sub_module2 # 此时 sub_module1 模块的名称变成了 sub_package.sub_module1 # 上面的 from 导入语句中的 . 则表示的是这个模块名称中上一个 . 前面的路径 sub_package # 相当于是 from sub_package import sub_module2 # module1.py 到 sub_package 目录中导入 sub_module2模块,这个当然没问题
-
问题点
-
如果直接修改成上述代码,那么执行
module1.py
文件不会报错了,但是直接执行sub_module1.py
文件会报错 -
sub_module1.py
文件代码from . import sub_module2 print(sub_module2.s2_m1) print(sub_module2.s2_m2)
-
直接执行执行
sub_module1.py
文件会报错Traceback (most recent call last): File "E:\StudyCode\Python\15-包和模块\包内导入\sub_package\sub_module1.py", line 2, in <module> from . import sub_module2 ImportError: attempted relative import with no known parent package # 导入错误:尝试相对导入,没有已知的顶级包
-
报错原因
- 直接执行
sub_module1.py
文件,就是直接由Python解释器运行当前模块时,此时当前模块的名称__name__
则是main
from . import xxx
中的.
是获取模块名称中.
前面的路径,但是此时模块名称是main
并没有.
,所以会报错
- 直接执行
-
如果即想直接执行模块,又想其他模块导入都不报错,那么可以将模块代码修改成如下:
# 根据模块名称的情况,分别使用不同的导入方式 if __name__ == '__main__': import sub_module2 else: from . import sub_module2 print(sub_module2.s2_m1) print(sub_module2.s2_m2)
关于Python2.x的说明
-
在Python2.x中,
import a
其实就是相对导入,但是这里面又没有加.
和..
的语法,这种称之为隐式的相对导入 -
但是为什么后面又把这种写法改变成了绝对导入呢?
- 因为,如果
import a
这种写法是相对导入,那么查找模块就是优先到当前路径下查找 - 那么,如果自定义模块与内置模块重名了,那就再也没有办法访问到内置模块了
- 所以,在Python3.x版本中,
import a
就升级成了绝对导入,查找模块优先在内置模块中查找 - 如果要查找与内置模块重名的自定义的模块,我们就可以使用显示的相对导入
from . import a
语法进行导入
- 因为,如果
-
如果要让Python2.x和Python3.x在相对和绝对导入兼容,可以使用一下语句
from __future__ import absolute_import # 禁用Python2.x版本中的隐式相对导入 # 使import a语法在Python2.x版本中也是绝对导入
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!