|
|
|
|
公众号矩阵

写 Python 脚本时,一定要加上这个

为什么很多优秀的编程语言,比如 C、Java、Golang、C++ 都有一个 main 入口函数呢?我想很重要的一个原因就是就是程序入口统一,容易阅读。

作者: somenzz 来源: Python七号|2021-10-29 06:56

我发现有不少朋友写 Python 脚本非常随意,要么不用函数,要么函数随处定义,反正第一眼看不出要执行的第一行代码位于何处,这样的脚本可读性很差,而且容易隐藏 bug,解决这个问题很简单,当我们写 Python 脚本时,一定要加上这个:

  1. def main(): 
  2.     # do something 
  3.     print("do something."
  4.  
  5.  
  6. if __name__ == "__main__"
  7.     main() 

你可能要反对了:我怎么爽就怎么写,凭什么听你的,多写个 if __name__...?

别急,让我说三个原因。

第一,它让 Python 文件的作用更加明确

首先需要明白 __name__ 的作用,当脚本直接被 Python 解释器执行时,其值就是 "__main__",当其被其他 Python 程序 import 的时候,其值就是对应的 Python 脚本文件名,可以在 Python 解释器验证下,假定有个 some_script.py 其内容如下:

  1. print("some_script.py"
  2. print(__name__) 

在 Python 解释器导入一下:

  1. ❯ vim some_script.py 
  2. ❯ python 
  3. Python 3.8.5 (v3.8.5:580fbb018f, Jul 20 2020, 12:11:27) 
  4. [Clang 6.0 (clang-600.0.57)] on darwin 
  5. Type "help""copyright""credits" or "license" for more information. 
  6. >>> import some_script 
  7. some_script.py 
  8. some_script 
  9. >>> 

可以看到,__name__ 的值就是 Python 脚本的文件名 some_script。

也就是说 if __name__ == "__main__": 后面的代码在 import 的时候是不会运行的。

明白了这一点,if __name__ == "__main__": 就可以做为区分脚本和库的一个标志,当我们看到 if __name__ == "__main__": 时,就认为这一个可以直接运行的脚本,当没有看到这行代码时,就认为这是一个库,可以被其他程序引用,Explicit is better than implicit.,不是吗?

再举个例子:

假如你写了一个不带if __name__ == "__main__": 的脚本,叫 bad_script.py,内容如下:

  1. def useful_function(x): 
  2.     return x * x 
  3.  
  4.  
  5. class UsefulClass: 
  6.     def __init__(self, x): 
  7.         self.x = x 
  8.  
  9. #你自己测试了一吧,没毛病 
  10. for i in range(7): 
  11.     print(useful_function(i)) 

别人写了个 useful.py,引用了你的 useful_function:

  1. from bad_script import useful_function 
  2.  
  3.  
  4. def main(): 
  5.     print(f'{useful_function(3)=}'
  6.  
  7.  
  8. if __name__ == '__main__'
  9.     main() 

一运行,发现打印了不可预期的内容,见下图红色部分:

查了半天原因,发现是你的脚本输出的,你说别人会不会骂你?

假如你在自己脚本里定义了全局变量,别人如果在不合适的位置导入了 *,就会把你这个全局变量也导入,导致变量覆盖,很容易会出现 bug。

第二,它让 Python 文件更加易读,对 IDE 友好

有了 if __name__ == "__main__": 相当于 Python 程序也有了一个入口函数,所有的变量都从这里开始定义和使用,我们可以清晰的知道程序的逻辑开始于何处(当然还需要我们自觉的把程序的开始逻辑都放在这里)

其实,这也是 PyCharm 推荐的做法,当你新建一个项目的时候,它默认创建的 main.py 就是长这样的:

在if __name__ == "__main__": 的那一行的最左边也有一个绿色的运行按钮,点击一下,程序就从这一行开始运行了。

为什么很多优秀的编程语言,比如 C、Java、Golang、C++ 都有一个 main 入口函数呢?我想很重要的一个原因就是就是程序入口统一,容易阅读。

第三、多进程场景下,必须用 if main

比如说你用多进程搞并行计算,写了这样的代码:

  1. import multiprocessing as mp 
  2.  
  3.  
  4. def useful_function(x): 
  5.     return x * x 
  6.  
  7. print("processing in parallel"
  8. with mp.Pool() as p: 
  9.     results = p.map(useful_function, [1, 2, 3, 4]) 
  10.     print(results) 

当你运行的时候,会发现程序不停的在创建进程,同时也在不停的报错 RuntimeError,即使你 Ctrl C 也无法终止程序。而加上了 if __name__ == "__main__": 程序就会按照预期的进行:

  1. import multiprocessing as mp 
  2.  
  3.  
  4. def useful_function(x): 
  5.     return x * x 
  6.  
  7. if __name__ == '__main__'
  8.     print("processing in parallel"
  9.     with mp.Pool() as p: 
  10.         results = p.map(useful_function, [1, 2, 3, 4]) 
  11.         print(results) 

这是为什么呢?

其实我是这样理解的,Python 的多程序就是启动了多个 Python 解释器,每个 Python 解释器都会导入你这个脚本,复制一份全局变量和函数给子进程用,如果有了if __name__ == "__main__":,那它后面的代码就不会被 import,也就不会被重复执行。否则,这个创建多进程的代码就会被 import,就会被执行,从而无限递归的去创建子进程,Python3 会报 RuntimeError,顺序是先创建进程,然后报错的,因此就会出现不停的创建进程,不停的报错,Ctrl C 也无法终止的现象,只能 kill 掉整个终端。这里有个官方解释[1]

最后的话

if __name__ == "__main__": 虽然不是强制的,但是基于上述三点原因,我强烈推荐你这么做,它是 Python 社区的约定,对应Python 之禅:明确优于隐晦。正如 _ 作为变量名的意思就是告诉读代码的人:这个变量不重要,后面也不会用到它。当你看到 Python 脚本有 if __name__ == "__main__": 时,就会意识到,这是一个可执行的脚本,当被其他程序导入时,这部分代码不会被执行,而多进程的程序中,这是必须的。

本文转载自微信公众号「Python七号」,可以通过以下二维码关注。转载本文请联系Python七号公众号。

【编辑推荐】

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区
  2. 用 Python 开发交互式 Web 应用,So Easy
  3. 如何在Python中创建和使用虚拟环境
  4. Python 居然开始抄作业了,这次抄的是Rust
  5. 使用 Python 创建一个简单的基于规则的聊天机器人
  6. Python爬虫助力疫情数据追踪项目实训
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

带你轻松入门 RabbitMQ

带你轻松入门 RabbitMQ

轻松入门RabbitMQ
共4章 | loong576

51人订阅学习

数据湖与数据仓库的分析实践攻略

数据湖与数据仓库的分析实践攻略

助力现代化数据管理:数据湖与数据仓库的分析实践攻略
共3章 | 创世达人

14人订阅学习

云原生架构实践

云原生架构实践

新技术引领移动互联网进入急速赛道
共3章 | KaliArch

42人订阅学习

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微