您的位置:首页 > 编程语言 > Python开发

Dave Python 练习十四 -- 模块

2011-09-15 16:15 357 查看
#encoding=utf-8
## *****************  模块  *************************

## ********** Part 1: 什么是模块       **************
#模块支持从逻辑上组织 Python 代码。 当代码量变得相当大的时候, 我们最好把代码分成一
#些有组织的代码段,前提是保证它们的彼此交互。 这些代码片段相互间有一定的联系, 可能是一个
#包含数据成员和方法的类, 也可能是一组相关但彼此独立的操作函数。 这些代码段是共享的,所以
#Python 允许 "调入" 一个模块, 允许使用其他模块的属性来利用之前的工作成果, 实现代码重用.
#这个把其他模块中属性附加到你的模块中的操作叫做导入(import) 。那些自我包含并且有组织的代
#码片断就是模块( module )。

#模块是用来组织 Python 代码的方法, 而包则是用来组织模块的。

## ********** Part 2: 模块和文件       **************
#如果说模块是按照逻辑来组织 Python 代码的方法, 那么文件便是物理层上组织模块的方法。
#因此, 一个文件被看作是一个独立模块, 一个模块也可以被看作是一个文件。 模块的文件名就是模
#块的名字加上扩展名 .py 。与其它可以导入类(class)的语言不同,在 Python 中你导入的是模块或模块属性。

## 2.1 模块名称空间
#一个名称空间就是一个从名称到对象的关系映射集合。模块名称是它们的属性名称中的一个重要部分。 例如
#string 模块中的 atoi() 函数就是 string.atoi() 。给定一个模块名之后, 只可能有一个模块被
#导入到 Python 解释器中, 所以在不同模块间不会出现名称交叉现象; 所以每个模块都定义了它自
#己的唯一的名称空间。 如果我在我自己的模块 mymodule 里创建了一个 atoi() 函数, 那么它的名
#字应该是 mymodule.atoi() 。 所以即使属性之间有名称冲突, 但它们的完整授权名称(fully
#qualified name)——通过句点属性标识指定了各自的名称空间 - 防止了名称冲突的发生。

## 2.2 搜索路径和路径搜索

#模块的导入需要一个叫做"路径搜索"的过程。 即在文件系统"预定义区域"中查找 mymodule.py
#文件(如果你导入 mymodule 的话)。 这些预定义区域只不过是你的 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 机器搜索路
#径的样例。切记, 搜索路径在不同系统下一般是不同的。

#import sys
#print(sys.path)
#-->
#['E:\\MyEclipseWorkplace\\MyPython\\src\\Practice', 'E:\\MyEclipseWorkplace\\MyPython\\src',
#'C:\\Python32\\DLLs', 'C:\\Python32\\lib', 'C:\\Python32', 'C:\\Python32\\lib\\site-packages',
#'C:\\Windows\\system32\\python32.zip']

#这只是个列表, 所以我们可以随时随地对它进行修改。 如果你知道你需要导入的模块是什么,
#而它的路径不在搜索路径里, 那么只需要调用列表的 append() 方法即可, 就像这样:
#import sys
#sys.path.append('D:\\Dave')
#print(sys.path)

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

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

#在执行期间有两个或三个活动的名称空间。 这三个名称空间分别是局部名称空间, 全局名称空间和内建名称空间,
# 但局部名称空间在执行期间是不断变化的, 所以我们说"两个或三个"。 从名称空间中访问这些名字依赖于它们的加载顺序,
# 或是系统加载这些名称空间的顺序。

#Python 解释器首先加载内建名称空间。 它由 __builtins__ 模块中的名字构成。 随后加载执
#行模块的全局名称空间, 它会在模块开始执行后变为活动名称空间。 这样我们就有了两个活动的名称空间。

#核心笔记: __builtins__ 和 __builtin__
#__builtins__ 模块和 __builtin__ 模块不能混淆。__builtins__ 模块包含内建名称空间中内建名字的集合。
#其中大多数(如果不是全部的话)来自 __builtin__ 模块, 该模块包含内建函数, 异常以及其他属性。
#在标准 Python 执行环境下,__builtins__ 包含 __builtin__ 的所有名字。

#如果在执行期间调用了一个函数, 那么将创建出第三个名称空间, 即局部名称空间。 我们可以
#通过 globals() 和 locals() 内建函数判断出某一名字属于哪个名称空间。

## 3.1 名称空间与变量作用域比较
#名称空间是纯粹意义上的名字和对象间的映射关系, 而作用域还指出了从用户代码的哪些物理位置可以访问到这些名字。

#注意每个名称空间是一个自我包含的单元。但从作用域的观点来看, 事情是不同的. 所有局部
#名称空间的名称都在局部作用范围内。局部作用范围以外的所有名称都在全局作用范围内。

#在程序执行过程中, 局部名称空间和作用域会随函数调用而不断变化, 而全局名称空间是不变的。

## 3.2 名称查找, 确定作用域, 覆盖
#访问一个属性时, 解释器必须在三个名称空间中的一个找到它。 首先从局部名称空间开始, 如果没有找到, 解释
#器将继续查找全局名称空间. 如果这也失败了, 它将在内建名称空间里查找。 如果最后的尝试也失败了, 你会得到这样的错误:
#>>> foo
#Traceback (innermost last): File "<stdin>", line 1, in ?
#NameError: foo
#这个错误信息体现了先查找的名称空间是如何"遮蔽"其他后搜索的名称空间的。 这体现了名称覆盖的影响。

## 3.3 无限制的名称空间
#Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间。

## ********** Part 4: 导入模块       **************

## 4.1 语句
#使用 import 语句导入模块, 它的语法如下所示:
#import module1
#import module2[
#:
#import moduleN
#
#也可以在一行内导入多个模块, 像这样
#import module1[, module2[,... moduleN]]
#但是这样的代码可读性不如多行的导入语句。 而且在性能上和生成 Python 字节代码时这两种
#做法没有什么不同。 所以一般情况下, 我们使用第一种格式。

#核心风格: import 语句的模块顺序
#我们推荐所有的模块在 Python 模块的开头部分导入。 而且最好按照这样的顺序:
#    Python 标准库模块
#    Python 第三方模块
#    应用程序自定义模块
#然后使用一个空行分割这三类模块的导入语句。 这将确保模块使用固定的习惯导入, 有助于减
#少每个模块需要的 import 语句数目。

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

## 4.2 from-import 语句
#可以在模块里导入指定的模块属性。 也就是把指定名称导入到当前作用域。 使用from-import 语句可以实现我们的目的, 它的语法是:
#from module import name1[, name2[,... nameN]]

## 4.3 多行导入
#从一个模块导入许多属性时,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
#
#不提倡使用不再流行的 from Tkinter import * 语句

## 4.4 扩展的 import 语句(as)
#有时候你导入的模块或是模块属性名称已经在你的程序中使用了, 或者你不想使用导入的名字。
#可能是它太长不便输入什么的, 总之你不喜欢它。 这已经成为 Python 程序员的一个普遍需求: 使
#用自己想要的名字替换模块的原始名称。一个普遍的解决方案是把模块赋值给一个变量:

#import longmodulename
#short = longmodulename
#del longmodulename

#使用时就可以使用 short.attribute来代替 longmodulename.attribute 。

#那么另一种更简便的方法:使用扩展的 import , 你就可以在导入的同时指定局部绑定名称。

#import Tkinter
#from cgi import FieldStorage
#可以替换为==》
#import Tkinter as tk
#from cgi import FieldStorage as form

## ********** Part 5: 模块导入的特性       **************

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

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

## 5.2 导入(import )和加载(load)
#一个模块只被加载一次, 无论它被导入多少次。 这可以阻止多重导入时代码被多次执行。 例
#如你的模块导入了 sys 模块, 而你要导入的其他 5 个模块也导入了它, 那么每次都加载 sys (或
#是其他模块)不是明智之举! 所以, 加载只在第一次导入时发生。

## 5.3 导入到当前名称空间的名称
#调用 from-import 可以把名字导入当前的名称空间里去, 这意味着你不需要使用属性/句点
#属性标识来访问模块的标识符。 例如, 你需要访问模块 module 中的 var 名字是这样被导入的:
#from module import var
#我们使用单个的 var 就可以访问它自身。 把 var 导入到名称空间后就再没必要引用模块了。
#当然, 你也可以把指定模块的所有名称导入到当前名称空间里:
#from module import *

#核心风格: 限制使用 "from module import *"
#在实践中, 我们认为 "from module import *" 不是良好的编程风格, 因为它"污染"当前名称
#空间, 而且很可能覆盖当前名称空间中现有的名字; 但如果某个模块有很多要经常访问的变量或者
#模块的名字很长, 这也不失为一个方便的好办法。
#我们只在两种场合下建议使用这样的方法, 一个场合是:目标模块中的属性非常多, 反复键入
#模块名很不方便, 例如 Tkinter (Python/Tk) 和 NumPy (Numeric Python) 模块, 可能还有
#socket 模块。另一个场合是在交互解释器下, 因为这样可以减少输入次数。

## 5.4 被导入到导入者作用域的名字
#只从模块导入名字的另一个副作用是那些名字会成为局部名称空间的一部分。这可能导致覆盖一个已经存在的具有相同名字的对象。
#而且对这些变量的改变只影响它的局部拷贝而不是所导入模块的原始名称空间。 也就是说, 绑定只是局部的而不是整个名称空间。

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

#import imptee
#imptee.show()
#imptee.foo = 123
#print('foo from impter:', imptee.foo)
#imptee.show()

## 5.5 关于 __future__
#使用 from-import 语句"导入"新特性, 用户可以尝试一下新特性或特性变化, 以便在特性固定下来的时候修改程序。 它的语法是:
#from __future__ import new_feature
#只 import __future__ 不会有任何变化,所以这是被禁止的。

## 5.6 警告框架
#和 __future__ 指令类似, 有必要去警告用户不要使用一个即将改变或不支持的操作, 这样他们会在新功能正式发布前采取必要措施。

#首先是应用程序(员)接口(Application programmers' interface , API)。 程序员应该有从
#Python 程序(通过调用 warnings 模块)或是 C 中(通过 PyErr_Warn() 调用)发布警告的能力。
#这个框架的另个部分是一些警告异常类的集合。 Warning 直接从 Exception 继承, 作为所有
#警告的基类: UserWarning , DeprecationWarning , SyntaxWarning , 以及 RuntimeWarning 。

#另一个组件是警告过滤器, 由于过滤有多种级别和严重性, 所以警告的数量和类型应该是可控
#制的。 警告过滤器不仅仅收集关于警告的信息(例如行号, 警告原因等等), 而且还控制是否忽略警
#告, 是否显示——自定义的格式——或者转换为错误(生成一个异常)。
#警告会有一个默认的输出显示到 sys.stderr , 不过有钩子可以改变这个行为, 例如,当运行
#会引发警告的 Python 脚本时,可以记录它的输出记录到日志文件中,而不是直接显示给终端用户。
#Python 还提供了一个可以操作警告过滤器的 API 。
#最后, 命令行也可以控制警告过滤器。 你可以在启动 Python 解释器的时候使用 -W 选项。

## 5.7 从 ZIP 文件中导入模块
#在 2.3 版中, Python 加入了从 ZIP 归档文件导入模块的功能。 如果你的搜索路径中存在一
#个包含 Python 模块(.py, .pyc, or .pyo 文件)的 .zip 文件, 导入时会把 ZIP 文件当作目录处
#理, 在文件中搜索模块。
#如果要导入的一个 ZIP 文件只包含 .py 文件, 那么 Python 不会为其添加对应的 .pyc 文件,
#这意味着如果一个 ZIP 归档没有匹配的 .pyc 文件时, 导入速度会相对慢一点。
#同时你也可以为 .zip 文件加入特定的(子)目录, 例如 /tmp/yolk.zip/lib 只会从 yolk 归
#档的 lib/ 子目录下导入。

## 5.8 "新的"导入钩子
#Python 2.3 引入的新导入钩子,从而简化了这个操作。 你只需要编写可调用的 import 类, 然
#后通过 sys 模块"注册"(或者叫"安装")它。
#你需要两个类: 一个查找器和一个载入器。 这些类的实例接受一个参数:模块或包的全名称。
#查找器实例负责查找你的模块, 如果它找到, 那么它将返回一个载入器对象。查找器可以接受一个
#路径用以查找子包(subpackages) 。载入器会把模块载入到内存。它负责完成创建一个 Python 模
#块所需要的一切操作, 然后返回模块。
#这些实例被加入到 sys.path_hooks 。 sys.path_importer_cache 只是用来保存这些实例, 这
#样就只需要访问 path_hooks 一次。 最后, sys.meta_path 用来保存一列需要在查询 sys.path 之
#前访问的实例, 这些是为那些已经知道位置而不需要查找的模块准备的。 meta-path 已经有了指
#定模块或包的载入器对象的读取器。

## ********** Part 6: 模块内建函数       **************

## 6.1 __import__()
#作为实际上导入模块的函数, 这意味着 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')

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

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

## 6.3 reload()
#reload() 内建函数可以重新导入一个已经导入的模块。 它的语法如下:
#    reload(module)
#module 是你想要重新导入的模块。使用 reload() 的时候有一些标准。 首先模块必须是全部
#导入(不是使用 from-import), 而且它必须被成功导入。另外 reload() 函数的参数必须是模块自
#身而不是包含模块名的字符串。 也就是说必须类似 reload(sys) 而不是 reload('sys')。
#模块中的代码在导入时被执行, 但只执行一次. 以后执行 import 语句不会再次执行这些代码,
#只是绑定模块名称。 而 reload() 函数不同。

## ********** Part 7: 包       **************
#包是一个有层次的文件目录结构, 它定义了一个由模块和子包组成的 Python 应用程序执行环境。
#与类和模块相同, 包也使用句点属性标识来访问他们的元素。 使用标准的 import 和from-import 语句导入包中的模块。

## 7.1 目录结构

#示例:
#import Phone.Mobile.Analog
#Phone.Mobile.Analog.dial()
#Phone 是最顶层的包, Voicedta 等是它的子包,Analog 是我们的模块(py 文件),dial 是模块里的函数

#也可使用 from-import 实现不同需求的导入。
#第一种方法是只导入顶层的子包, 然后使用属性/点操作符向下引用子包树:
#from Phone import Mobile
#Mobile.Analog.dial('555-1212')
#
#此外, 我们可以还引用更多的子包:
#from Phone.Mobile import Analog
#Analog.dial('555-1212')
#
#事实上, 可以一直沿子包的树状结构导入:
#from Phone.Mobile.Analog import dial
#dial('555-1212')

#注意:
#在包的目录中有__init__.py 文件。 这些是初始化模块,from-import 语句导入子包时需要用到它。 如果没有用到, 他们可以是空文件。
#程序员经常忘记为它们的包目录加入 __init__.py 文件, 所以从 Python 2.5 开始, 这将会导致一个ImportWarning 信息。

## 7.2 使用 from-import 导入包
#包同样支持 from-import all 语句:
#    from package.module import *
#然而, 这样的语句会导入哪些文件取决于操作系统的文件系统. 所以我们在__init__.py 中加
#入 __all__ 变量. 该变量包含执行这样的语句时应该导入的模块的名字. 它由一个模块名字符串列表组成.。

## 7.3 绝对导入
#包的使用越来越广泛, 很多情况下导入子包会导致和真正的标准库模块发生(事实上是它们的
#名字)冲突。 包模块会把名字相同的标准库模块隐藏掉, 因为它首先在包内执行相对导入, 隐藏掉
#标准库模块。
#为此, 所有的导入现在都被认为是绝对的, 也就是说这些名字必须通过 Python 路径
#(sys.path 或是 PYTHONPATH )来访问。
#这个决定的基本原理是子包也可以通过 sys.path 访问, 例如 import Phone.Mobile.Analog 。
#在这个变化之前, 从 Mobile 子包内模块中导入 Analog 是合理的。作为一个折中方案, Python 允
#许通过在模块或包名称前置句点实现相对导入。
#
#从 Python 2.7 开始, 绝对导入特性将成为默认功能。

## 7.4 相对导入
#相对导入特性稍微地改变了 import 语法, 让程序员告诉导入者在子包的哪里查找某个模块。
#因为 import 语句总是绝对导入的, 所以相对导入只应用于 from-import 语句。

#语法的第一部分是一个句点, 指示一个相对的导入操作。 之后的其他附加句点代表当前 from 起始查找位置后的一个级别。

#from Phone.Mobile.Analog import dial
#from .Analog import dial

## ********** Part 8: 模块的其他特性       **************

## 8.1 自动载入的模块
#当 Python 解释器在标准模式下启动时, 一些模块会被解释器自动导入, 用于系统相关操作。
#唯一一个影响你的是 __builtin__ 模块, 它会正常地被载入, 这和 __builtins__ 模块相同。

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

#windows 下:

#import sys
#print(sys.modules.keys())
#-->
#dict_keys(['reprlib', 'heapq', 'sre_compile', '_collections', 'locale', '_sre', 'functools',
#           '__main__', 'weakref', 'site', 'sysconfig', 'operator', 'io', 'encodings',
#           'copyreg', 'msvcrt', 'encodings.gbk', '_heapq', '_weakref', 'abc', 'builtins',
#           'linecache', 'errno', 'token', 'sre_constants', 're', 'encodings.latin_1',
#           'collections', 'ntpath', 'tokenize', 'nt', '_warnings', 'genericpath', 'stat',
#           'zipimport', 'warnings', '_codecs', '_bisect', 'encodings.utf_8', 'sys', 'getpass',
#           'codecs', '_thread', '_multibytecodec', 'os.path', '_functools', '_codecs_cn', '_locale',
#           'sitecustomize', 'keyword', 'bisect', 'signal', 'traceback', 'encodings.mbcs', '_io',
#           '_weakrefset', 'itertools', 'encodings.aliases', 'sre_parse', 'os', '_abcoll'])

## 8.2 阻止属性导入
#如果你不想让某个模块属性被 "from module import *" 导入 , 那么你可以给你不想导入的属
#性名称加上一个下划线( _ )。 不过如果你导入了整个模块或是你显式地导入某个属性(例如 import
#foo._bar ), 这个隐藏数据的方法就不起作用了。

## 8.3 不区分大小的导入
#在 Unix(区分大小写)和 Win32(不区分大小写)下, 一切都很明了, 但那些新的不区分大小写
#的系统不会被加入区分大小写的特性。 PEP 235 指定了这个特性, 尝试解决这个问题, 并避免那些
#其他系统上"hack"式的解决方法。 底线就是为了让不区分大小写的导入正常工作, 必须指定一个叫
#做 PYTHONCASEOK 的环境变量。 Python 会导入第一个匹配模块名( 使用不区分大小写的习惯 )。
#否则 Python 会执行它的原生区分大小写的模块名称匹配, 导入第一个匹配的模块。

## 8.4 源代码编码
#从 Python 2.3 开始, Python 的模块文件开始支持除 7 位 ASCII 之外的其他编码。 当然
#ASCII 是默认的, 你只要在你的 Python 模块头部加入一个额外的编码指示说明就可以让导入者
#使用指定的编码解析你的模块, 编码对应的 Unicode 字符串。 所以你使用纯 ASCII 文本编辑器的
#时候不需要担心了(不需要把你的字符串放入 "Unicode 标签" 里) 。
#一个 UTF-8 编码的文件可以这样指示:
##!/usr/bin/env python
## -*- coding: UTF-8 -*-
#如果你执行或导入了包含非 ASCII 的 Unicode 字符串而没有在文件头部说明, 那么你会在
#Python 2.3 得到一个 DeprecationWarning , 而在 2.5 中这样做会导致语法错误。

## 8.5 导入循环
#假设有A.py 和B.py 两个模块, 他们直接有相互的调用(即循环导入),那么在运行的时候就会报错。
#解决方法就是把 import 语句移到函数内部:
#
#A.py:
#def fun1():
#    import B
#    B.fun2()


-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware Weibo: http://weibo.com/tianlesoftware Email: dvd.dba@gmail.com
DBA1 群:62697716(满); DBA2 群:62697977(满) DBA3 群:62697850(满)
DBA 超级群:63306533(满); DBA4 群: 83829929(满) DBA5群: 142216823(满)
DBA6 群:158654907(满) DBA7 群:69087192(满) DBA8 群:172855474
DBA 超级群2:151508914 DBA9群:102954821 聊天 群:40132017(满)
--加群需要在备注说明Oracle表空间和数据文件的关系,否则拒绝申请
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: