Python基础:26模块

来源:互联网 发布:php中魔术常量 编辑:程序博客网 时间:2024/06/08 02:50

一:模块和文件

        1:模块是逻辑上组织 Python 代码的方法,文件是物理层上组织模块的方法。因此,一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件。 模块的文件名就是模块的名字加上扩展名 .py。

 

        2:从基本概念来说,一个名称空间就是一个从名称到对象的关系映射集合。每个模块都定义了它自己的唯一的名称空间。所以,即使有两个模块中,有相同名称的函数atoi,但它们的完整授权名称(fully qualified name)——通过句点属性标识指定了各自的名称空间——防止了名称冲突的发生。

 

        3:模块的导入需要一个"路径搜索"的过程。即在文件系统"预定义区域"中查找mymodule.py文件的过程。这些预定义区域是 Python 搜索路径的集合。

        有时候导入模块操作会失败:

>>> import xxx

Traceback (innermost last):

File "<interactive input>",line 1,in ?

ImportError: No module named xxx

        发生这样的错误时,意味着解释器无法访问请求的模块,可能的原因是模块不在搜索路径里,从而导致了路径搜索的失败。

 

        默认搜索路径是在编译或是安装时指定的。可以在一个或两个地方修改。

        一个是启动Python 的 shell 或命令行的 PYTHONPATH环境变量。该变量的内容是一组用冒号分割的目录路径。如果想让解释器使用这个变量,那么必须在启动解释器或执行 Python 脚本前手动设置或修改该变量。否则该变量为空。

        解释器启动之后,也可以访问这个搜索路径,它会被保存在 sys 模块的sys.path 变量里。比如下面是一个 Unix 搜索路径的样例:

>>> sys.path

['','/usr/local/lib/python2.x/','/usr/local/lib/ python2.x/plat-sunos5','/usr/local/lib/python2.x/lib-tk','/usr/local/lib/python2.x/lib-dynload','/usr/local/lib/Python2.x/site-packages',]

        注意该列表的第一个元素为空字符串,它对应着当前目录。

        这只是个列表,所以可以随时随地对它进行修改。如果知道需要导入的模块是什么,而它的路径不在搜索路径里,那么只需要调用列表的 append()方法即可,就像这样:sys.path.append('/home/wesc/py/lib')。修改完成后,就可以加载自己的模块了。

如果有一个模块的很多拷贝。解释器会使用沿搜索路径顺序找到的第一个模块。

        使用sys.modules 可以找到当前导入了哪些模块和它们来自什么地方。sys.modules 是一个字典,使用模块名作为键(key) ,对应物理地址作为值( value )。

 

二:名称空间

        1:名称空间是名称(标识符)到对象的映射。向名称空间添加名称的操作过程涉及到绑定标识符到指定对象的操作,以及给该对象的引用计数加 1 。

        程序执行期间有两个或三个活动的名称空间。这三个名称空间分别是局部名称空间,全局名称空间和内建名称空间但局部名称空间在执行期间是不断变化的,所以说是"两个或三个"。

        Python解释器首先加载内建名称空间。 它由 __builtins__ 模块中的名字构成。随后加载执行模块的全局名称空间,它会在模块开始执行后变为活动名称空间。如果在执行期间调用了一个函数,那么将创建出第三个名称空间,即局部名称空间。

可以通过 globals() 和 locals() 内建函数判断出某一名字属于哪个名称空间。

 

        2:访问一个属性时,解释器必须在三个名称空间中的一个能找到它。首先从局部名称空间开始,如果没有找到,将继续查找全局名称空间。如果这也失败了,它将在内建名称空间里查找。如果最后的尝试也失败了,会得到这样的错误:

>>> foo

Traceback (innermost last): File "<stdin>",line 1,in ?

NameError: foo

 

        因此,局部名称空间中找到的名字会隐藏全局或内建名称空间的对应对象。这就相当于"覆盖"了那个全局变量。

 

        3:无限制的名称空间

        Python的一个有用的特性在于,可以在任何需要放置数据的地方获得一个名称空间。可以在任何时候给函数添加属性(使用熟悉的句点属性标识)。比如:

def foo():

        pass

foo.__doc__ = 'Oops,forgot to add doc str above!'

foo.version = 0.2

        这对于类和模块也是一样的。可以把任何想要的东西放入一个名称空间里。

 

三:导入模块

        1:import语句

        使用 import 语句导入模块,语法如下所示:

import  module1

import  module2

......

import  moduleN

 

        也可以在一行内导入多个模块:

import  module1[, module2[,... moduleN]]

        但是这样的代码可读性不如多行的导入语句。而且在性能上和生成 Python 字节代码时没什么优势。所以推荐使用第一种格式。

 

        推荐所有的模块在 Python 模块的开头部分导入。而且最好按照这样的顺序:

Python 标准库模块;Python 第三方模块;应用程序自定义模块;然后使用一个空行分割这三类模块的导入语句。

 

        解释器执行到import语句, 如果在搜索路径中找到了指定的模块, 就会加载它。该过程遵循作用域原则, 如果在一个模块的顶层导入, 那么它的作用域就是全局的;如果在函数中导入, 那么它的作用域是局部的。

        如果模块是被第一次导入, 它将被加载并执行。在这样的语句中,调用方法必须是:module.name

 

        2:from-import语句

        可以在模块中导入指定的模块属性。也就是把指定名称导入到当前作用域。语法是:

from  module  import  name1[,name2[,... nameN]]

        这样的导入方法,访问属性时,直接使用name即可,不能使用module.name的方式。因为解释器不能识别module。

        这样的导入方式,也会执行顶层代码。而且,除了导入的name之外,其他的属性也是不可访问的。当然,name也可以引用本模块的其他符号,不会出错比如下面的例子:

#test.py

print  'this is test1'

 

def  foo():

        print 'this is foo, call bar now'

        bar()

      

def  bar():

        print 'this is bar'

 

print  'this is test2'

 

>>> from  test  import  foo

this  is  test1

this  is  test2

>>> foo()

this  is  foo,  call  bar  now

this  is  bar

>>> bar()

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

NameError: name 'bar' is not defined

 

        从一个模块导入许多属性时,import 行会很长,需要一个’/’。比如:

from Tkinter import Tk, Frame, Button, Entry, Canvas, \

Text, LEFT, DISABLED, NORMAL,RIDGE, END

        也使用多行的 from-import 语句:

from Tkinter import Tk, Frame, Button, Entry, Canvas, Text

from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END

 

        3:别名

        如果不想使用导入的名字,可以使用自己想要的名字替换模块的原始名称。使用扩展的 import , 就可以在导入的同时指定局部绑定名称。类似这样:        

import Tkinter

from cgi import FieldStorage

. . . 可以替换为 . . .

import Tkinter as tk

from cgi importFieldStorage as form

 

四:模块导入的特性

        1:加载模块会导致这个模块被"执行"。也就是被导入模块的顶层代码将直接被执行。这通常包括设定全局变量以及类和函数的定义。如果有检查__name__的操作, 那么它也会被执行。

        当然,这样的执行可能不是我们想要的结果。应该把尽可能多的代码封装到函数。明确地说,良好的模块编程习惯是只把函数和模块定义放入模块的顶层。

 

        2:一个模块可以导入多次,但是它只被加载一次,这可以阻止多重导入时,代码被多次执行。例如你的模块导入了 sys 模块, 而你要导入的其他模块也导入了它, 那么,加载只在第一次导入时发生。

 

        3:调用 from-import 可以把名字导入当前的名称空间里去,可以把指定模块的所有名称导入到当前名称空间里:from module import *

        但是,"from module import *" 不是良好的编程风格, 因为它"污染"当前名称空间, 而且很可能覆盖当前名称空间中现有的名字。

 

        只从模块导入名字的另一个副作用是,那些名字会成为局部名称空间的一部分。 这可能导致覆盖一个已经存在的具有相同名字的对象。对这些变量的改变只影响它的局部拷贝而不是所导入模块的原始名称空间。比如下面的例子

#############

# imptee.py #

#############

foo = 'abc'

def  show():

        print  'foo from imptee:',  foo

#############

# impter.py #

#############

from  imptee  import  foo,show

show()

foo = 123

print  'foo from impter:', foo

show()

        运行impter.py,发现从被导入者的观点看, 它的 foo 变量没有改变:

foo  from  imptee: abc

foo  from  impter: 123

foo  from  imptee: abc

 

        唯一的解决办法是使用 import 和完整的标识符名称(句点属性标识)。

 

        4:由于改进,新特性,以及当前特性增强, 某些变化会影响到当前功能。所以为了让 Python 程序员为新事物做好准备, Python 实现了 __future__ 指令。

        使用 from-import 语句"导入"新特性, 用户可以尝试一下新特性或特性变化, 以便在特性固定下来的时候修改程序。 它的语法是:

from  __future__  import  new_feature

        注意,只 import  __future__ 不会有任何变化,必须显示地导入指定特性。

 

        5:在 2.3 版中, Python 加入了从 ZIP 归档文件导入模块的功能。如果搜索路径中存在一个包含 Python 模块(.py, .pyc, or .pyo 文件)的 .zip 文件, 导入时会把 ZIP 文件当作目录处理, 在文件中搜索模块。

 

五:模块内建函数

        1:__import__()

        import 语句会调用__import__() 函数完成它的工作。提供这个函数是为了让有特殊需要的用户覆盖它, 实现自定义的导入算法。它的语法是:

__import__(module_name[, globals[, locals[, fromlist]]])

        module_name 变量是要导入模块的名称, globals 是包含当前全局符号表的名字的字典, locals 是包含局部符号表的名字的字典, fromlist 是一个使用 from-import 语句所导入符号的列表。globals , locals , 以及 fromlist 参数都是可选的, 默认分别为 globals() , locals() 和 [] 。

        比如,调用 import sys 语句可以使用下边的语句完成:

sys = __import__('sys')

 

        2:globals() 和locals()

        globals() 和locals() 内建函数分别返回调用者全局和局部名称空间的字典。 在一个函数内部, 局部名称空间代表在函数执行时候定义的所有名字,locals() 函数返回的就是包含这些名字的字典。 globals() 会返回函数可访问的全局名字。

        在全局名称空间下, globals() 和 locals() 返回相同的字典, 因为这时的局部名称空间就是全局空间。 例子如下:

def  foo():

        print  '\ncalling foo()...'

        aString = 'bar'

        anInt = 42

        print  "foo()'s globals:", globals().keys()

        print  "foo()'s locals:", locals().keys()

 

print  "__main__'sglobals:", globals().keys()

print  "__main__'slocals:", locals().keys()

foo()

 

执行这个脚本, 得到如下的输出:

__main__'s  globals: ['__doc__','foo', '__name__', '__builtins__']

__main__'s  locals: ['__doc__', 'foo','__name__', '__builtins__']

calling  foo()...

foo()'s globals: ['__doc__', 'foo', '__name__', '__builtins__']

foo()'s locals: ['anInt', 'aString']

 

        3:reload()

        reload()内建函数可以重新导入一个已经导入的模块。 语法如下:

reload(module)

        module是想要重新导入的module对象,它之前必须已经被成功的全部导入(不是使用 from-import)。否则解释器会无法识别module。 module必须是模块自身而不是包含模块名的字符串。 也就是说必须类似 reload(sys) 而不是 reload('sys')。

        该函数返回module对象。该函数的作用如下:修改了模块的源代码,在不离开当前python解释器的情况下,使用修改后的新版本。

        注意,reload()函数在每次调用时,都会执行模块的顶层代码。这不同于import语句。

 

六:包

        包是一个有层次的文件目录结构, 它用来帮助解决如下问题:

为平坦的名称空间加入有层次的组织结构

允许程序员把有联系的模块组合到一起

允许分发者使用目录结构而不是一大堆混乱的文件

帮助解决有冲突的模块名称

 

        与类和模块相同, 包也使用句点属性标识来访问他们的元素。使用标准的import 和 from-import 语句导入包中的模块。

 

        1:目录结构

        假定包的例子有如下的目录结构:

Phone/

        __init__.py

        common_util.py

        Voicedta/

                __init__.py

                Pots.py

                Isdn.py

        Fax/

                __init__.py

                G3.py

        Mobile/

                __init__.py

                Analog.py

                igital.py

        Pager/

                __init__.py

                Numeric.py

 

        Phone 是最顶层的包, Voicedta等是它的子包。可以只导入顶层的子包, 然后使用属性/点操作符向下引用子包树:

from Phone import Mobile

Mobile.Analog.dial('555-1212')

 

        可以一直沿子包的树状结构导入:

from Phone.Mobile.Analog import dial

dial('555-1212')

 

        为了让Python 将目录当做包,目录下必须包含 __init__.py 文件;最简单的情况下,__init__.py 可以只是一个空的文件,但它也可以为包执行初始化代码或设置__all__变量, from-import 语句导入子包时需要用到它。

        程序员经常忘记为它们的包目录加入 __init__.py 文件, 所以从Python 2.5 开始, 这将会导致一个ImportWarning信息。不过, 除非给解释器传递了 -Wd 选项, 否则它会被简单地忽略。

 

        2:包同样支持 from-import all 语句:from package.module import *

        然而, 这样的语句会导入哪些文件取决于操作系统的文件系统。所以可以在__init__.py中加入 __all__ 变量。该变量包含执行这样的语句时应该导入的模块的名字。它由一个模块名字符串列表组成。

 

        3:绝对导入和相对导入

        绝对导入是指,为了避免导入子包覆盖掉标准库模块(由于名字相同,发生冲突),所以规定import语句只能导入绝对路径的模块(从sys.path或PYTHONPATH导入),无法通过直接import包名字来导入隔壁包的模块。

        也就是说你在项目的某层里想导入隔壁包的某模块,需要从项目的根那一层一直点到当前的层级,然后再导入隔壁的包。比如在/Alpha/Beta/Gamma中导入/Alpha/Beta/Delta 包,就要importAlpha.Beta.Delta

        绝对导入特性从2.7版本开始成为默认功能,import语句总是绝对导入的。

 

        相对导入只应用于 from-import 语句,在包名字的前面加一个点,表示从当前层级下的某包导入。

        比如在/Alpha/Beta/Gamma 中导入/Alpha/Beta/Delta包中的模块,可以写成:from .Delta import xxx

        在包的名字前加两个点,表示从上一层级里的某包导入。比如在/Alpha/Beta/Gamma 中导入/Alpha/Sigma 包中的模块,可以写成

        from ..Sigma import xxx

        在开启了absolute_import特性之后,存在相对导入语句的模块,不能直接运行,会报错。

 

七:其他特性

        1:自动载入的模块

        当 Python 解释器在标准模式下启动时,一些模块会被解释器自动导入,用于系统相关操作。

        sys.modules变量包含一个由当前载入(完整且成功导入)到解释器的模块组成的字典, 模块名作为键, 它们的位置作为值。

        例如在 Windows 下:

>>> import sys

>>> sys.modules.keys()

['os.path', 'os', 'exceptions', '__main__', 'ntpath', 'strop', 'nt','sys', '__builtin__', 'site', 'signal', 'UserDict', 'string', 'stat']

        Unix 下载入的模块很类似:

>>> import sys

>>> sys.modules.keys()

['os.path', 'os', 'readline', 'exceptions', '__main__', 'posix', 'sys','__builtin__', 'site', 'signal', 'UserDict', 'posixpath', 'stat']

 

        2如果不想让某个模块属性被 "from module import *" 导入 , 那么你可以给你不想导入的属性名称加上一个下划线( _ )。

不过如果导入了整个模块或是你显式地导入某个属性(如importfoo._bar ), 这个隐藏数据的方法就不起作用了。

 

        3:从 Python 2.3 开始,Python的模块文件开始支持除 7 位 ASCII 之外的其他编码。 当然ASCII 是默认的。

        只要在 Python 模块头部加入一个额外的编码指示说明,就可以让导入者使用指定的编码解析你的模块,编码对应的 Unicode 字符串。 所以使用纯 ASCII 文本编辑器的时候不需要担心了。一个 UTF-8 编码的文件可以这样指示:

#!/usr/bin/env python

# -*- coding: UTF-8 -*-

        如果你执行或导入了包含非 ASCII 的 Unicode 字符串而没有在文件头部说明, 那么会在Python 2.3 得到一个 DeprecationWarning , 而在 2.5 中这样做会导致语法错误。

 

        4:如果模块 omh4cli 尝试导入 cli4vof , 而模块 cli4vof 也试着导入 omh4cli 。则最后谁也不会完成导入工作, 引发错误。

这是一个导入循环的例子,解决这个问题,需要移除其中一个导入语句。

 

0 0
原创粉丝点击