模块

基本概念

模块的定义

一般情况下,模块(module)是一个以 .py 为后缀的文件,其他可作为 module 的文件类型还有 .pyo.pyc.pyd.so.dll ,但初学者几乎用不到

在模块中能定义函数、类、变量,也能包含可执行的代码,在导入的时候会把模块完整地先执行一遍

模块的作用

隐藏代码细节,提高可维护性

模块的分类

  • Python 的官方标准库(直接 import 就能开用)
  • 第三方模块(用 pip 下载的包里面的模块等)
  • 自己写的模块(下面来试一试)

初尝模块

首先,在当前目录新建一个 calc.py ,再在里面保存一些函数

1
2
3
4
5
6
7
8
9
10
11
def add(a,b):
return a+b

def sub(a,b):
return a-b

def mul(a,b):
return a*b

def div(a,b):
return a/b

现在我们在其他文件中引用它,在同一目录新建一个 .py 文件

import …

导入整个模块

1
2
3
4
5
import calc

result = calc.add(10, 20)

print(result) # 30

使用这种方法,在调用其中的函数或类时,必须加上模块名的前缀

from … import …

导入特定的内容,使用时不用前缀

1
2
3
4
5
from calc import add, sub # 可以导入任意个

result = add(10, 20)

print(result) # 30

from … import *

这会把模块中的所有函数、类、变量等全部导进来,虽然方便,但是不建议使用(可能与已有的内容冲突)

1
2
3
4
5
from calc import *

result = mul(10, 20)

print(result) # 200

稍稍深入

取别名

在导入的时候可以给模块或者函数使用 as 取别名

1
import calc as c # 这样在下面都可以使用 c 来代替 calc 了
1
from calc import add as a # 这样在下面都可以使用 c 来代替 add 了

(下面的包也可以这样操作,可以把前面冗长的前缀如 包名.子包名.模块名 简化成一个词)

路径问题

现在,你已经学会如何导入模块了,但是,python 是从哪里找到这些模块的呢?

答案是去 sys.path 中查找,这个列表的第一个元素是当前路径,然后还有一些预定义的路径

使用下面的命令查看 sys.path

1
2
3
4
5
6
7
8
9
10
11
12
13
import sys

print("\n".join(sys.path))

# d:\Study\Python
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\python39.zip
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\DLLs
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\lib
# C:\Users\Nick\AppData\Local\Programs\Python\Python39
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\lib\site-packages
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\lib\site-packages\win32
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\lib\site-packages\win32\lib
# C:\Users\Nick\AppData\Local\Programs\Python\Python39\lib\site-packages\Pythonwin

若要手动添加路径到 sys.path 里面,可以使用 sys.path.append() 方法

而且这个一般会结合 os.path.dirname(__file__)os.path.abspath(__file__) 来使用,可以尝试下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os

path1 = os.path.abspath(__file__) # 获取当前执行脚本的完整路径。只有当在脚本中执行的时候,os.path.abspath(__file__)才会起作用。
print("path1 = ", path1)

path2 = os.path.dirname(__file__) # 获取当前运行脚本的绝对路径
print("path2 = ", path2)

path3 = os.path.dirname(os.path.abspath(__file__)) # 先获取当前执行脚本的完整路径,再去掉最后1个路径(相当于:获取当前运行脚本的绝对路径)
print("path3 = ", path3)

path4 = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 先获取当前执行脚本的完整路径,再去掉最后2个路径(相当于:获取当前运行脚本的绝对路径,再去掉1个路径)
print("path4 = ", path4)

path5 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 先获取当前执行脚本的完整路径,再去掉最后2个路径(相当于:获取当前运行脚本的绝对路径,再去掉1个路径)
print("path5 = ", path5)

# path1 = d:\Study\Python\test.py
# path2 = d:\Study\Python
# path3 = d:\Study\Python
# path4 = d:\Study
# path5 = d:\

联合使用这三者可以将要导入的模块的绝对路径添加到 sys.path 里面,这样能够保证可以找到你所要导入的模块

1
2
3
path5 = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))  # 先获取当前执行脚本的完整路径,再去掉最后2个路径(相当于:获取当前运行脚本的绝对路径,再去掉1个路径)

sys.path.append(path5) # 把你要导入的那个模块所在的绝对路径添加到sys.path中

_name_ 属性

在很多地方,你都会看见这样一句话,它与模块的导入有关

1
if __name__=='__main__':

__name__ 属性可以理解为一个全局变量,它的值在各种地方是不同的

  • 当该模块被其它模块调用时, __name__ 的值当前模块的名字
  • 当该模块被当做执行脚本时(也就是程序从这个模块开始运行),__name__ 的值为 __main__
1
2
3
4
5
6
# Filename: using_name.py

if __name__ == '__main__':
print('程序自身在运行')
else:
print('我来自另一模块')

运行输出如下:

1
2
$ python using_name.py
程序自身在运行
1
2
3
4
$ python
>>> import using_name
我来自另一模块
>>>

基本概念

为避免模块名冲突,Python 引入了按目录组织模块的方法,称之为包(package)

模块与包的关系就如文件与文件夹的关系,文件夹的层次是用斜杠或者反斜杠来表示的,而包是用 . 来分隔层次的

你可以导入整个包,或仅仅只是导入包中的模块

如何导入

如果你想导入包,可以使用 import 包名import 包名.子包名

如果想导入模块,可以使用 import 包名.模块名 (使用时必须有前缀) 或 from 包名 import 模块名 (推荐用法)

如何构建

_init_.py

在一个文件下同时有 __init__.py 文件和其他模块文件时,该文件夹即看作一个包

如果导入的对象是包,会执行那个包的 __init__ .py 文件

_all_ 属性

这东西在模块里其实也是有意义的,但一般都用在 __init__ .py

在模糊导入包或模块时( from pacakge_1 import * ),仅仅会导入 __all__ 中指定的包和模块(如果没有指定,那么什么都不会被导入)

1
2
__all__ = ["echo", "surround", "reverse"] # 指定模块
__all__ = ["file_a", "file_b"] # 指定子包

例子

菜鸟教程最底下的那个例子就挺好的