返回介绍

模块导入和重载

发布于 2024-01-29 22:24:17 字数 5467 浏览 0 评论 0 收藏 0

到现在为止,本书已经讲到了“导入模块”,而实际上没有介绍这个名词的意义。我们将会在第五部分深入学习模块和较大的程序架构,但是由于导入同时也是一种启动程序的方法,为了能够入门,这一节将会介绍一些模块的基础知识。

用简单的术语来讲,每一个以扩展名py结尾的Python源代码文件都是一个模块。其他的文件可以通过导入一个模块读取这个模块的内容。导入从本质上来讲,就是载入另一个文件,并能够读取那个文件的内容。一个模块的内容通过这样的属性(这个术语我们将会在下一节定义)能够被外部世界使用。

这种基于模块的方式使模块变成了Python程序架构的一个核心概念。更大的程序往往以多个模块文件的形式出现,并且导入了其他模块文件的工具。其中的一个模块文件设计成主文件,或叫做顶层文件(就是那个启动后能够运行整个程序的文件)。

我们将会对这样的架构问题有更深入的探索。本章最关心的是被载入的文件通过导入操作最终可运行代码。正是如此,导入文件是另一种运行文件的方法。

例如,如果开始一个交互对话(从系统命令行、从开始菜单或者在IDLE中),你可以运行之前创建的文件script1.py,通过简单的import来实现(确保删除了在上一节中添加的input行,或者你无缘无故地需要按Enter键)。

这可以运行,但是在默认情况下,只是在每次会话的第一次运行(真的,不信你可以试一下)。在第一次导入之后,其他的导入都不会再工作,甚至在另一个窗口中改变并保存了模块的源代码文件也不行。

这是有意设计的结果。导入是一个开销很大的操作,以至于每个文件、每个程序运行不能够重复多于一次。当你学习第21章时会了解,导入必须找到文件,将其编译成字节码,并且运行代码。

但是如果真的想要Python在同一次会话中再次运行文件(不停止和重新启动会话),需要调用imp标准库模块中可用的reload函数(这个函数也是一个Python 2.6内置函数,但在Python 3.0中不是内置的)。

这里的from语句直接从一个模块中复制出一个名字(稍后更详细地介绍)。reload函数载入并运行了文件最新版本的代码,如果已经在另一个窗口中修改并保存了它,那将反映出修改变化。

这允许你在当前交互会话的过程中编辑并改进代码。例如,这次会话中,在第一个import和reload调用这段时间里,在script1.py中的第二个打印语句在另一个窗口中改成了2**16。

reload函数希望获得的参数是一个已经加载了的模块对象的名称,所以如果在重载之前,请确保已经成功地导入了这个模块。值得注意的是,reload函数在模块对象的名称前还需要括号,import则不需要。reload是一个被调用的函数,而import是一个语句。

这也就是为什么你必须把模块名称传递给reload函数作为括号中的参数,并且这也是在重载时得到了额外的一行输出的原因。最后一行输出是reload调用后的返回值的打印显示,reload函数的返回值是一个Python模块对象。函数将会在第16章介绍。

注意:版本差异提示:Python 3.0把reload内置函数移到了imp标准库模块中。它仍然像以前一样重载文件,但是,必须导入它才能使用。在Python 3.0中,运行import imp并使用imp.reload(M),或者像这里所示的,运行from imp import并使用reload(M)。我们将在下一节介绍import和from语句,并且在本书稍后更加正式地讨论这些内容。

如果你在使用Python 2.6(或者是更常见的2.X),reload可以作为内置函数使用,因此,不需要导入。在Python 2.6中,reload可以以两种形式使用,内置函数或者模块函数,这有助于向Python 3.0的转换。换句话说,在Python 3.0中仍然可以使用重载,但是需要一行额外的代码来导入对reload的调用。

向Python 3.0迁移,可能部分动机是由一些众所周知的问题所引起的,这些问题包括我们将在下一节讨论的reload和from语句。简而言之,用一个from载入的名字不会通过一个reload直接更新,但是,用一条import语句访问的名字则会。如果你的名字似乎不会在一次重载后改变,尝试使用import和module.attribute名称引用。

模块的显要特性:属性

导入和重载提供了一种自然的程序启动的选择,因为导入操作将会在最后一步执行文件。从更宏观的角度来看,模块扮演了一个工具库的角色,这将在第五部分学到。从一般意义上来说,模块往往就是变量名的封装,被认作是命名空间。在一个包中的变量名就是所谓的属性:也就是说,属性就是绑定在特定的对象上的变量名(就像一个模块)。

在典型的应用中,导入者得到了模块文件中在顶层所定义的所有变量名。这些变量名通常被赋值给通过模块函数、类、变量以及其他被导出的工具。这些往往都会在其他文件或程序中使用。表面上来看,一个模块文件的变量名可以通过两个Python语句读取——import和from,以及reload调用。

为了讲清楚,请使用文本编辑器创建一个名为myfile.py的单行的Python模块文件,其内容如下所示:

这也许是世界上最简单的Python模块文件之一了(它只包含了一行赋值语句),但是它已经足够讲明白基本的要点。当文件导入时,它的代码运行并生成了模块的属性。这个赋值语句创建了一个名为title的模块的属性。

可以通过两种不同的办法从其他组件获得这个模块的title属性。第一种,你可以通过使用一个import语句将模块作为一个整体载入,并使用模块名后跟一个属性名来获取它:

一般来说,这里的点号表达式代表了object.attribute的语法,可以从任何的object中取出其任意的属性,并且这是Python代码中的一个常用操作。在这里,我们已经使用了它去获取在模块myfile中的一个字符串变量title,即myfile.title。

作为替代方案,可以通过这样的语句从模块文件中获得(实际上是复制)变量名:

就像今后看到的更多细节一样,from和import很相似,只不过增加了对载入组件的变量名的额外的赋值。从技术上讲,from复制了模块的属性,以便属性能够成为接收者的直接变量。因此,能够直接以title(一个变量)引用导入字符串而不是myfile.title(一个属性)引用[1]

无论使用的是import还是from去执行导入操作,模块文件myfile.py的语句都会执行,并且导入的组件(对应这里是交互提示模式)在顶层文件中得到了变量名的读取权。也许在这个简单的例子中只有一个变量名(变量title被赋值给一个字符串),但是如果开始在模块中定义对象,例如,函数和类时,这个概念将会很有用。这样一些对象就变成了可重用的组件,可以通过变量名被一个或多个客户端模块读取。

在实际应用中,模块文件往往定义了一个以上的可被外部文件使用的变量名。下面这个例子中定义了三个变量名:

文件treenames.py,给三个变量赋值,并对外部世界生成了三个属性。这个文件并且在一个print语句中使用它自有的三个变量,就像在将其作为顶层文件运行时看到的结果一样:

所有的这个文件的代码运行起来就和第一次从其他地方导入(无论是通过import或者from)后一样。这个文件的客户端通过import得到了具有属性的模块,而客户端使用from时,则会获得文件变量名的复本。

这里的结果打印在括号中,因为它们实际上是元组(本书的下一部分介绍的一种对象);目前我们可以暂时忽略它们。

一旦你开始就像这里一样在模块文件编写多个变量名,内置的dir函数开始发挥作用了。你可以使用它来获得模块内部的可用的变量名的列表。下面代码返回了一个Python字符串列表(我们将从下一章开始学习列表):

我们在Python 3.0和Python 2.6中分别运行它,较早的Python可能返回较少的名字。当dir函数就像这个例子一样,通过把导入模块的名称传至括号里,进行调用后,它将返回这个模块内部的所有属性。其中返回的一些变量名是“免费”获得的:一些以双下划线开头并结尾的变量名,这些通常都是由Python预定义的内置变量名,对于解释器来说有特定的意义。那些通过代码赋值而定义的变量(a、b和c)在dir结果的最后显示。

模块和命名空间

模块导入是一种运行代码文件的方法,但是就像稍后我们即将在本书中讨论的那样,模块同样是Python程序最大的程序结构。

一般来说,Python程序往往由多个模块文件构成,通过import语句连接在一起。每个模块文件是一个独立完备的变量包,即一个命名空间。一个模块文件不能看到其他文件定义的变量名,除非它显式地导入了那个文件,所以模块文件在代码文件中起到了最小化命名冲突的作用。因为每个文件都是一个独立完备的命名空间,即使在它们拼写相同的情况下,一个文件中的变量名是不会与另一个文件中的变量冲突的。

实际上,就像你将看到的那样,正是由于模块将变量封装为不同部分,Python具有了能够避免命名冲突的优点。我们将会在本书后面章节讨论模块和其他的命名空间结构(包括类和函数的作用域)。就目前而言,模块是一个不需要重复输入而可以反复运行代码的方法。

注意:import VS from:我应该指出,from语句在某种意义上战胜了模块的名称空间分隔的目的,因为from把变量从一个文件复制到另一个文件,这可能导致在导入的文件中相同名称的变量被覆盖(并且,如果发生这种情况的话,不会为你给出警告)。这根本上会导致名称空间重叠到一起,至少在复制的变量上会重叠。

因此,有些人建议使用import而不是from。然而,我不建议这么做,不仅因为from更短,而且因为它传说中的问题在实际中几乎不是问题。此外,这是由你来控制的问题,可以在from中列出想要的变量;只要你理解它们将是要赋的值,这不会比编写赋值语句更危险,而赋值是你可能想要使用的另一功能。

[1]注意,import和from列出模块名时,都是使用myfile,没有.py后缀。到了第五部分,你就会学到,当Python寻找实际文件时,知道在搜索程序中加上后缀名。然而,系统shell命令行中,一定要记得加上后缀名,但是import语句中则不用。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文