充电时刻——模块
python的标准安装包含一组模块,称为标准库。
模块
>>> import math
>>> math.sin(0)
0.0
模块是程序
不论什么python程序都能够作为模块导入。
$ cat hello.py
#!/usr/bin/python
print "Hello,signjing!"
$ ./hello.py
Hello,signjing!
如果将python程序保存在/home/ggz2/magiccube/mysh/pys文件夹中,运行以下的代码:
>>> import sys
>>> sys.path.append('/home/ggz2/magiccube/mysh/pys')
这里所做的仅仅是告诉解释器:除了从默认的文件夹中寻找之外,还须要从文件夹/home/ggz2/magiccube/mysh/pys中寻找模块。
完毕这个步骤后,就能导入自己的模块了:
>>> import hello
Hello,signjing!
注意:在导入模块的时候。可能会看到有新文件出现,本例中是/home/ggz2/magiccube/mysh/pys/hello.pyc。这个以.pyc为扩展名的文件是(平台无关的)经过处理(编译)的,已经转换成python可以更加有效地处理的文件。假设稍后导入同一个模块,python会导入.pyc文件而不是.py文件。除非.py文件已经改变——在这样的情况下,会生成新的.pyc文件。
删除.pyc文件不会损害程序(仅仅要等效的.py文件存在就可以)——必要的时候会创建新的.pyc文件。
如你所见。在导入模块的时候。当中的代码就被运行了。只是再次导入该模块。就不会发生什么了。
>>> import hello
>>>
由于导入模块并不意味着在导入时运行某些操作。
它们主要用于定义。
此外,由于仅仅须要定义这些东西一次,导入模块多次和导入一次的效果是一样的。
模块用于定义
模块在第一次导入程序中时被运行。
这看起来有点用——但并不算非常实用。真正的用处在于它们(像类一样)能够保持自己的作用域。这就意味着定义的全部类和函数以及赋值后的变量都成为了模块的特性。
在模块中定义函数
$ cat hello2.py
#!/usr/bin/python
def hello():
print "morning,signjing"
>>> import hello2
>>> hello2.hello()
morning,signjing
能够通过相同的方法来使用不论什么在模块的全局作用域中定义的名称。
为了让代码可重用,请将它模块化!
在模块中添加測试代码
模块用来定义函数、类和其它一些内容。但有些时候(其实是常常)。在模块中加入一些检查模块本身是否正常工作的測试代码是非常实用的。
$ cat hello3.py
#!/usr/bin/python
def hello():
print "Hello!"
# a test
hello()
>>> import hello3
Hello!
>>> hello3.hello()
Hello!
避免这样的情况的关键在于:“告知”模块本身是作为程序执行还是导入到其它程序。为了实现这一点。须要使用__name__(双下划线)变量:
>>> __name__
'__main__'
>>> hello3.__name__
'hello3'
可见。在主程序(包含解释器的交互式提示符在内)中。变量__name__的值是’__main__’。而在导入的模块中。这个值被设定为模块的名字。因此。为了让模块的測试代码更加好用,能够将其放置在if语句中:
$ cat hello4.py
#!/usr/bin/python
def hello():
print "hello"
def test():
hello()
if __name__=='__main__':test()
>>> import hello4
>>>
>>> hello4.hello()
hello
让你的模块可用
将模块放置在正确位置
将你的模块放置在正确位置(或者某个正确位置)是非常easy的。仅仅须要找出python解释器从哪里查找模块。然后将自己的文件放置在那里就可以。
linux系统:
>>> import sys,pprint
>>> pprint.pprint(sys.path)
['',
'/usr/lib64/python26.zip',
'/usr/lib64/python2.6',
'/usr/lib64/python2.6/plat-linux2',
'/usr/lib64/python2.6/lib-tk',
'/usr/lib64/python2.6/lib-old',
'/usr/lib64/python2.6/lib-dynload',
'/usr/lib64/python2.6/site-packages',
'/usr/lib64/python2.6/site-packages/gtk-2.0',
'/usr/lib/python2.6/site-packages']
windows系统:
>>> import sys,pprint
>>> pprint.pprint(sys.path)
['',
'D:\\software(x86)\\Python27\\Lib\\idlelib',
'C:\\Windows\\system32\\python27.zip',
'D:\\software(x86)\\Python27\\DLLs',
'D:\\software(x86)\\Python27\\lib',
'D:\\software(x86)\\Python27\\lib\\plat-win',
'D:\\software(x86)\\Python27\\lib\\lib-tk',
'D:\\software(x86)\\Python27',
'D:\\software(x86)\\Python27\\lib\\site-packages']
每一个字符串都提供了一个放置模块的文件夹。解释器能够从这些文件夹中找到所需的模块。虽然这些文件夹都能够使用。但site-packages文件夹是最佳选择,由于它就是用来做这些事情的。
在windows操作系统中的D:\\software(x86)\\Python27\\lib\\site-packages下创建文件another_hello.py:
def hello():
print "hello,Win7"
>>> import another_hello
>>> another_hello.hello()
hello,Win7
可见。仅仅要将模块放入类似site-packages这种文件夹中,全部程序就都能将其导入了。
告诉编译器去哪里找
“将模块放置在正确的位置”这个解决方式在下面几种情况下可能并不适用:
1)不希望将自己的模块填满python解释器的文件夹;
2)没有python解释器文件夹中存储文件的权限。
3)想将模块放在其它地方
既然如此,那就告诉解释器去哪里找。之前提到了sys.path。但这不是通用的方法。标准的实现方法是在PYTHONPATH环境变量中包括模块所在的文件夹。
PYTHONPATH环境变量的内容会由于使用的操作系统不同而有所差异,但从基本上来说。它与sys.path非常类似——一个文件夹列表。
环境变量并非python解释器的一部分。它们是操作系统的一部分。
提示:不须要使用PYTHONPATH来更改sys.path。
路径配置文件提供了一个实用的捷径。能够让python替你完毕这些工作。路径配置文件是以.pth为扩展名的文件,包含应该加入到sys.path中的文件夹信息。空行和以#开头的行都会被忽略。
以import开头的文件会被运行。对于Windows来说,使用sys.prefix定义的文件夹名;在unix和mac OSX中则使用site-packages文件夹。
命名模块
包括模块代码的文件要和模块名一样——再加上.py扩展名。
在windows系统中,也能够使用.pyw扩展名。
包
为了组织好模块,能够将它们分组为包。包基本上就是另外一类模块。
有趣的地方就是它们都能包括其它模块。
当模块存储在文件里时(扩展名为.py),包就是模块所在的文件夹。为了让python将其作为包对待。它必须包括一个命名为__init__py的文件(模块)。假设将它作为普通模块导入的话,文件的内容就是包的内容。比方有个名为constants的包,文件constants/__init__.py包括语句PI=3.14,那么能够这么做:
import constants
print constants.PI
为了将模块放置在包内。直接把模块放在包文件夹内就可以。
探究模块
怎样独立地探究模块,是极有价值的技能。由于职业生涯中可能会遇到非常多实用的模块。
模块中有什么
探究模块最直接的方式就是在python解释器中研究它们。
第一件事就是导入它。
如果有个叫做copy的标准模块:
>>> import copy
使用dir
查看模块中包括的内容能够使用dir函数,会将对象(以及模块的全部函数、类、变量等)的全部特性列出。
>>> dir(copy)
['Error', 'PyStringMap', '_EmptyClass', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_copy_dispatch', '_copy_immutable', '_copy_inst', '_copy_with_constructor', '_copy_with_copy_method', '_deepcopy_atomic', '_deepcopy_dict', '_deepcopy_dispatch', '_deepcopy_inst', '_deepcopy_list', '_deepcopy_method', '_deepcopy_tuple', '_keep_alive', '_reconstruct', '_test', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']
一些名字下面划线開始——暗示(约定俗成)它们并非为在模块外部使用而准备的。过滤到它们:
>>> [n for n in dir(copy) if not n.startswith('_')]
['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref']
all变量
__all__这个名字包括一个列表,在copy模块内部被设置。
>>> copy.__all__
['Error', 'copy', 'deepcopy']
它定义了模块的公共接口。
更准确地说,它告诉解释器:从模块导入全部名字代表什么含义。
使用例如以下代码from copy import *。则仅仅能使用__all__变量中的函数。要导入PyStringMap的话,就得显式地实现,或者导入copy然后使用copy.PyStringMap,或者使用from copy import PyStringMap。
在编写模块的时候,像设置__all__这种技术还是相当实用的。
由于模块中可能会有一大堆其它程序不须要或不想要的变量、函数和类,__all__会“客气地”将它们过滤了出去。
假设没有设定__all__,用import *语句默认将会输出模块中全部不下面划线开头的全局名称。
用help获取帮助
对于探究工作。交互式解释器是个很强大的工具。而对该语言的精通程度决定了对模块探究的程度。只是还有个标准函数可以为你提供日常所需的信息,这个函数叫做help。
>>> help(copy.copy)
Help on function copy in module copy:
copy(x)
Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
>>>
其实。上面的帮助文档是从copy函数的文档字符串中提取出的:
>>> print copy.copy.__doc__
Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
>>>
使用help与直接检查文档字符串相比。优点在于获得很多其它信息。
help(copy)会打印出很多其它信息,此处略;
文档
并不是每一个模块或函数都有不错的文档字符串(虽然都应该有)。有些时候可能须要十分透彻地描写叙述这些模块和函数是怎样工作的。
学习python编程最实用的文档莫过于python库參考。它对全部标准库中的模块都有描写叙述。
使用源码
对于希望真正理解python语言的人来说。要了解模块,是不能脱离源码的。
阅读源码其实是学习python最好的方式——除了自己编写代码外。
真正的阅读不是问题,可是问题在于源码那里。假设希望阅读标准模块copy的源码。一种方案是检查sys.path。然后自己找。还有一种是检查模块的__file__属性:
>>> copy.__file__
'D:\\software(x86)\\Python27\\lib\\copy.pyc'
注意:一些模块并不包括不论什么能够阅读的python源码。它们可能已经融入到解释器内了(比方sys模块),或者可能是使用c语言写成的。