每次重进python的解释器我们上次定义的函数和变量都会消失,因此有些常用的信息我们希望能够一直保存。比较好的办法是将那些内容用文档编辑器保存起来(俗称的脚本),然后作为模块添加到解释器中。随着你编程时间的变长,你会想要把他们分成各个部分来方便保存,你也会希望一些常用的函数不要每次都重新定义。为了实现这个目的,用户可以把定义的内容整合到一个脚本中或者一个解释器实例中。我们把这样的整合内容称为模块,模块可以被其他模块调用也可以在解释器中直接调用。在保存文件模块的时候我们用.py作为后缀。在一个模块内,可以用全局变量__name__来调用模块的名字。首先我们用文本编辑器来保存一个fibo.py:
我用的是notepad++,属于比较好用的~然后我们把这个文件保存在python目录中就可以在解释器中引用他了:
如果你模块里有个函数你比较常用,可以直接把他赋值给本地变量(我个人不推荐这种做法,感觉容易搞混啊):
6.1 More on Modules
模块和函数一样可以包含一些执行代码,这部分代码的目的往往是为了初始化模块。但是他们只有当模块第一次被引用的时候才会执行,以后就不会了。每一个模块都一个局部变量的列表,当然这些局部变量对于模块内的函数来说就是全局变量。所以模块的作者使用全局变量的时候并不用担心和使用者的全局变量相互冲突。当然如果你真的想要修改模块的变量也是可以的,使用modname.itemname这种形式就可以调用了~
模块可以调用其他模块,通常来说(当然并不是强制要求)我们会把引入的模块全部标注在一个模块的最上端。引入的模块名称会存在进行引入的模块的全局变量列表中。引入模块的方法import有一些特殊的使用方法,下面我们来介绍一下:
使用这种方法引入,可以在使用函数式不需要再写模块的名称了;还可以直接将所有模块中的函数都这样引入:
在这种情况下会把所有的元素都引入,但是以下划线(_)开头的不会被引入。一般情况下我们是不会这么使用的,因为可能会引入一大堆我们不知道的东西,覆盖掉一些我们自己想要使用的元素。为了保证效率,所有模块只会被引入一次,也就是说在引入一个模块后你修改了模块的内容再下次重启解释器之前模块的内容并不会变。当让你可以强制他重新引入,使用importlib.reload()
6.1.1 Executing modules as scripts
当我们这么使用模块的时候:
python fibo.py <arguments>
在fibo模块内的代码会被执行,和我们引入他的效果是一样的。不过这样做会把变量__name__赋值为‘__main__’,这就是说如果我们在模块中加入判断语句,就可以将模块当作脚本那么用:
这样就保证只有将模块作为‘主要’文件执行时代码才会去解析我们想要它执行的那部分:
但是引入他的时候并不会去执行那部分代码,因为引入后模块的__name__变量等于模块的名字,而不是__main__。这一般使用做测试或者为用户提供一个方便的模块界面。
6.1.2 The Module Search Path
在引入一个叫做spam的模块时解释器首先会在内建模块中去寻找这个名字,如果找不到的话就在sys.path的列表储存的位置中寻找这个模块。sys.path一般是包含正在运行脚本的那个文件夹和PYTHONPATH还有一些在安装时默认的文件夹。在初始化之后是可以修改sys.path的。正在运行脚本的那个文件夹是排列最靠前的,也就是说如果在这个文件里有一个和标准库重名的模块,就会忽视标准库中的模块。这一点需要格外注意,起名一定要慎重啊~
6.2 Standard Modules
python有个标准模块库(Python Library Reference)。有些模块是内建在解释其中的,他们可以高效的操作系统。当然他们必然是基于平台的,比如winreg这个模块只有在windows平台上才可以用。值得注意的是sys模块,他在任何平台都是存在的,sys.ps1和sys.ps2是用来保存交互界面提示符的变量:
只有在交互模式才会定义这两个变量。另一个重点就是上面说过的sys.path这个列表,这里面保存的都是模块的查找路径,里面默认的内容是通过环境变量PYTHONPATH设置的,当然我们也可以向其中添加新变量:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
6.3 The dir( ) Function
内建函数dir()的作用就是看一个模块都定义了些什么,包括函数啊方法啊变量啊之类的东西。他的返回值是一个列表:
如果没有填入变量的话,就会返回当前模块(main)定义的内容:
注意dir()并不会显示内建的函数和变量之类的东西,如果你需要这些东西可以使用builtins:
6.4Packages
package可以将modules用.的结构来管理,比如我们有个模块叫做A.B就是说他是packageA中的B模块。就如同modules可以避免不同的作者之间的模块全部变量重名,package可以避免模块的重名。假设现在你需要把处理统一的声音文件数据的模块集合起来,这可能需要处理很多种类型的声音文件,所以你类型间的协议模块会不断增长;而且你会想要对声音文件进行不同的操作,你也会不停的增加执行操作的模块。我们来看一个可行的package结构:
sound/ Top-level package
__init__.py Initialize the sound package
formats/ Subpackage for file format conversions
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Subpackage for sound effects
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Subpackage for filters
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
在引入package时,python会在sys.path中搜索所有的package的子文件夹。__init__.py的作用是让python将文件夹按照包含package来处理,这么做的目的是为了防止有些模块被忽视,因为如果文件夹的名字起得比较常见和可以引入的模块重名了,就会导致模块不能被正确的引入。最简单的情况__init__.py可以就是一个简单的空文件,但他还是可以为package执行初始化代码,或者设置__all__变量,这个我们稍后再述~
当然我们也是可以直接在package中有选择的引入模块:
import sound.effects.echo
这样只会把echo模块引入,但是在使用的时候必须使用他的全名:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
还有另一种可选的引入模式:
from sound.effects import echo
其实引入的模块都是echo模块,但是在使用的时候就不需要使用全名,而是直接使用echo就可以了:
echo.echofilter(input, output, delay=0.7, atten=4)
同时也是可以只引入模块中的某些方法和函数的:
from sound.effects.echo import echofilter
使用的时候就可以直接写函数名就可以了
echofilter(input, output, delay=0.7, atten=4)
值得注意的是,使用from package import items这种格式,items可以是模块或者是其他的package或者是一些方法和函数;import声明会先在package中寻找item是否已经被定义了,如果没有的话就假设他是一个模块并试图去引入他,要是还找不到这个模块的话就会引起importErros。反之,如果使用import item.subitem.subsubitem的时候,最后引入的subsubitem必须是模块或是,其他的都必须是package。
6.4.1 Importing * From a Package
如果我们from sound.effects import * 会发生些什么哪?理想情况我们当然是希望它找到effecs的所有子模块然后引入他们。但是这肯能会花费很长时间并且有许多副作用,只有在所有子模块已经清楚的引入时才奏效。唯一的解决办法就是package的作者提供一个完整的子模块名单,import协议会检查__init__.py是否定义了__all__这个变量,这个变量里的内容就是from sound.effects import * 执行时会被引入的模块。作者一般会随着版本的更新会更新这个变量里的内容,但也可能作者根本就不打算定义这个变量,因为他们觉得不应该这样引用package里的内容。举个例子sound/effects/__init__.py可能就会含有这样的内容:
__all__ = ["echo", "surround", "reverse"]
这就保证在执行from sound.effects import * 时这三个模块会被引入。
如果__all__变量并没有被定义的话,那么from sound.effects import * 并不会把所有子模块都引入到当前的命名空间中;他只会保证sound.effects被引入,然后引入package内任意的被定义的内容,这包括__init__.py中的所有内容。当然已经被明确的引入的模块也会包含在内。我们看一下这个代码:
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
在这个例子中echo和surround被引入到当前的命名空间了因为他们已经在sound的package中定义了。虽然确定的模块会这样肯定会被import *引入,但这还是一个坏习惯尤其是在实际的生产代码中。使用from Package import specific_submodule是没有任何问题的,而且这也是推荐的使用方法。
6.4.2 Intra-package References
如果一个package中包含其他的package(就如同上面那个sound)那个例子,我们可以绝对位置的引入其中的内容。比如模块sound.filters.vocoder想要引入sound.effects中的echo模块,可以直接使用from sound.effects import echo。同时还有一种相对位置的引入方法,格式还是from module import name在这个时候使用.来代表当前模块的上一级package,比如我们在surround模块中可以使用如下引入方式:
from . import echo
from .. import formats
from ..filters import equalizer
需要注意的是相对位置的引入是基于当前模块的名称的,但是交互模式中不能这样使用,因为交互模式中当前模块的名字永远是__main__,所以只能使用绝对位置的引入。
6.4.3 Packages in Multiple Directories
package支持另外一个叫做__path__的属性,这个变量被初始化成一个列表来保存package中__init__文件的路径。这个变量是可以修改的,可以通过对他的修改来改变搜索module和package的位置。这个变脸不怎么常用到,主要是用来拓张package。