我的Python学习日记
Date | Log |
---|---|
2023.12.03 | 完成编写 |
7. Module 模块
7.1 import
在2.6 import中我们简单介绍了使用import导入Python自带标准库的方式
- 其实我们自己也可以自己制作模块,把自己定义的函数、变量等存放在某个.py文件中,这个模块也可以被别的程序引入来使用其中的函数等功能。
- 一个模块只会被导入一次,不管执行多少次
import
当解释器遇到import
语句,如果模块在当前的搜索路径就会被导入。
- 搜索路径是一个解释器会先进行搜索的所有目录的列表,python解释器就会依次从这些目录中去寻找所引入的模块。
- 搜索路径是在python编译或安装的时候确定的,安装新的库应该也会修改。
- 搜索路径通常是包含代码文件所在的目录的,如果在当前目录下存在一个与要引入的模块同名的文件,Python就会优先加载该文件,而不是引入你想要的模块,这被称为模块屏蔽,可能会导致引入错误的模块。如果想要避免这种问题,可以重命名引入的模块,为其取一个别名,或者使用绝对路径或相对路径明确指定模块的路径:
from . import somemodule
from somemodule import func1
- 从模块somemodule中导入函数func1,这样可以直接将该函数导入到本文件中,而忽略该模块中其余部分,并且该模块本身也不会进入字符表中
from math import isnan
print(isnan(1)) # False
print(isnan(math.inf)) # NameError name 'math' is not defined
- 如果直接将整个模块导入,这样并没有把模块中的函数名称写入到当前的符号表中,只是把模块名写入,所以要用模块名称来访问函数
import math
print(math.isnan(1)) # False
print(math.isnan(math.inf)) # False
- 如果觉得这样很不方便,可以将这些函数本地化:
import math
isnan = math.isnan
inf = math.inf
print(isnan(1)) # False
print(isnan(inf)) # False
- 每个模块都有各自独立的符号表,所以模块内部的全局变量不会与其他模块或程序的变量搞混。
模块中也可以导入其他模块,将这些模块放入该模块的字符表中 - 也可以使用
from somemodule import *
将somemodule中全部变量和函数(除了_
开头的名字),所以该方法一般不使用
7.2 模块内置属性
7.2.1 ' __name__ '
- 这个属性表示模块的名称
-
import math print(math.__name__) # math
-
- 一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用
__name__
属性来使该程序块仅在该模块自身运行时执行:
当一个模块被直接运行时,__name__
的值是"__main__"
,而当模块被导入时,__name__
的值是模块的实际名称,所以我们可以利用以下语句来标定:-
if __name__ == '__main__': print('程序自身在运行') else: print('我来自另一模块')
-
7.2.2 '__file__'
- 这个属性表示模块的文件路径
-
import mymodule as mm print(mm.__file__) # ...\MyBlog\mymodule.py
-
- 对于内置模块和在交互式环境(命令行等)中执行的代码,
__file__
可能为None
7.2.3 '__doc__'
- 文档字符串,通常用于提供对模块的文档、目的、使用方法等信息的描述,有助于提高代码可读性和可维护性
- 文档字符串位于模块文件顶部,即任何实际代码之前,使用三重(单/双)引号括起
import mymodule as mm
print(mm.__doc__) # 这个模块会输出本模块的名字
- 如果该字符串前有实际代码,该模块的
__doc__
为None
7.2.4 '__package__'
- 这个属性表示模块所属的包
- 如果不属于任何包,该属性值为None
7.2.5 '__loader__'
- 这个属性表示用于加载模块的加载器对象,在动态导入时可能会用到。
7.2.6 '__spec__'
- 这个属性表示模块的规范。模块规范是一个对象,包含有关模块如何加载、实例化和执行的信息。
- 一般不需要直接操作
7.3 dir()函数
内置的函数dir()
可以找到模块内定义的所有名称,以一个字符串列表的形式返回:
import mymodule as mm
print(dir(mm))
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'printmodname']
如果没有给定参数,那么dir()
函数会罗列出当前定义的所有名称
import mymodule as mm
print(dir())
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'mm']
7.4 Package 包
包是一种管理模块命名空间的形式,采用“A.B”的命名规则,表示包A中的子模块B
- 不同包之间的模块重名不会互相影响
- 包中也可以包含子包
- 目录中只有包含一个叫做“__init__.py”的文件才会被认作是一个包
- 最简单情况只需要放一个空的__init__.py文件就可以了,也可以包含一些初始化代码或者为
__all__
变量赋值 - 子包同样需要__init__.py文件
- 最简单情况只需要放一个空的__init__.py文件就可以了,也可以包含一些初始化代码或者为
在导入包的子模块时,可以直接导入某个特定模块:
import somepackage.somemodule
这种导入方式的问题是每次都需要使用全名访问,如果有子包的话需要输入的更多:
import somepackage.somemodule
somepackage.somemodule.func1()
所以我们可以使用from ... import ...
的方式导入:
from mypack import mymodule as mm
这样每次访问就方便多了,我们也可以使用这种方法直接导入这个模块的某个或某些函数或变量
这样调用的模块的__package__
属性为其所在的子包:
from mypack.subpack import mymodule as mm
print(mm.__package__) # mypack.subpack
__all__
- 如果我们使用
from mypack import *
来导入包中全部模块,python会进入系统一个一个把它们导入进来,但是Windows系统是不区分大小写的系统,所以这样可能导致导入子模块产生模糊,__all__
就是用来避免这种情况的 __all__
写在__init__.py
文件中,是一个列表变量,包含了应该被导入的名字的列表-
__all__ = ["mymodule"]
- 这表示在
from mypack.subpack import *
时会导入mymodule模块
-
- 如果没有定义
__all__
,但仍要使用from mypack.subpack import *
时,就会把这个包里定义的所有内容导入进来,包括__init__.py
文件,所以我们需要尽量避免使用这种方式导入模块
__path__
包还提供一个额外的属性__path__
,如同模块中的__file__
,用来标定包的目录