Python中的模块
1. 概述
模块是一个包含Python定义和语句的文件。文件名就是模块名后跟文件后缀 .py
在一个模块内部,模块名(作为一个字符串)可以通过全局变量 name 的值获得。
在一个模块中导入另一个模块使用关键字import,后跟模块的名字。
fibo.py
# Fibonacci numbers module
if __name__ == 'fibo':
print('module name == ', __name__)
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
# return Fibonacci series up to n
def fib2(n):
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
main.py
import fibo
fibo.fib(100)
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89
Process finished with exit code 0
模块可以包含可执行的语句以及函数定义。这些语句用于初始化模块。它们仅在模块 第一次 在 import 语句中被导入时才执行。
每个模块都有它自己的私有符号表。,该表用作模块中定义的所有函数的全局符号表。仅限于在该模块中可见。不必担心与导入它的其他模块的全局变量发生意外冲突。
模块可以导入其它模块。习惯上把所有 import 语句放在模块(或脚本)的开头(但是不要求必须这样做。)
2. 导入模块
2.1 导入整个模块的符号名称
语法1:
import <模块名>
被导入的模块名存放在调入模块的全局符号表中。导入的符号包括以下划线 _ 开始的名称。
main.py
import fibo
fibo.fib(100)
print('fibo module name = ', fibo.__name__)
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89
fibo module name = fibo
Process finished with exit code 0
语法2:
from <模块名> import *
导入模块内定义的所有名称,但是不会导入以下划线 _ 开头的符号名称。
main.py
from fibo import *
fib(100)
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89
Process finished with exit code 0
2.2 导入一个模块中的部分符号名称
导入部分符号,各符号之间使用逗号分隔
*这种导入方式不会把被调模块名(如fibo)引入到局部变量表中(即main模块中)。
from fibo import fib, fib2
fib(500)
lst = fib2(100)
for i in lst:
print(i, end=' ')
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
0 1 1 2 3 5 8 13 21 34 55 89
Process finished with exit code 0
2.3 使用as关键字给模块命名别名
如果模块名称之后带有 as,则跟在 as 之后的名称将直接绑定到所导入的模块。as之后的名称作为被导入模块的别名。
import fibo as fib
fib.fib(500)
lst = fib.fib2(100)
for i in lst:
print(i, end=' ')
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
0 1 1 2 3 5 8 13 21 34 55 89
Process finished with exit code 0
2.4 使用as关键字给模块中的符号命名别名
在被导入符号的名字后跟as关键字,并且as后跟别名
from fibo import fib as fibonacci
fibonacci(500)
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
Process finished with exit code 0
注解 出于效率的考虑,每个模块在每个解释器会话中只被导入一次。因此,如果你更改了你的模块,则必须重新启动解释器, 或者,如果它只是一个要交互式地测试的模块,请使用 importlib.reload(),例如 import importlib; importlib.reload(modulename)。
3. 以脚本的方式执行模块
查看脚本所在的路径:
在命令行同过python执行模块,模块里的代码会被执行,就像导入了模块一样,但是模块的__name__被赋值为__main__。
在模块的代码末尾加 if _ name _ == “_ main _”:,只有模块同过命令行调用执行时,这块代码才会执行,但是如果同过在另一个模块中导入,这个代码块是不会被执行的。
修改fibo.py:
# Fibonacci numbers module
if __name__ == 'fibo':
print('module name == ', __name__)
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
# return Fibonacci series up to n
def fib2(n):
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
if __name__ == '__main__':
print('in module fibo')
fib(100)
在命令行,执行脚本(不传参)
修改fibo.py,处理命令行参数:
if __name__ == '__main__':
import sys
print('in module fibo')
fib(int(sys.argv[1]))
在命令行,执行脚本(传参)
4. 模块的搜索路径
当一个名为 spam 的模块被导入的时候,解释器首先寻找具有该名称的内置模块。如果没有找到,然后解释器从 sys.path 变量给出的目录列表里寻找名为 spam.py 的文件。
sys.path 的初始目录有:
1. 模块文件所在的目录
2. PYTHONPATH环境变量中的目录列表
3. Python的安装目录
在模块初始化后,Python程序可以更改 sys.path。包含正在运行脚本的文件目录被放在搜索路径的开头处, 在标准库路径之前。这意味着将加载此目录里的脚本,而不是标准库中的同名模块。
sys.path 变量是一个字符串列表,用于确定解释器的模块搜索路径,该变量被初始化为从环境变量 PYTHONPATH 获取的默认路径,或者如果 PYTHONPATH 未设置,则从内置默认路径初始化。
也可以使用标准列表操作对其进行修改:
5. “编译过的”Python文件
为了加速模块载入,Python在 pycache 目录里缓存了每个模块的编译后版本,名称为 module.version.pyc,例如fibo.py模块:
其中名称中的版本字段对编译文件的格式进行编码; 它一般使用Python版本号。上面的38对应的是Python3.8。此命名约定允许来自不同发行版和不同版本的Python的已编译模块共存。
Python根据编译版本检查源的修改日期,以查看它是否已过期并需要重新编译。这是一个完全自动化的过程。此外,编译的模块与平台无关,因此可以在具有不同体系结构的系统之间共享相同的库。
Python在两种情况下不会检查缓存:
1. 对于从命令行直接载入的模块,它从来都是重新编译并且不存储编译结果
2. 如果没有源模块,它不会检查缓存。
6. 标准模块
Python附带了一个标准模块库,一些模块内置于解释器中。例如 sys模块,它被内嵌到每一个Python解释器中,变量 sys.ps1 和 sys.ps2 定义用作主要和辅助提示的字符串,这两个变量只有在编译器是交互模式下才被定义。
更改辅助提示字符串:
7. 内置函数dir()
内置函数 dir() 用于查找模块中定义的符号名称。 它返回一个排序过的(升序)字符串列表
注意:它列出所有类型的名称:变量,模块,函数,等等。dir() 不会列出内置函数和变量的名称。如果你想要这些,它们的定义是在标准模块 builtins 中:
import fibo
print(type(dir(fibo)), ' : ' ,dir(fibo))
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
<class 'list'> : ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']
Process finished with exit code 0
如果dir()不带参数,则默认查找当前模块的符号名称:
import fibo
testStr = 'this is main module'
print(type(dir(fibo)), ' : ', dir(fibo))
print(type(dir()), ' : ', dir())
输出:
E:\Python3\Exercise\venvs\Scripts\python.exe C:/Users/Administrator/PycharmProjects/pythonProject/main.py
module name == fibo
<class 'list'> : ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']
<class 'list'> : ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fibo', 'testStr']
Process finished with exit code 0
8. 包
包是一种通过用 “带点号的模块名” 来构造 Python 模块命名空间的方法。
例如,模块名 A.B 表示 A 包中名为 B 的子模块。如下使用PyCharm创建一个名为A的包,它会在这个A包下自动创建一个名为 _ init _.py 的包初始化文件:
包其实就是一个目录:
必须要有 _ init _.py 文件才能让 Python 将包含 _ init _.py文件的目录当作包。
_ init _.py 刚被创建时它是一个空文件,但它也可以执行包的初始化代码或设置 _ all _ 变量
当导入这个包时,Python搜索 sys.path 里的目录,查找包的子目录。
创建A.B.C包,其中B是A的子包,C是B的子包,如下图的结构关系:
8.1 导入包中的模块
8.1.1 导入子包中的单个模块
例如导入A.B.C包中的CHello模块
CHello模块的代码:
C子包中_ init _py的 内容:
main.py中的代码
输出结果:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/main.py
import module of C packet
Hello, World! I'm in C subpacket
Process finished with exit code 0
在引用导入的子模块中的函数或者说是变量时,必须使用它的全名进行引用,如上面的A.B.C.CHello.Hello()
8.1.2 使用关键字from导入包中的模块
from A.B.C import CHello
CHello.Hello()
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/main.py
import module of C packet
Hello, World! I'm in C subpacket
Process finished with exit code 0
使用关键字from导入子包中的模块,子包路径必须是完整的包路径,不能是诸如如下的形式:
from A.B import C.CHello
C.CHello.Hello()
8.2 导入包中的所有模块(from package import *)
设置包的显示索引列表,即在其他模块中导入包中的所有模块时,为了避免导入一个子包中的所有模块,可以设置允许导出模块,通过子包中的__init__.py模块中的_ all _属性列表来设置。
C包的__init__.py模块
__all__ = ["CHello", "CEcho"]
print('import module of C packet')
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/main.py
import module of C packet
Hello, World! I'm in C subpacket
echo -----------! I'm in C subpacket
Process finished with exit code 0
没有在包的_ all _ 模块列表中的模块是导不出来的。
8.3 子包的相对导入
当包被构造成子包时,你可以使用绝对导入来引用兄弟包的子模块。还可以使用import语句的 from module import name 形式编写相对导入。这些导入使用前导点(.或…)来指示相对导入中涉及的当前包和父包。
from . import CEcho # 相对当前模块所在包的导入
from .. import BHello # 相对当前模块所在包的父包中的模块导入
from A import AHello # 绝对包模块路径导入
def Hello():
CEcho.Echo()
BHello.Hello()
print("Hello, World! I'm in C subpacket")
输出:
D:\Work\PRJS\pythonProject\pythonProject4\venv\Scripts\python.exe D:/Work/PRJS/pythonProject/pythonProject4/venv/main.py
import module of C packet
echo -----------! I'm in C subpacket
Hello, World! I'm in B subpacket
Hello, World! I'm in C subpacket
Process finished with exit code 0