1 | def func(): |
这种形式的代码,相信你已经见过很多,在脚本的末尾,出现一个if条件判断,这个if条件判断的作用是什么呢,__name__
事先并没有定义,为什么可以直接使用呢,它从哪里来? 回答这些问题,必须先了解模块属性
1.模块属性
一个python脚本(以.py 结尾的文件)就是一个模块,模块自身有若干属性,其中比较常用的是如下两个
__name__
模块的名称__file__
脚本的完整文件路径
在任意一个python脚本里,你都可以输出这两个属性(注意Jupyter notebook等交互式IDE可能会报__file__错,直接在终端或vscode运行就好了)
1 | print(__name__) |
得到结果
1 | /Users/wangyaowei/Desktop/test.py |
__name__
的值是__main__
,这表示模块的名称是__main__
__file__
是文件的完整路径
虽然弄清楚了__name__
是怎么一回事,但也带来了新的疑问,明明__name__
就等于__main__
,为何还要做if条件判断呢?显然是存在__name__
不等于__main__
的情况。
2.直接执行与其他模块引入
简单的功能,我们可以在一个python脚本里完成,但复杂的功能,我们会写多个python 脚本,比如下面的例子里,有两个脚本,一个是main.py ,做为整个程序的启动脚本,utils.py 提供一些辅助函数,供main.py使用
(1) utils.py
1 | def safe_division(a, b): |
(2) main.py
1 | from utils import safe_division |
接下来,通过两步实验,来理解__name__
在不同场景下的取值情况。
第一步,先来执行utils.py文件
1 | python utils.py |
第二步,执行main.py
1 | python main.py |
这里有一个现象,你必须理解其背后的原因,我们明明执行的main.py脚本,但是utils.py脚本里的代码也被执行了,这是因为在main.py脚本里引入了utils.py 这个模块,被引入的脚本里的代码会在引入时执行。
当utils.py 被其他脚本引入时,它的__name__
就不等于__main__
, 而是等于utils,恰好是文件名称去掉.py 剩余的部分。
经过上面的实验,我们可以得出两个结论
- 当脚本被直接执行时,模块属性
__name__
等于__main__
- 当脚本被其他模块引入时,模块属性
__name__
等于脚本名称去掉.py 后剩余的部分
3.终极目的—测试模块里函数
由于一个脚本被引入时,自身的代码会被执行,因此我们在每个脚本里都写上一段if __name__ == '__main__'
如果你希望一些代码只有在脚本被直接执行时才执行,那么就把这些代码放入到if 语句块中,最常见的情形就是测试代码,下面我对utils.py 进行修改
1 | def safe_division(a, b): |
我们写完一个函数后,不免要写一些测试的代码,而这些测试的代码我们不希望他们在utils.py被引入时执行,只有当我们主动执行utils.py 进行测试才执行这些测试代码