简介
现在你已经知道通过定义函数可以在你的程序中复用代码。但当你想在你编写的其他程序中复用大量函数怎么办呢?
也许你可以猜到了,办法就是利用模块。
有各种编写模块的方式,但最简单的方式是创建一个以.py为后缀的文件并包含所需的函数与变量。
另一种方式是以编写python解释器的本地语言编写模块。
例如C语言编写的模块被编译后可供运行于标准python解释器上的python代码使用。
模块可以被其它程序导入以使用其提供的功能。这也是为什么我们可以使用python标准库。
我们先来看看如何使用标准库模块。
范例:
#!/usr/bin/python
# Filename: using_sys.py
import sys
print('The command line arguments are:')
for i in sys.argv:
print(i)
print('/n/nThe PYTHONPATH is', sys.path, '/n')
输出:
$ python using_sys.py we are arguments
The command line arguments are:
using_sys.py
we
are
arguments
The PYTHONPATH is ['', 'C://Windows//system32//python30.zip',
'C://Python30//DLLs', 'C://Python30//lib',
'C://Python30//lib//plat-win', 'C://Python30',
'C://Python30//lib//site-packages']
工作流程:
首先我们使用import语句导入sys模块。本质上这告诉python我们希望使用这个模块。
sys模块包含python解释器与其工作环境(即系统)相关的功能。
当python执行import sys语句时,它将查找sys模块。本例中sys是内建模块之一,因此python知道在哪能找到它。
如果导入的不是一个编译模块,即不是用python编写的模块,python解释器会在变量sys.path中列出的目录中查找它。
(注:if it was not a compiled module i.e. a module written in Python)
如果模块被找到,这个模块中的语句将被执行然后你就可以使用它了(注: 只有顶级语句才会执行,也就是主块中的语句)。
注意一个模块只有在第一次导入时会被初始化。
sys模块中的argv通过点号引用即sys.argv。它清晰的指出这个名字是sys模块中的一部分。
这种语法的另一个优势是不会和你的程序中的同名argv变量发生冲突。
变量sys.argv是一个字符串列表(后章会详细解释列表)。
具体说sys.argv是一个包含命令行参数的列表,也就是使用命令行传递给你的程序的参数。
如果你在使用IDE编写程序,请在菜单中查找为程序指定命令行参数的方法。
这里,当我们执行python using_sys.py we are arguments时,我们以python命令运行using_sys.py模块,其后的内容是传递给程序的参数。
python将它们存到sys.argv以供我们使用。
记住,被运行脚本的脚本名永远是sys.argv的第一个参数。
所以本例中sys.argv[0]为’using_sys.py’,sys.argv[1]为’we’,sys.argv[2]是’are’, argv[3]是’arguments’。注意python下标从0开始而非1。
sys.path包含一个目录名列表指示从哪里导入模块。
观察程序输出,sys.path的第一个字符串为空 – 其指出当前目录也是sys.path的一部分,这与PYTHONPATH环境变量是相同的。
这意味着你可以直接导入当前目录下的模块,否则你就必须将你的模块放到sys.path列出的目录中的一个了。
注意程序在哪个目录运行的,这个目录就是这个程序的当前目录。运行import os; print(os.getcwd()) 可以看到你的程序的当前目录。
(注:windows下sys.path[0]可能不为空,而是显式指出当前路径)
字节编译文件 .pyc
导入模块是一个相对昂贵的操作,所以python使用了一些技巧加速这个过程。
一个办法是创建后缀为.pyc的字节编译文件用于将程序转换为中间格式。(还记得介绍python如何工作的那一节吗?)
当你下次从其他文件导入模块时pyc文件会非常有用 – 它将大大增加导入速度,因为导入模块的部分操作已经预先完成了。
并且这个字节编译文件仍然是平台无关的。
注意
.pyc文件一般被创建在与其对应的.py文件所在的相同目录下。如果python没有这个目录的写权限,则.pyc文件不会被创建。
from…import…语句
如果你希望将变量argv直接导入到你的程序中(避免每次输入sys.),那么可以使用from sys import argv语句。
如果希望导入sys模块中的所有名字,则from sys import *可以做到。此语句可以用于任何模块。
通常你应该避免使用这个语句并用import语句代替之,因为使用后者可以避免名字冲突,程序的可读性也更好。
模块的__name__属性
每个模块都有一个名字,并且通过模块中的某些语句可以得到这个模块名。
在一些想要搞清模块是独立运行还是被导入的情况下,这会非常方便。
如前所述当模块第一次被导入时模块中的代码会被执行。我们可以据此改变模块独立执行时的行为方式。
这可以通过模块的__name__属性做到。(注:独立运行是指程序最开始运行的那个脚本文件(/模块))
范例:
#!/usr/bin/python
# Filename: using_name.py
if __name__ == '__main__':
print('This program is being run by itself')
else:
print('I am being imported from another module')
输出:
$ python using_name.py
This program is being run by itself
$ python
>>> import using_name
I am being imported from another module
>>>
工作流程:
每个python模块都有自己的__name__定义,如果它是’__main__’则暗示模块为独立运行,我们可以进行一些适当的处理。
制作你自己的模块
创建你自己的模块很简单,其实你一直在这样做!因为每个python脚本都是一个模块。你只需确保它带有.py扩展名即可。
下面的例子会让你对其有一个清晰的认识:
范例:
#!/usr/bin/python
# Filename: mymodule.py
def sayhi():
print('Hi, this is mymodule speaking.')
__version__ = '0.1'
# 结束mymodule.py编写
上面就是一个简单的模块,如你所见,这和我们平时的python程序相比没有什么特别之处。
记住模块应该放到导入它的那个程序所在的目录下,或者放到sys.path列出的目录之一中。
#!/usr/bin/python
# Filename: mymodule_demo.py
import mymodule
mymodule.sayhi()
print ('Version', mymodule.__version__)
输出:
$ python mymodule_demo.py
Hi, this is mymodule speaking.
Version 0.1
如何工作:
注意我们同样使用点号访问模块成员。
python很好的重复利用了相同的符号,带来独特的’Pythonic’感受,这样我们就不必学习更多的语法知识了。
下面是一个使用from…import语法的版本:
#!/usr/bin/python
# Filename: mymodule_demo2.py
from mymodule import sayhi, __version__
sayhi()
print('Version', __version__)Python en:Modules 59
mymodule_demo2.py与mymodule_demo的输出完全相同。
注意,如果导入mymodule的模块中已经存在同名的__version__,则将发生名字冲突。
事实上这很可能发生,因为每个模块都用__version__声明它的版本是一种常见的做法。
因此建议你优先考虑import语句,虽然它可能会让你的程序变的更长一些。
你同样可以使用:
from mymodule import *
这将导入模块的所有公有名字,例如sayhi,但是不会导入__version__因为它以双下划线开头。
Python之禅
python的一个指导原则是”清晰的好过隐晦的”. 执行import this可以看到完整内容。
这里的讨论列出了每个原则的范例(http://stackoverflow.com/questions/228181/zen-of-python)
dir函数
你可以使用dir函数列出一个对象定义的所有标识符。例如对于一个模块,标识符包括函数,类,变量。
当你为dir()函数提供一个模块名,它将返回定义在其中的所有名字。
当dir()的参数为空时,返回定义在当前模块中所有名字。
范例:
$ python
>>> import sys # 得到sys模块的属性列表
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__',
'__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache',
'_compact_freelists', '_current_frames', '_getframe', 'api_version', 'argv',
'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook',
'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', executable',
'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getfil
esystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof',
'gettrace', 'getwindowsversion', 'hexversion', 'intern', 'maxsize', 'maxunicode',
'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform',
'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit',
'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']
>>> dir() # 得到当前模块的属性列表
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> a = 5 # create a new variable 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']
>>> del a # 删除名字a
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
工作流程:
首先,我们通过被导入的sys模块应用dir函数. 可以看到sys包含巨多的属性。
接下来,我们不为dir函数提供参数。默认的它返回当前模块的属性列表。注意被导入模块列表也是当前模块列表的一部分。
为了观察到dir确定起作用了,我们定义一个新变量a并为其赋值然后检验dir的返回值,我们发现在返回的列表中确实出现
了一个与变量a同名的值。在我们使用del语句删除当前模块的变量/属性后,改变再次反映到了dir函数的输出上。
del注解 – 这个语句用于删除一个变量/名字,在本例中,del a之后你就无法访问变量a了 – 就像它从来没有存在过一样。
注意dir()函数可用于任何对象。例如执行dir (print)学习更多关于print函数的属性,或是dir(str)列出str类的属性。
包
如今,你必须开始留心组织你的程序层次了。
变量在函数内部,函数和全局变量通常在模块内部。那么如何组织模块呢?这就轮到包登场了。
包仅仅是包含模块的文件夹,并带有一个特殊的文件__init__.py用于指示python这个文件夹是特殊的,因为它包含python模块。
让我们假设你需要创建一个叫做’world’的包,里面包括诸如’asia’,‘africa’等的子包。
下面告诉你应该如何组织文件夹结构:
- <some folder present in the sys.path>/
- world/
- __init__.py
- asia/
- __init__.py
- india/
- __init__.py
- foo.py
- africa/
- __init__.py
- madagascar/
- __init__.py
- bar.py
包只是用来有层次的组织模块。你会在标准库中看到它的很多应用。
小结
就象函数是程序的可复用部分一样,模块是可复用的程序。
包用于组织模块的层次结构。python自带的标准库正是一组包和模块的范例。
我们已经看到如何使用这些模块,并且知道如何创建自己的模块。
接下来,我们将学习一个比较有趣的概念 – 数据结构。