0%

1
2
3
4
5
def func():
print('ok')

if __name__ == '__main__':
func()

这种形式的代码,相信你已经见过很多,在脚本的末尾,出现一个if条件判断,这个if条件判断的作用是什么呢,__name__事先并没有定义,为什么可以直接使用呢,它从哪里来? 回答这些问题,必须先了解模块属性

1.模块属性

一个python脚本(以.py 结尾的文件)就是一个模块,模块自身有若干属性,其中比较常用的是如下两个

  1. __name__ 模块的名称
  2. __file__ 脚本的完整文件路径

在任意一个python脚本里,你都可以输出这两个属性(注意Jupyter notebook等交互式IDE可能会报__file__错,直接在终端或vscode运行就好了)

1
2
print(__name__)
print(__file__)

得到结果

1
2
/Users/wangyaowei/Desktop/test.py
__main__

__name__的值是__main__ ,这表示模块的名称是__main__
__file__是文件的完整路径

虽然弄清楚了__name__是怎么一回事,但也带来了新的疑问,明明__name__就等于__main__,为何还要做if条件判断呢?显然是存在__name__不等于__main__的情况。

2.直接执行与其他模块引入

简单的功能,我们可以在一个python脚本里完成,但复杂的功能,我们会写多个python 脚本,比如下面的例子里,有两个脚本,一个是main.py ,做为整个程序的启动脚本,utils.py 提供一些辅助函数,供main.py使用

(1) utils.py

1
2
3
4
5
6
def safe_division(a, b):
if b == 0:
return None
return a/b

print("utils 模块里的__name__ 值为:", __name__)

(2) main.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from utils import safe_division

def func():
value = input("输入两个整数,中间用空格分开:")
arrs = value.split()
a = int(arrs[0])
b = int(arrs[1])

result = safe_division(a, b)
print(result)

if __name__ == '__main__':
func()

接下来,通过两步实验,来理解__name__在不同场景下的取值情况。

第一步,先来执行utils.py文件

1
2
python utils.py
>>> utils 模块里的__name__ 值为: __main__

第二步,执行main.py

1
2
3
4
python main.py
>>>utils 模块里的__name__ 值为: utils
>>>输入两个整数,中间用空格分开:5 2
>>>2.5

这里有一个现象,你必须理解其背后的原因,我们明明执行的main.py脚本,但是utils.py脚本里的代码也被执行了,这是因为在main.py脚本里引入了utils.py 这个模块,被引入的脚本里的代码会在引入时执行。

当utils.py 被其他脚本引入时,它的__name__就不等于__main__, 而是等于utils,恰好是文件名称去掉.py 剩余的部分。

经过上面的实验,我们可以得出两个结论

  1. 当脚本被直接执行时,模块属性__name__等于__main__
  2. 当脚本被其他模块引入时,模块属性__name__等于脚本名称去掉.py 后剩余的部分

3.终极目的—测试模块里函数

由于一个脚本被引入时,自身的代码会被执行,因此我们在每个脚本里都写上一段if __name__ == '__main__'如果你希望一些代码只有在脚本被直接执行时才执行,那么就把这些代码放入到if 语句块中,最常见的情形就是测试代码,下面我对utils.py 进行修改

1
2
3
4
5
6
7
8
9
def safe_division(a, b):
if b == 0:
return None

return a/b

if __name__ == '__main__':
print(safe_division(10, 5) == 2)
print(safe_division(10, 0) == None)

我们写完一个函数后,不免要写一些测试的代码,而这些测试的代码我们不希望他们在utils.py被引入时执行,只有当我们主动执行utils.py 进行测试才执行这些测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import matplotlib.pyplot as plt 
import numpy as np
import pandas as pd
from pylab import *

plt.figure(figsize=(8.3,6))
label_font = {'family':'Arial','weight':'normal','size':22}
legend_font = {'family':'Arial','weight':'normal','size':15}
plt.rcParams['xtick.direction'] = 'in'#将x周的刻度线方向设置向内
plt.rcParams['ytick.direction'] = 'in'#将y轴的刻度方向设置向内
plt.yticks(fontproperties = 'Arial', size = 24)
plt.xticks(fontproperties = 'Arial', size = 20)
plt.ylabel('$\mathregular{R^{2}}$'+' coefficient of determination',label_font)
plt.xlabel('Number of features',label_font)
tick_params(which='major',width=2,length=6)
ax=plt.gca()
bwith = 2
ax.spines['bottom'].set_linewidth(bwith)
ax.spines['top'].set_linewidth(bwith)
ax.spines['left'].set_linewidth(bwith)
ax.spines['right'].set_linewidth(bwith)

plt.ylim([0,1])
my_x_ticks = np.arange(1, 6, 1)#原始数据有13个点,故此处为设置从0开始,间隔为1
plt.xticks(my_x_ticks)
plt.title('Random forest regression',fontsize=20)

d = [1,2,3,4,5]
r2_rf = [0.822,0.888,0.964,0.964,0.964]
r2tr_rf = [0.8264,0.892,0.9629,0.964,0.9604]
r2te_rf = [0.5456,0.5919,0.7318,0.7362,0.7203]


plt.plot(d,r2_rf,'o',ls='solid',lw=1,markeredgecolor='black',markersize=8,label='Entire dataset(50 cases) without cross-validation')
plt.plot(d,r2tr_rf,'s',ls='solid',lw=1,markeredgecolor='black',markersize=8,label='Trainning dataset(45 cases)')
plt.plot(d,r2te_rf,'^',ls='solid',lw=1,markeredgecolor='black',markersize=8,label='Test dataset(5 cases)')

plt.legend(loc='best',prop =legend_font,fancybox=False,edgecolor='black')
plt.tight_layout()
plt.savefig('rfr.pdf',dpi = 300)
plt.show()

效果图: