python学习笔记7-模块整理

时间:2022-03-26 00:31:22

一、模块
模块是Pyhon*别的程序组织单元,它将程序代码和数据封装起来以便重用。实际的角度,模块往往对应Python程序文件。
每个文件都是一个模块,并且模块导入其他模块之后就可以使用导入模块定义的变量名。模块可以由两个语句和一个重要的内置函数进行处理。
import: 使客户端(导入者)以一个整体获取一个模块。
from:容许客户端从一个模块文件中获取特定的变量名。
reload:在不中止Python程序的情况下,提供了一个重新载入模块文件代码的方法。

在一个模块文件的顶层定义的所有变量名都成为了被导入的模块对象的属性。
模块至少有三个角色:
代码重用:模块还是定义变量名的空间,被认作是属性。可以被多个外部的客户端应用。
系统命名空间的划分:
现实共享服务和数据:
1、python程序构架
一个ptyhon程序包括了多个含有Python语句的文件。程序是作为一个主体的,顶层的文件来构造的,配合有零个或多个支持文件,在Python中这些文件称作模块。
标准模块:python自带了200多个使用的模块、成为标准连接库
import如何工作
执行三个步骤
1)、找到模块文件
2)、编译成位码(需要时)
3)、执行模块的代码来创建其所定义的对象。
在之后导入相同的模块时候,会跳过这三个步骤,而只提取内存中已加载模块对象。

搜索模块
导入模块时,不带模块的后缀名,比如.py
Python搜索模块的路径:
1)、程序的主目录
2)、PTYHONPATH目录(如果已经进行了设置)
3)、标准连接库目录(一般在/usr/local/lib/python2.X/)
4)、任何的.pth文件的内容(如果存在的话).新功能,允许用户把有效果的目录添加到模块搜索路径中去
.pth后缀的文本文件中一行一行的地列出目录。
这四个组建组合起来就变成了sys.path了,
>>> import sys
>>> sys.path
导入时,Python会自动由左到右搜索这个列表中每个目录。
第1,第3元素是自动定义的,第2,第4可以用于扩展路径,从而包括自己的源码目录。
import b的形式可能加载
源码文件b.py
字节码文件.pyc
目录b
编译扩展模块,比如linux的b.so
用C编写的编译好的内置模块,并通过静态连接至Python
ZIP文件组件,导入时自动解压压缩。
java类型,在Jython版本的python中。
.NET组件,在IronPython版本中的Python中

脚本中随处可见 object.attribute这里表达式法:多数对象都有一些可用的属性。可以通过"."运算符取出。
有些是可调用的对象。例如,函数。

第三方工具:distutils
第三方扩展,通常使用标准连接库中的distutils工具来自动安装。使用distutils的系统一般附带setup.py脚本

命令空间是一种独立完备的变量包,而变量就是命名空间对象的属性。模块的命令空间包含了代码在模块文件顶层赋值的所有变量名(也就是没有嵌套与def和class语句中)

二、模块代码编写基础
1、模块的创建和使用。
创建模块
后缀.py文本文件,模块顶层指定的所有变量名都会变成其属性。
定义一个module.py模块
name='diege'
age=18
def printer(x):
print x
使用模块
import全部导入
>>> import module
属性
>>> module.name
'diege'
函数
>>> module.printer('hi')
hi
>>> module.printer('9')
9
【from语句】
from 将获取(复制)模块特定变量名
from 模块名 import 需要复制的属性
from 模块名 import 需要复制的属性 as 新的属性名
from会把变量名赋值到另一个作用域,所以它就可以让我们直接在脚本中使用复制后的变量名,而不是通过模块
>>> from module import name
>>> name
'diege
>>> from module import name as myname
>>> myname
'diege'
>>> from module import printer as PR
>>> PR('hi python')
hi python
>>> PR('99')
99
from * 语句
from 模块名 import *
取得模块顶层所有赋了值的变量名的拷贝。
from 模块名 import * 【不推荐用这种方法,如果这样方法不会导入_开头的模块属性】

模块只导入一次,因为该操作开销大
import和from是赋值语句,是可执行的语句,可以嵌套到if ,def语句中
【和def一样import和from都是隐性赋值语句】
*import将整个模块对象赋值给一个变量名
*from将一个或多个变量名赋值给另一个模块中同名的对象
*from as将一个或者多个变量名赋值给另一个模块中不同名的对象
>>> from module import name,age 复制多个变量名时要用逗号隔开
>>> name,age
('diege', 18)
>>> from module import name as myname,age as myage 复制多个变量名并改变需时需要用逗号隔开多个as

from语句的潜在陷阱
from让变量位置更隐秘和模糊。
from语句有破坏命名空间的潜质。如果from导入变量,而那些变量碰巧和作用域中现有的变量同名,变量就会被悄悄地覆盖掉。
使用简单的import时就不会存在这个问题。因为必须通过模块名才能获取其内容。
真正务实的建议就是:简单模块一般倾向于使用import而不是from。
【多数的from语句是用于明确列举出想要的变量,而且限制在每个文件中只用一次from *形式。】
何时使用import
当使用两个不同的模块内定义的相同变量名时,才真的必须使用import.用from会覆盖。

2、模块命名空间
模块最好理解为变量名的封装,简而言之,模块就是命名空间(变量名建立所在的场所),而存在于模块之内的变量名就是模块对象的属性。
文件生成命名空间
*模块语句会在首次导入时执行。
*顶层的赋值语句会创建模块属性(文件顶层不在的def和class之内的,但def和class隐性创建的变量名也属于模块属性)。赋值的变量名会存储在模块的命名空间内。
*模块的命名空间能通过属性__dict__(module.__dict__)或dir(module)获取
由于导入而建立的模块的命名空间是字典,可通过模块对象相关联的内置__dict__属性读取。
dir函数查看,大至与对象的__dict__属性的键排序后的列表相等,但是它还包含了类继承的变量名。
*模块是一个独立的作用域。
3、重载模块
模块程序代码默认值对每个过程执行一次,要强制使模块代码重新载入并重新运算需要使用reload内置函数。
reload是函数,import是语句。两个语法不一样。
>>> import module
>>> reload(module)
<module 'module' from 'module.pyc'>
>>> reload(test17)
<module 'test17' from '/root/test17.py'>

reload()之前需得import过一次

三、模块包
除模块名以外,导入也可以指定目录路径,Python代码的目录就是称为包。因此这类导入就称为包导入
1、模块包基础
import dir1.dir2.mod
from dir1.dir2.mod import x
.号路径相当于机器上目录层次的路径。
dir1在容器目录dir0中,dir0这个目录可以在Python模块搜索路径中找到。
__init__.py包文件
如果选择使用包导入,那就必须遵循一条约束:包导入语句的路径的每个目录内部都必须有__init__.py这个文件,
否则导入包会失败。
dir1和dir2中必须包含__init__.py,容器目录dir0不需要这类文件。因为本身没有列在import语句中
__init__.py文件可以包含程序代码,也可以是空的。 更通常情况下,__init__.py文件扮演了包初始化的挂钩
替目录产生模块命名空间以及用目录导入实现from *行为的角色。
*包初始化:
首次导入某个目录时,会自动执行该目录下__init__.py文件中所有程序代码。
所以这个文件就是放置包内文件所需初始化的代码的场所。可以使用其初始化文件,创建所需的数据文件,
连接数据库等。
*模块命名空间的初始化:
*from * 语句的行为:
【作为一个高级功能,可以在__init__.py文件中使用__all__列表来定义目录以form *语句形式导入时,需要
导出什么。__all__列表是指出当包(目录—)名称使用from *的时候,应该导入的子模块名称清单。】
eg:
/usr/local/lib/python2.7/sqlite3/__init__.py
from dbapi2 import *
/usr/local/lib/python2.7/site-packages/mod_python/__init__.py
__all__ = ["apache", "cgihandler", "psp",
"publisher", "util", "python22"]

version = "3.3.1"
常见的第三方扩展都是以包目录形式发布给用户,而不是单纯的模块列表。
这样就可以通过路径来导入
from mod_python.apache import CallBack
从mod_python包(一个安装子目录)的apache模块取出一些变量

四、高级模块话题
相对导入语法,数据隐藏,__future__模块,__name__变量和sys.path修改等。
__future__模块 启用以后的语言特性。
from __future__ import featurename 这个语句应该出现在模块文件的顶端。
1、在模块中隐藏数据
最小化from *的破坏:【_X和__all__达到隐藏变量名的目的】
有种特定情况,可以把下划线放在变量名前(_X),可以防止客户端使用from * 语句导入模块名时,把其中的那些变量名赋值出去。这其实是为了把命名空间的破坏最小化而已。下划线和__all__不是私有声明,还可以通过其他导入形式修改这类变量名。
例如import语句、from module import _X
以外,也可以在模块顶层把变量名的字符串列表赋值给变量__all__,以达到类似于_X命名惯例的隐藏效果【__all__是不隐藏的】
mod_python.__all__ 可以看到可以用from *语句复制那些变量名
_X和__all__ 对比
_X 隐藏了 无法from * #不过如果你导入了整个模块或是你显式地导入某个属性(例如 importfoo._bar ), 这个隐藏数据的方法就不起作用了。
__all__ 只显示,from *只能获取__all__中指定的,其他隐藏。
python中from *会先寻找模块内的__all__列表,有的话复制其中的变量名,如果没有定义的话,from *就会复制开头没有下划线的所有命令名。

>>> dir(mod_python)
['__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'version']
>>> mod_python.__all__
['apache', 'cgihandler', 'psp', 'publisher', 'util', 'python22']
>>> from mod_python.apache import *

怎么觉得__all__列表里存放的是模块呢???
2 、混合用法模式:__name__和__main__
这是一个特殊的与模块相关的技巧,可以把文件作为模块导入,并以独立式程序的形式运行。每个模块都有个名为__name__的内置属性。Python会自动设置该属性:
*如果文件是以顶层程序文件执行,在启动时,__name__就会被设置为字符串__main__
*如果文件被导入,__name__就会改设成客户端所了解模块名。
结果就是模块可以检测自己的__name__,来确定它是执行还是在导入。
定义一个文件test15.py
def tester():
print "It's python test!"
if __name__=='__main__':
tester()
导入模块
>>> import test15
>>> test15.tester()
It's python test
模块导入后要调用函数才能获得结果
直接执行
# python test15.py
It's python test
直接执行就可以获得结果
简而言之,可以在文件末尾加一个__name__测试,把测试模块导出的程序代码放在模块中。这个是最常见的自我测试代码,也是最常见最简单的单元测试协议。
编写既可以做命令行工具,也可以作为工具库使用文件时。__name__技巧很好用。
以__name__进行单元测试
>>> def minmax(test,*args):
... res=args[0]
... for arg in args[1:]:
... if test(arg,res):
... res=arg
... return res
...
>>> def lessthan(x,y):return x<y
>>> def greatthan(x,y):return x>y
>>> print minmax(lessthan,4,2,1,5,6,3)
1
>>> print minmax(greatthan,4,2,1,5,6,3)
6
改进之后
def minmax(test,*args):
res=args[0]
for arg in args[1:]:
if test(arg,res):
res=arg
return res

def lessthan(x,y):return x<y
def greatthan(x,y):return x>y
if __name__=='__main__':
print minmax(lessthan,4,2,1,5,6,3)
print minmax(greatthan,4,2,1,5,6,3)
3、修改模块搜索路径
可以通过PYTHONPATH以及可能的.pth路径文件进行定制。
Python程序本身是修改sys.path的内置列表。sys.path在程序启动时就进行初始化,但那之后也可以随意对其元素进行删除,附加和重设
>>> import sys
>>> sys.path
# cd /tmp/
# python
>>> sys.path.append('/root')
>>> sys.path
['', '/usr/local/lib/python2.7/site-packages/MySQL_python-1.2.3-py2.7-freebsd-8.2-RELEASE-i386.egg', '/usr/local/lib/python2.7/site-packages/setuptools-0.6c12dev_r88846-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Babel-0.9.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Trac-0.12.3-py2.7.egg', '/usr/local/lib/python2.7/site-packages/Genshi-0.6-py2.7.egg', '/usr/local/lib/python2.7/site-packages/IniAdmin-0.2_r10454-py2.7.egg', '/usr/local/lib/python2.7/site-packages/TracAccountManager-0.4dev_r11251-py2.7.egg', '/usr/local/lib/python2.7/site-packages/SvnAuthzAdminPlugin-0.2-py2.7.egg', '/usr/local/lib/python27.zip', '/usr/local/lib/python2.7', '/usr/local/lib/python2.7/plat-freebsd8', '/usr/local/lib/python2.7/lib-tk', '/usr/local/lib/python2.7/lib-old', '/usr/local/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/site-packages', '/root']
导入/root目录下test17.py,注意启动python时在/tmp目录,所以/root不是程序启动目录
>>> import test17
>>> dir(test17)
>>> test17.lessthan(3,4)
True
4、相对导入语法
from语句现在可以使用点号(.)更倾向于同一个包内的模块(称为包相对导入),而不是位于模块导入搜索路径上其他地方的模块(所谓的绝对导入)
*现在,可以使用点号指出该导入应该与其所在包相关联:这类导入倾向于导入位于该包内的模块,而不是导入搜索路径sys.path上其他地方的同名模块
from .apache CallBack as CB
同一个包内导入apache模块CallBack为CB变量
5、模块设计理念
*总是在Python的模块内编写代码
*模块耦合要降到最底:全局变量。模块应该尽可能和其他模块的全局变量无关。
*最大化模块的沾合性:统一目标
*模块应该少去修改其他模块的的变量。
1)模块是对象:元程序
模块通过内置属性显示了他们大多数的特性。因此,可很容易的编写程序来管理其他程序。我们通常称这类管理程序为元程序,因为他们是在其他系统上工作。这也称为内省,因为程序能看见和处理对象的内部。内省是高级功能,但是它可以做创建程序工具,要取得模块内名为name的属性,(1)可以使用结合点号运算,(2)或者对模块的属性字典进行索引运算(在内置__dict__属性中显示)。
(3)Python也在sys.modules字典中导出所有已经加载的模块。(4)并提供一个内置函数getattrr,让我们以字符串名来取出属性。(就好像object.attr,而attr是运行时的字符串)
>>> test17.name
'diege'
>>> test17.__dict__.keys()
>>> test17.__dict__['name']
'diege
>>> test17.__dict__['lessthan']
<function lessthan at 0x28495844>
>>> sys.modules 显示所有加载的模块
>>> sys.modules['test17']
<module 'test17' from '/root/test17.py'>
>>> sys.modules['test17'].name
'diege
>>> getattr(test17,'lessthan')
<function lessthan at 0x28495bc4>

6、模块陷阱
1)顶层代码的语句次序的重要性
*在导入时,模块文件顶层的程序代码(不在函数内)一旦python运行,就会立刻执行。因此,该语句是无法引用文件后面位置赋值的变量名。
*位于函数主体内的代码知道函数被调用后才会运行。因为函数内的变量名在函数实际执行前都不会解析,通常可以引用文件内任意地方的变量。在顶层程序内混用def不尽难读,也造成了对语句顺序的依赖性。作为一条原则,如果需要把立即执行的代码和def一起混用,就要把def放在文件前面,把顶层代码放在后面。这样的话,你的函数在使用的代码运行时,可以保证他们都已定义并赋值过了。
2)通过变量名字符串导入模块的方法
import或from语句内的模块名是”硬编码“的变量名。
>>> x='string'
>>> import x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named x
这里python会试着导入文件x.py
为了避免发生这样的问题,通常的做法就是把import语句构造成python代码的字符串,再传给exec语句执行:
>>> modname='string'
>>> exec "import "+modname
exec语句以及和他类似eval会编译一段字符串代码,将其传给Python解析器来执行。
3)from复制变量名,而不是连接
from语句其实是在导入者的作用域内对变量名的赋值语句,也就是变量名拷贝运算,而不是变量名的别名机制。它的实现和python所有赋值运算都一样,微妙之处在于,共享对象的代码存在于不同的文件中。然后,我们使用import获得了整个模块,然后赋值某个点号运算的变量名,就会修改导入的模块中的变量名。点号运算把python定向到了模块对象,而不是赋值模块中对象。
4)from*会让变量语义模糊
5)reload不会影响from导入
6)不要使用reload、from以及交互模式测试
reload中引用模块得通过import至少将其加载一次:
不要from导入之后reload
7) reload使用没有传递性
当重载一个模块时,Python只会重载那个模块的文件,不会自动重载该文件重载嘶碰巧还要导入的模块。
8)递归形式的from import无法工作
不要在递归导入中使用 from。

【核心编程添加】
一、名称空间【命名空间】
名称空间是名称(标识符)到对象的映射。 向名称空间添加名称的操作过程涉及到绑定标识符到
指定对象的操作(以及给该对象的引用计数加 1 )。 改变一个名字的绑定叫做重新绑定, 删除一个名字叫做解除绑定。
在执行期间有两个或三个活动的名称空间。 这三个名称空间分别是
局部名称空间, 全局名称空间和内建名称空间, 但局部名称空间在执行期间是不断变化的, 所以我
们说"两个或三个"。 从名称空间中访问这些名字依赖于它们的加载顺序, 或是系统加载这些名称空
间的顺序。
Python 解释器首先加载内建名称空间。 它由 __builtins__ 模块中的名字构成。 随后加载执
行模块的全局名称空间, 它会在模块开始执行后变为活动名称空间。 这样我们就有了两个活动的名
称空间。

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

二、名称查找, 确定作用域, 覆盖
它所要做的就是名称查询. 访问一个属性时, 解释器必须在三个名称空间中的一个找到它。 首先从局部名称空间开始, 如果没有找到, 解释
器将继续查找全局名称空间. 如果这也失败了, 它将在内建名称空间里查找。 如果最后的尝试也失
败了, 你会得到这样的错误:
>>> foo
Traceback (innermost last): File "<stdin>", line 1, in ?
NameError: foo

三、无限制的名称空间
Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间。
无限制的名称空间
Python 的一个有用的特性在于你可以在任何需要放置数据的地方获得一个名称空间
def foo():
pass
foo.__doc__ = 'Oops, forgot to add doc str above!'
foo.version = 0.2
外部
class MyUltimatePythonStorageDevice(object):
pass
bag = MyUltimatePythonStorageDevice()
bag.x = 100
bag.y = 200
bag.version = 0.1
bag.completed = False

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

五、模块内建函数
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')
2.globals() 和 locals()
globals() 和 locals() 内建函数分别返回调用者全局和局部名称空间的字典。 在一个函数内
部, 局部名称空间代表在函数执行时候定义的所有名字, locals() 函数返回的就是包含这些名字
的字典。 globals() 会返回函数可访问的全局名字。

要让模块中函数的局部命名空间,可以提升为全局命名空间,可使用global
def testjin():
global name
name='jin'

testjin()
print name
这里要注意,函数testjin()要执行后,函数内testjin()中局部变量name才会提升为全局变量,函数外才能访问
但是一般都不用这种临时的方式,全局的就在全局定义,局部的就在局部定义

3、reload()
内建函数可以重新导入一个已经导入的模块。 它的语法如下:
reload(module)
module 是你想要重新导入的模块。使用 reload() 的时候有一些标准。 首先模块必须是全部
导入(不是使用 from-import), 而且它必须被成功导入。另外 reload() 函数的参数必须是模块自
身而不是包含模块名的字符串。 也就是说必须类似 reload(sys) 而不是 reload('sys')。


六、相关模块
1、 imp - 这个模块提供了一些底层的导入者功能。
2、modulefinder - 该模块允许你查找 Python 脚本所使用的所有模块。你可以使用其中的
ModuleFinder 类或是把它作为一个脚本执行, 提供你要分析的(另个) Python 模块的文件
名。
3、 pkgutil - 该模块提供了多种把 Python 包打包为一个"包"文件分发的方法。 类似 site
模块, 它使用 *.pkg 文件帮助定义包的路径, 类似 site 模块使用的 *.pth 文件。
4、site - 和 *.pth 文件配合使用, 指定包加入 Python 路径的顺序, 例如 sys.path ,
PYTHONPATH 。你不需要显式地导入它, 因为 Python 导入时默认已经使用该模块。你可能
需要使用 -S 开关在 Python 启动时关闭它。你也可以完成一些 site 相关的自定义操作,
例如在路径导入完成后在另个地方尝试。
5、zipimport - 你可以使用该模块导入 ZIP 归档文件中的模块。 需要注意的是该功能已经"
自动"开启, 所以你不需要在任何应用中使用它。在这里我们提出它只是作为参考。
6、distutils - 该模块提供了对建立、 安装、分发 Python 模块和包的支持。 它还可以帮助
建立使用 C/C++ 完成的 Python 扩展。 更多关于 distutils 的信息可以在 Python 文档
里找到, 参阅:
http://docs.python.org/dist/dist.html
http://docs.python.org/inst/inst.html

小总结:
sys.modules字典中导出所有已经加载的模块
sys.path的内置列表 导入时,Python会自动由左到右搜索这个列表中每个目录