充电时刻——模块
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
让你的模块可用
将模块放置在正确位置
将你的模块放置在正确位置(或者某个正确位置)是很容易的。只需要找出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语言写成的。
python基础教程_学习笔记12:充电时刻——模块