14.4 执行其他 (Python)程序

当讨论执行其他程序时,我们把它们分类为Python程序和其他所有的非Python程序,后者包括了二进制可执行文件或其他脚本语言的源代码。我们先讨论如何运行其他的Python程序,然后是如何用os模块调用外部程序。

14.4.1 导入

在运行时刻,有很多执行另外Python脚本的方法。正如我们先前讨论的,第一次导入模块会执行模块最高级的代码。不管你是否需要,这就是Python导入的行为。提醒,只有属于模块最高级的代码才是全局变量、全局类和全局函数声明。

14.4 执行其他 (Python)程序 - 图1核心笔记:当模块导入后,就执行所有的模块

这只是一个善意的提醒:在先前的第3章和第12章已经谈过了,现在再说一次,当导入Python模块后,就会执行所有的模块!当你导入foo模块时候,它运行所有最高级别的(即没有缩进的)Python代码,比如,“main()”。如果foo含有bar函数的声明,那么便执行def foo(…)。再问一次为什么会这样做呢?由于某些原因,bar必须被识别为foo模块中一个有效的名字,也就是说bar在foo的名称空间中。其次,解释器要知道它是一个已声明的函数,就像本地模块中的任何一个函数。现在我们知道要做什么了,那么如何处理那些不想每次导入都执行的代码呢?缩进它,并放入if name == ‘main’的内部。

跟着应该是一个if语句,它通过检测name来确定是否要调用脚本,比如,“if name == ‘main’”。如果相等的话,你的脚本会执行main内代码;否则只是打算导入这个脚本,那么可以在这个模块内对代码进行测试。

当导入Python模块后,会执行该模块!当你导入foo模块时候,它运行所有最高级别的(即没有缩进的)Python代码,再问一次为什么会这样做呢?由于某些原因,bar必须被识别为foo模块中一个有效的名字,也就是说bar在foo的名称空间中,其次,解释器要知道它是一个已声明的函数,就像本地模块中的任何一个函数。现在我们知道要做什么了,那么如何处理那些不想每次导入都执行的代码呢?缩进它,并放入if name == ‘main’的内部。

14.4 执行其他 (Python)程序 - 图2

这里是import2.py的内容:

14.4 执行其他 (Python)程序 - 图3

这是当我们导入import1时的输出:

14.4 执行其他 (Python)程序 - 图4

根据建议检测name值的迂回工作法,我们改变了importl.py和import2.py里的代码,这样的情况就不会发生了。

这里是修改后的import.py版本:

14.4 执行其他 (Python)程序 - 图5

接着是import2.py的代码,以相同的方式修改:

14.4 执行其他 (Python)程序 - 图6

当从Python中导入import1的时候,我们不再会得到任何输出:

14.4 执行其他 (Python)程序 - 图7

这不意味着在任何的情况下,都应该这样编写代码。在某些情况中,你可能想要显示输出来确定输入模块。这取决于你自身的情况。我们的目标是提供实效的编程例子来屏蔽副作用。

14.4.2 execfile()

显然,导入模块不是从另外的Python脚本中执行Python脚本最可取的方法。那也就不是导入过程。导入模块的副作用是导致最高级代码运行。

这章一开始,我们描述了如何通过文件对象,使用exec语句来读取Python脚本的内容并执行。下面的代码给出了例子:

14.4 执行其他 (Python)程序 - 图8

这3行可以调用execfile()来换掉:

14.4 执行其他 (Python)程序 - 图9

虽然上述代码执行了一个模块,但是仅可以在现有的执行环境下运行(比如,它自己的全局和局部的名称空间)。在某些情况下,可能需要用不同全局和局部的名称空间集合,而不是默认的集合来执行模块。execfile()函数的语法非常类似于eval()函数的。

14.4 执行其他 (Python)程序 - 图10

类似eval()、globals和locals都是可选的,如果不提供参数值的话,默认为执行环境的名称空间。如果只给定globals,那么locals默认和globals相同。如果提供locals值的话,它可以是任何映射对象(一个定义/覆盖了getitem()的对象)。在2.4之前,locals必须是一个字典。注意:(在修改的时候)小心局部名称空间。比较安全的做法是传入一个虚假的”locals”字典并检查是否有副作用。execfile()不保证不会修改局部名称空间。见《Python库参考手册》(Python Library Reference Manual)对execfile()的解释。

14.4.3 将模块作为脚本执行

Pythpon2.4里加入了一个新的命令行选项(或开关),允许从shell或DOS提示符,直接把模块作为脚本来执行。当以脚本的方式来书写模块的时候,执行它们是很容易的。可以使用命令行从你的工作目录调用你的脚本。

14.4 执行其他 (Python)程序 - 图11

如果模块是标准库的一部分,安装在site-packages里,或者仅仅是包里面的模块,处理这样的模块就不是那么容易了,尤其是它们共享了已存在的同名Python模块。举例来说,你想运行免费的Python web服务器,以便创建和测试你自己的Web页面和CGI脚本。

你将必须在命令行敲入如下的字符:

14.4 执行其他 (Python)程序 - 图12

这是段很长的命令,如果它是第三方的,你不得不深入到site-packages去找到它真正定位的地方。如果没给出完全的路径名,可以从命令行运行一个模块,并让Python的导入机制为我们做这种跑腿工作吗?答案是肯定的。我们可以用Python-c命令行开关:

14.4 执行其他 (Python)程序 - 图13

该选项允许你指定你想要运行的Python语句。虽然它可以这样工作,但问题是name模块不是 ‘main’…而是你正在使用的模块(需要的话,你可以参阅前面的3.4.1小节复习name)。在最后一行,解释器通过import装载了你的模块,并不是它当作脚本。因为如此,所有在if name == ‘main’之下的代码是不会执行的,所以你不得不手动地调用模块的test()函数,就如同前面我们所做的一样。所以我们想同时要两者的优点——能够在类库中执行作为脚本的模块而不是作为导入的模块。这就是-m参数的动机。现在可以像这样运行脚本:

14.4 执行其他 (Python)程序 - 图14

这是不小的改进。尽管如此,还没有完全如预想那样实现特性。所以在Python2.5中,-m开关有了更多的兼容性。从2.5开始,你可以用相同的参数来运行包内或需要特别加载的模块,比如zip文件里的模块,这是在2.3加入的特性(12.5.7小节,396页)。Python2.4只让你执行标准的库模块。所以初始版本的-m选项是不能运行特殊的模块如PyCHecker (Python的lint),或其他的性能测试器(注意这些是装载和运行其他模块的模块)。但是2.5版本解决了这个问题。