- 译者序
- 前言
- 第1章 问答环节
- 第2章 Python 如何运行程序
- 第3章 如何运行程序
- 第4章 介绍 Python 对象类型
- 第5章 数字
- 第6章 动态类型简介
- 第7章 字符串
- 第8章 列表与字典
- 第9章 元组、文件及其他
- 第10章 Python 语句简介
- 第11章 赋值、表达式和打印
- 第12章 if 测试和语法规则
- 第13章 while 和 for 循环
- 第14章 迭代器和解析,第一部分
- 第15章 文档
- 第16章 函数基础
- 第17章 作用域
- 第18章 参数
- 第19章 函数的高级话题
- 第20章 迭代和解析,第二部分
- 第21章 模块:宏伟蓝图
- 第22章 模块代码编写基础
- 第23章 模块包
- 第24章 高级模块话题
- 第25章 OOP:宏伟蓝图
- 第27章 更多实例
- 第28章 类代码编写细节
- 第29章 运算符重载
- 第30章 类的设计
- 第31章 类的高级主题
- 第32章 异常基础
- 第34章 异常对象
- 第35章 异常的设计
- 第36章 Unicode 和字节字符串
- 字符串基础知识
- Python 的字符串类型
- 文本和二进制文件
- Python 3.0 中的字符串应用
- 转换
- 编码 Unicode 字符串
- 编码非ASCII文本
- 编码和解码非ASCII文本
- 其他 Unicode 编码技术
- 转换编码
- 在 Python 2.6 中编码 Unicode 字符串
- 源文件字符集编码声明
- 使用 Python 3.0 Bytes 对象
- 序列操作
- 创建 bytes 对象的其他方式
- 混合字符串类型
- 使用 Python 3.0(和 Python 2.6)bytearray 对象
- 使用文本文件和二进制文件
- Python 3.0 中的文本和二进制模式
- 类型和内容错误匹配
- 使用 Unicode 文件
- 在 Python 3.0 中处理 BOM
- Python 2.6 中的 Unicode 文件
- Python 3.0 中其他字符串工具的变化
- Struct二进制数据模块
- pickle对象序列化模块
- XML解析工具
- 本章小结
- 本章习题
- 习题解答
- 第37章 管理属性
- 第38章 装饰器
- 第39章 元类
- 附录A 安装和配置
- 附录B 各部分练习题的解答
- 作者介绍
- 封面介绍
模块导入和重载
用简单的术语来讲,每一个以扩展名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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论