6.模块和包
模块modules
在之前的程序里,你可能用过类似from math import pi
的语句来导入变量或函数。这其实就是在使用模块。
模块:Python将程序存入一个文件,可在解释器中运行。这个文件就是模块。
模块中的语句可以导入(import)到其他Python程序中。
使用模块的好处:模块化,每个模块写相关的功能;避免文件过长和混乱。
创建模块
新建一个文件(文件名为模块名.py
),在文件里写程序。
举个例子:创建一个和三角形相关的模块:
新建文件tri_f.py
,然后在里面写入:
import math
print("tri_f 模块 开始")
def edge_length(a, b, c):
"""返回三边长度之和"""
return a + b + c
def area(a, b, c):
"""返回三角形面积"""
p = (a + b + c) / 2
s = math.sqrt(p * (p - a) * (p - b) * (p - c))
return s
print("tri_f 模块 结束")
然后保存(Ctrl+s)。这样模块就创建好了。
使用模块
使用模块通过import
语句导入模块中的变量定义、函数等可执行语句等。
例,使用模块tri.f
。
在tri_f.py
同级目录下创建test.py
。
注意:如果不是同级目录,解释器会找不到,需要在sys.path中添加查找路径,如
import sys sys.path.append(r'D:\PY_TEST\pythonProject\6modules')
#替换为自己的目录
a, b, c = 3, 4, 5
# 从模块导入特定函数
from tri_f import area # 从tri_f模块导入函数area的定义
print(area(a, b, c)) # 然后就可以使用函数area,计算三角形面积了
# 导入模块
import tri_f #导入 tri_f模块
print(tri_f.area(a, b, c)) # 函数前面需要加上模块名
# 给模块起别名
import tri_f as tr
print(tr.area(a, b, c))
# (不推荐)从模块导入全部函数(_开头的函数除外)
from tri_f import *
print(area(a, b, c))
print(edge_length(a, b, c))
运行test.py我们得到下面结果:
tri_f 模块 开始
tri_f 模块 结束
6.0
6.0
6.0
6.0
12.0
通过import模块 ,我们不仅导入了函数,还会执行模块中的语句。
总结一下,Import的用法:
# 从模块导入特定函数
from 模块 import 函数
# 导入模块
import 模块
# 给模块起别名
import 模块 as 别名
# (不推荐)从模块导入全部函数(_开头的函数除外)及变量
from 模块 import *
模块名__name__
你可能看到过 下面的语句:
if __name__ == '__main__':
print('hello')
这里的__name__
实际上就是模块的名字。模块被导入时,__name__
是模块的文件名。当这个模块作为主程序运行时,模块的__name__
会赋值为'__main__'
。
import math
print('name=',__name__)
def edge_length(a, b, c):
"""返回三边长度之和"""
return a + b + c
def area(a, b, c):
"""返回三角形面积"""
p = (a + b + c) / 2
s = math.sqrt(p * (p - a) * (p - b) * (p - c))
return s
if __name__ == '__main__':
print(area(3, 4, 5))
print('hello')
运行tri_f.py,输出:
name= __main__
6.0
hello
运行test.py时,tri_f中的__name__
就会变成模块名 tri_f
,所以不会执行tri.f模块中if里的内容:
if __name__ == '__main__':
print(area(3, 4, 5))
print('hello')
模块详情
import语句导入模块时发生的事情:
执行模块中语句(包括定义函数等)。
注意:
!模块中的语句用于初始化模块,且仅在 import 语句 第一次 遇到模块名时执行(防止重复)。
!需要注意,如果有多个相同的函数名,最后定义的函数会把之前的同名函数给覆盖掉。
!可以用与访问模块函数一样的标记法,访问模块的全局变量,modname.itemname
。
模块有自己的私有符号表,用作模块中所有函数的全局符号表。因此,模块内全局变量不会与用户定义的全局变量发生冲突。(别纠结,命名空间在第九章类会详细说)
包package
创建包
包是装着模块的文件夹,文件夹下必须含 __init__.py
文件。
最简情况下,__init__.py
只是一个空文件,但该文件也可以执行包的初始化代码,或设置 __all__
变量,详见下文(从包中导入*)。
sound/ #顶层包
__init__.py #初始化
formats/ #子包,用于格式化声音
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ #子包,用于声音特效
__init__.py
echo.py
surround.py
reverse.py
...
filters/ #子包,用于过滤
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
使用包
从包导入与从模块导入非常类似。
- 可以从包中导入单个模块,例如:
import sound.effects.echo
这段代码加载子模块 sound.effects.echo
,但引用时必须使用子模块的全名:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
- (推荐)另一种导入子模块的方法是:
from sound.effects import echo
这段代码还可以加载子模块 echo
,不加包前缀也可以使用:
echo.echofilter(input, output, delay=0.7, atten=4)
- Import 语句的另一种变体是直接导入所需的函数或变量:
from sound.effects.echo import echofilter
同样,这样也会加载子模块 echo
,但可以直接使用函数 echofilter()
:
echofilter(input, output, delay=0.7, atten=4)
注意,使用 from package import item
时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。
优先查找包中定义的函数、类或变量等,未找到则假定 item 是模块,并尝试加载模块。如果仍然找不到 item,则触发 ImportError 异常。
相反,使用 import item.subitem.subsubitem
句法时,除最后一项外,每个 item 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量。
从包中导入 *
类似模块导入*,使用 from sound.effects import *
时,该语句应该导入包的所有子模块。但是这可能会导入太多东西, 浪费时间且造成冲突。因此,使用 from sound.effects import *
只会导入在__init__.py
中__all__
变量里的模块。
__all__ = ["echo", "surround", "reverse"]
相对导入
包中含有多个子包时还可以用 import 语句的 from module import name
形式执行相对导入。这些导入语句使用前导句点表示相对导入中的当前包和父包。例如,相对于 effect
包下的surround
模块,可以使用:
from . import echo
from .. import formats
from ..filters import equalizer