至此,我们的函数已经学个差不多,接下来我们就进入模块的学习。
我们学习函数的目的是为了简化代码,提高代码可读性,增强代码扩展性,然而通过这几天的写程序发现,在一个文件中定义了二十多个函数依旧使得我们的代码显得很乱,可读性差。我们希望能找到一种方法,使我们的代码看起来整洁,又能保证程序所有功能的实现。模块这个东西就完美的帮我们实现了。我们可以将函数封装成一个个模块,再导入到其他文件内使用,使代码变得简洁。
一、什么是模块
模块就是一个py文件,这个py文件中放置了许多功能(函数)
二、为什么要用模块
使用模块将不同的功能板块分成不同文件。减少重复代码。模块还让我们可以直接使用别人写好的第三方模块。
三、模块的分类
1. 自定义模块:自己写的模块
2. 内置模块:通过c语言写的模块
3. 第三方模块:从网上下载的别人写的模块
四、如何使用模块
在执行文件中通过import关键字将我们要用的模块导入进去就可以使用了
在导入模块时,只需要import + 模块名,而不需要import + 模块名.py
# 举例:
import time
那么我们来说一下在执行代码时,遇到import都干了哪些事:
1. 遇到import会先将import后面的模块名所对应的文件执行一遍。既然执行文件,必然涉及到名称空间的建立。python会为模块单独建立一个名称空间,存放的就是模块内所有的名字与值的内存地址的联系。
2. 将模块的名称空间与执行文件的名称空间建立联系,使我们可以在执行文件中可以访问到模块中的名称。
说白了就是在当前执行文件中建立一个名称,该名称为模块名,指向该模块对应的名称空间。我们通过调用 模块名.名称 就可以访问到模块内的名称
# 重点:多次import同一个模块,只会执行一次。不会多次执行
# 举例:
import time
import time
import time
# 哪怕导入三次,也只会加载一次,不会加载三次。
# 重点2:模块的名称空间和当前执行文件的名称空间是相互独立的。两个名称空间互不干扰。只能在当前执行文件通过模块名访问到模块的名称空间
import cuboid
print(cuboid.long) # 通过模块名.名称 可以访问到模块的名称空间内的名称
我们还可以在导入模块时给模块取一个别名(新名字)import 模块名 as a 这个语句的用处是:当模块名过长时,我们可以将它设定一个新名字a,我们通过a就可以访问到模块名对应的名称空间。(这里的a可以是任意名字。)同时我们也只能通过a来访问到模块的名称空间,而不能再通过模块名访问到名称空间。
# 举例:
import time as t
t.sleep(3) # 就相当于time.sleep(3)
我们还可以在一行导入多个模块:import 模块1, 模块2.... 模块名与模块名之间用逗号分隔
# 举例:
import sys, time, os
# 同一行导入多个模块
我们还可以使用from 模块名 import 名称来实现部分导入,这种导入模式是直接将模块中我们导入的名称直接复制一份到当前执行文件的名称空间。from语句也可以给名称取别名,和import是一样的。from ... import age as a 这样,a就代表了模块...中的名称age
# 举例:
from cuboid import long as l
# 我们就可以在当前执行文件直接调用l
我们还可以通过from语句导入一个模块的所有名称。from ... import * 这样就实现了导入一个模块的所有名称
我们可以在写模块的时候定义一个叫做__all__ 的内置属性,它的作用是控制from ... import * ,*所能得到的名称。默认为全部,我们也可以指定。
# 举例:
__all__ = ['perimeter', 'area']
long = 19
wide = 17
high = 5
def perimeter():
res = (long + wide + high) * 4
return res
def area():
res = long * wide * high
return res
if __name__ == '__main__':
perimeter()
area()
# 我们在这了指定 * 只能得到perimeter和area,而不能访问到long, wide, high
五、python文件的两种执行方式
上面我们提到,在import模块的时候会将模块执行一遍,那么我们就明白了,一个python文件有两种执行方式,一种是直接执行文件,一种是当做模块导入时执行文件。
那么我们来想一下有没有这样一种情况:我们写了一个模块,里面有一些代码是我们在该模块被导入时不执行而当做执行文件时执行的。那么这些代码应该怎么办呢? 每次执行的时候打开注释?被导入的时候加上注释吗?并不是这样的,python给我们提供了一种简单的方法让我们去实现上述情况。
long = 19
wide = 17
high = 5
def perimeter():
res = (long + wide + high) * 4
return res
def area():
res = long * wide * high
return res
if __name__ == '__main__': # 文件作为执行文件时__name__等于__main__
perimeter() # 文件当做模块被导入时__name__等于模块名
area()
通过上述代码,我们就可以让该文件当做执行文件时运行代码,而当做模块导入时不运行这段代码。
六、模块搜索路径
当我们导入一个模块时,如果该模块不存在,会抛出异常,那么python是怎么确定模块是否存在的呢?
python有一个模块搜索的顺序,当python将能搜索的地方全部搜索一遍还没有找到模块时,就会抛出这个异常
那么python都会搜索哪些地方呢?
搜索顺序:内存中已加载的模块 ——> 内置模块 ——> sys.path下的路径。
我们可以通过sys.modules来查看内存中已加载的模块有哪些。
import sys
print(sys.modules)
# 这样就可以看到内存中已加载的模块有哪些
我们还可以通过sys.path查看python都会去哪些路径下查找
import sys
print(sys.path)
# 会打印出所有查找路径
打印sys.path我们会发现它是一个列表,那么如果我们的模块不在python查找的路径范围内,我们就可以手动添加模块所在的路径到sys.path中。这样就可以找到模块了。
# 注意:在pycharm中,自动帮我们把项目路径也添加到sys.path中了,这是pycharm帮我们干的,所以我们在写代码的时候一定要记得,直接忽略掉这个路径,因为我们的代码写出来不知道别人是否会在pycharm运行,如果不在pycharm环境中运行,就可能会出错。