见鬼!导入Python模块执行了全部代码,你不知道__name__变量是什么意思吗?

可能不少同窗在阅读Python源代码时会发现常常会出现if __name__ == '__main__':这样的代码,那么这样的代码起到什么做用呢?本文将为你深刻解析__name__变量的含义和应用场景。
当Python解析器读源代码文件时,会作以下两件事情:
  • 设置特殊变量,如__name__;css

  • 执行源代码文件中的全部代码;python


如今咱们将焦点放到__name__变量上来,看看在Python程序中为何要使用__name__变量。
让咱们先使用一段代码示例来探索导入和脚本的工做方式。假设这些代码位于名为foo.py的文件中。
print("before import")import math
print("before functionA")def functionA(): print("Function A")
print("before functionB")def functionB(): print("Function B {}".format(math.sqrt(100)))
print("before __name__ guard")if __name__ == '__main__': functionA() functionB()print("after __name__ guard")
当Python解释器读取源文件时,它首先定义一些特殊变量。在这个案例中,咱们关心的是__name__变量。若是将Python脚本文件做为主程序运行,也就是经过下面的命令运行foo.py。
python foo.py
Python解释器会使用下面的代码为__name__变量赋值,也就是说,__name__变量的值是"__main__"。
__name__ = "__main__"
另外一方面,假设其余模块是主程序,而且它将导入foo.py。这意味着在主程序中会有以下的语句:
import foo
Python解释器将搜索foo.py文件(以及搜索其余一些变体),在执行该模块以前,它将从import语句中将名称“ foo”分配给__name__变量,也便是使用下面的代码为__name__变量赋值。
__name__ = "foo"
设置__name__变量后,Python解释器经过一次执行一条语句的方式执行模块中的全部代码。您可能想要在代码示例侧面打开另外一个窗口,以便您能够按照如下说明进行操做。
如今执行前面的代码,会输出以下内容:
before importbefore functionAbefore functionBbefore __name__ guardFunction AFunction B 10.0after __name__ guard
若是当前的Python脚本按着主程序的方式执行(使用python命令),那么__name__变量的值就是"__main__"。这样就会调用functionA和functionB函数,从而输出 "Function A" 和 "Function B 10.0"
若是foo.py脚本不是做为主程序运行,而是被另外一个程序导入,则__name__变量的值是“ foo”,而不是“ __main__”,在这种状况下,将不会调用functionA和functionB函数不过在这种状况下,仍然会输出 "after __name__ guard" ,由于这条语句不属于if语句。
如今总结一下:
根据foo.py的运行方式,可能会有两种输出结果。
(1)做为主程序运行,会输出以下结果
before importbefore functionAbefore functionBbefore __name__ guardFunction AFunction B 10.0after __name__ guard
(2)做为模块被导入,会输出以下结果:
before importbefore functionAbefore functionBbefore __name__ guardafter __name__ guard
可能有不少同窗会有这样的疑问,为何Python要提供这个功能呢?像C#、Java这样的编程语言并无这样的功能啊!其实这要从Python脚本的运行机理谈起。若是Python脚本做为主程序执行,这个执行方式与Java相似。不过当导入一个Python模块就不同了。对于Java语言,导入一个包,也只是导入而已,除非显式调用包中的API,不然单单导入,是不会执行Java代码的。但Python就不同了,若是使用import语句导入一个模块,实际上是先执行被导入模块中的全部代码,而后才会执行当前模块的代码。例如,下面的代码,会先执行foo.py脚本中的代码,而后才会执行print('current module')
import fooprint('current module')
执行这段代码,会输出以下的内容:
before importbefore functionAbefore functionBbefore __name__ guardafter __name__ guardcurrent module
若是一个Python脚本,同时便可以做为主程序执行,也能够做为模块被导入,这就要求在模块被导入时不执行做为主程序执行时的代码,因此若是是在主程序中执行的代码,若是使用__main__变量进行判断。
关于__name__的一些疑问
有的同窗问,在脚本文件中能够有多个__name__校验代码块吗?其实一般只有一个__name__校验代码块吗,但Python解析器并不会阻止你编写多个__name__校验代码块吗。
下面再给你们2段代码,看看输出结果会是什么:
# foo2.pydef functionA(): print("a1") import foo2 print("a2") functionB() print("a3")
def functionB(): print("b")
print("t1")if __name__ == "__main__": print("m1") functionA() print("m2")print("t2")
在这段代码中导入了模块自己(foo2.py),执行代码,会输出以下结果:
t1m1a1t1t2a2ba3m2t2
你们能够分析一下这段代码的执行过程。下面将if __name__ == "__main__":去掉,看看会发生什么。
# foo3.pydef functionA(): print("a1") import foo3 print("a2") functionB() print("a3")
def functionB(): print("b")
print("t1")print("m1")functionA()print("m2")print("t2")
执行这段代码,会引发死循环吗?实际上是不会的,由于Python解析器有缓存,若是一个模块在当前模块中已经被导入了,当第二次导入时,将不会再次执行被导入模块的代码,而是直接使用缓存中的内容,因此import foo3只会致使foo3.py文件中的代码做为导入模块的方式被执行一次。因此执行这段代码,输出结果以下:
t1m1a1t1m1a1a2ba3m2t2a2ba3m2t2

你们能够本身分析一下程序的执行过程。

推荐阅读   点击标题可跳转

一、连Python产生器(Generator)的原理都解释不了,还敢说Python用了5年?nginx

二、牛掰了!鸿蒙与Android完美融合,将鸿蒙设备当Android设备用web

三、【鸿蒙学院】鸿蒙App开发直播学员提问与回答编程

四、【鸿蒙学院】鸿蒙IDE:下载、安装DevEco Studioswift

五、 Python高效编程之88条军规(2):你真的会格式化字符串吗?缓存

六、像极客同样提取Android的Root权限微信


关注「极客起源」公众号,加星标,不错过精彩技术干货
app



本文分享自微信公众号 - 极客起源(geekculture)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。编程语言

相关文章
相关标签/搜索