Python——模块(1)

来源:互联网 发布:nginx 防止ddos 编辑:程序博客网 时间:2024/05/16 15:43
模块是最高级别的程序组织单元,它将程序代码和数据封装起来以便重用。
从实际角度来看,模块往往对应于Python程序文件。每一个文件都是一个模块,并且模块导入其他模块之后就可以使用导入模块定义的变量名。
模块可以由两个语句和一个重要的内置函数进行处理。

import:使客户端(导入者)以一个整体获取一个模块

from:允许客户端从一个模块文件中获取特定的变量名。

imp.reload:在不终止Python程序的情况下,提供了一种重新载入模块文件代码的方法。

模块至少有三个角色:

1.代码重用
2.系统命名空间的划分

3.实现共享服务和数据

===========================================================================

模块搜索路径

Python的模块搜索路径是下述的主要组件组合而成的结果:

1.程序的主目录
2.PYTHONPATH目录(如果已经进行了设置)
3.标准链接库目录
4.任何的.pth文件的内容(如果存在的话)

这四个组件组合起来就变成了sys.path,即目录名称字符串的列表。
这四个路径的第一和第三元素是自动定义的,第二和第四可以用于拓展路径,从而包含自己的源代码目录。

以下是Python使用路径组件的方式:

1.主目录
Python会首先在主目录内搜索导入的文件。当你运行一个程序的时候,这个入口是包含程序的顶层脚本文件的目录。当在交互式模式下工作时,这一入口就是当前工作的目录。

因为这个目录总是先被搜索,如果程序完全位于单一目录,所有导入都会自动工作,而不需要配置路径。另一方面,由于这个目录是先搜索的,其文件也将覆盖路径上的其他目录中具有同样名称的模块。如果你需要在自己的程序中使用库模块的话,小心不要以这种方式意外地隐藏库模块。

2.PYTHONPATH目录

之后,Python会从左至右(假设你设置了的话)搜索PYTHONPATH环境变量设置中所列出的所有目录。简而言之,PYTHONPATH是设置包含Python程序文件的目录的列表,这些目录可以是用户定义的或平台特定的目录名。

因为Python会先搜索主目录,当导入的文件跨目录时,这个设置才显得格外重要。

3.标准库目录

接着,Python会自动搜索标准库模块安装在机器上的那些目录。

4.  .pth文件目录

最后,Python有个相当新的功能,允许用户把有效的目录添加到模块搜索路径中去,也就是在后缀名为.pth(路径的意思)的文本文件中一行一行地列出目录。这些路径配置文件是和安装相关的高级功能。它提供了PYTHONPATH设置是一种替代方案。

===========================================================================

模块的创建

定义模块,只要使用文本编辑器,把一些Python代码输入至文本文件中,然后以".py"为后缀名进行保存,任何此类文件都会被自动认为是Python模块。在模块顶层制定的所有变量名都会变成其属性(与模块对象结合的变量名)。

下面创建名为module1.py的文件,文件中输入下面的def语句:

def printer(x):print(x)
将这个文件导入,就会创建一个拥有一个属性的模块对象:变量名为printer,而这个变量名引用了一个函数对象。

===========================================================================

模块的使用
客户端可以执行import或from语句,以使用我们刚才编写的简单模块文件。如果模块还没有加载,这两个语句就会去搜索、编译以及执行模块文件程序。主要的差别在于,import会读取整个模块,所以必须定义后才能读取它的变量名;from语句将获取(或者说是复制)模块特定的变量名。

------------------------------------------------------------------------------------------------------------------------

import 语句

下述例子,变量名module1有两个不同的目的:识别要被载入的外部文件,同时会生成脚本中的变量,在文件加载后,用来引用模块对象。

>>> import module1>>> module1.printer('Hello world!')Hello world!
------------------------------------------------------------------------------------------------------------------------
from 语句

因为from会把变量名复制到另一个作用域,所以它就可以让我们直接在脚本中使用赋值后的变量名,而不需要通过模块:

>>> from module1 import printer>>> printer('Hello World')Hello World
这时候我们使用printer时就可以少输入一些,可以直接输入变量名,而无须再嵌套模块名称之后。
------------------------------------------------------------------------------------------------------------------------

from *语句

当我们使用*时,会取得模块顶层所有赋了值的变量名的拷贝。在这里,我们还是在脚本中使用复制后得到的变量名printer,而不需要通过模块名。

>>> from module1 import *>>> printer('Hello word')Hello word
从技术角度上来说,import和from语句都会使用相同的导入操作,from *形式只是多加了个步骤,把模块中所有变量名复制到了进行导入的作用域中。从根本是来说,这就是把一个模块的命名空间融入到另一个模块之中,同样的,实际效果就是可以让我们少输入一些。

===========================================================================

导入只发生一次

模块会在第一次import或from时载入并执行,并且只在第一次如此。之后的导入操作都只会去除已经加载的模块对象。
例如,编写模块simple.py

print('Hello')spam = 1
此例中,print和=语句在模块第一次导入时执行,而变量spam也在导入时初始化:
>>> import simpleHello>>> simple.spam1
第二次和其后的导入并不会重新执行此模块的代码,只是从Python内部模块表中取出已经创建的模块对象,因此,变量spam不会再进行初始化。

>>> simple.spam = 2>>> import simple>>> simple.spam2
不过,有时需要一个模块的代码通过某种导入后再一次运行,这要通过稍后要介绍的内置函数reload实现。
===========================================================================

import和from是赋值语句

它们像def一样,是可执行的语句,而不是编译期间的声明,而且它们可以嵌套在if测试中,出现在函数def中,直到执行程序时,Python执行到这些语句,才会进行解析。它们都是隐形的赋值语句:

1.import将整个模块对象赋值给另一个变量名
2.from将一个或多个变量名赋值给另一个模块中同名的对象。

之前介绍的关于赋值语句方面的内容,也适用于模块的读取,例如,以from赋值的变量名会变成对共享对象的引用。
===========================================================================

from语句潜在的陷阱

因为from语句是将一个或多个变量名赋值给另一个模块中同名的对象,它会让变量位置更隐秘而模糊,它有破坏命名空间的潜质。因为如果使用from导入变量,而那些变量恰巧和作用域中现有变量同名,变量就会被悄悄地覆盖掉。使用简单的import语句不存在这个问题,因为你一定得通过模块吗才能获取其内容,而module.attr不会和你的作用域内的名为attr的变量相冲突。不过,使用from时,只要你了解并预料到可能发生这种事,在实际情况下这就不是一个大问题了。

再者,from module import *形式更有破坏命名空间的潜质,这种形式会把命名空间融入到另一个,使得模块的命名空间的分割特性失效。

所以,真正务实的建议是:简单模块一般倾向于使用import,而不是from,多数的from语句是用于明确列举出想要的变量,而且限制在每个文件中只用一次from * 形式。


【模块的命名空间能够通过属性__dict__或dir(module) 获取】

>>> dir(simple)['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'spam']
===========================================================================
模块重载

导入只会在模块第一次导入时加载和执行该模块的代码,之后的导入只会使用自己加载的模块对象,而不会重载或重新执行文件的代码。

而reload函数会强制已加载的模块的代码重新载入并重新执行。此文件中新的代码的赋值语句会在适当的地方修改现有的模块对象。

reload函数可以修改程序的一些部分,而无须停止整个程序。重载无法用于每种情况,但是能用时,可以缩短开发的流程。例如,想象一下,数据库程序必在启动时链接服务器,因为程序修改或调整可在重载后立即测试,在调试时,只需连接一次就可以了,长时间运行的服务器可以以这种方式更新自己。

------------------------------------------------------------------------------------------------------------------------

reload基础

与import和from不同的是:

1.reload是Python中的内置函数,而不是语句
2.传给reload的是已经存在的模块对象,而不是变量名。
3.reload在Python3.0中位于模块之中,并且必须导入自己。


因为reload期望得到的是对象,在重载之前,模块一定是已经预先成功导入了。重载看起来如下所示:

import module...use module.attributes......from imp import reloadreload(module)...use module.attributes...
一般用法是:导入一个模块,在文本编辑器中修改其源代码,然后将其重载。当调用reload时,Python会重读模块文件的源代码,重新执行其顶层语句。reload会在适当的地方修改模块对象,reload并不会删除并重建模块对象,因此,程序中任何引用该模块对象的地方,自动会受到reload的影响。下面是一些细节:

1.reload会在模块当前命名空间内执行模块文件的新代码。重新执行模块文件的代码会覆盖其现有的命名空间,并非进行删除而进行重建。
2.文件顶层赋值语句会使得变量名换成新值。
3.重载会影响所有使用import读取了模块的客户端
4.重载只会对以后使用from的客户端造成影响。之前使用from来读取属性的客户端并不会受到重载的影响,那些客户端引用的依然是重载前所取出的旧对象。

------------------------------------------------------------------------------------------------------------------------

reload实例

下面的实例,我们要修改并重载一个模块文件,但是不会终止交互模式Python会话。
编写changer.py的模块文件,内容如下:

message = 'First version'def printer():    print(message)
这个模块会建立并导入两个变量名:一个是字符串,另一个是函数。现在启动解释器,导入该模块,然后调用其导出的函数。

>>> import changer>>> changer.printer()First version
不要关掉解释器,现在,在另一个窗口中编辑该模块文件

message = 'After editing'def printer():    print('reloaded:',message)
然后,回到Python窗口,重载该模块从而获得新的代码。注意下面的交互模式,再次导入该模块并没有效果。我们得到了原始的message,即使文件已经修改过了。我们得调用reload,才能够获取新的版本。
>>> import changer>>> changer.printer()First version>>> from imp import reload>>> reload(changer)<module 'changer' from 'F:\\data\\changer.py'>>>> changer.printer()reloaded: After editing
注意,reload实际上为我们返回了模块对象:其结果通常被忽略,但是,因为表达式结果会在交模式提示符下打印出来,Python会打印默认的<module 'name'>表现形式。
------------------------------------------------------------------------------------------------------------------------

为什么要在意:模块重载

模块重载在较大系统中很有用处,在重新启动整个应用程序的代价太大时尤其如此。
重载在GUI工作中也很有用,组件的回调行为可以在GUI保持活动的状态下进行修改。
通常情况下,重载使程序能够提供高度动态的接口。

0 0
原创粉丝点击