python 模块

如果你从Python解释器中退出,并且再次进入,你会发现你以前定义的函数和变量都已经丢失了。所以,如果你想写一个在某种程度上更长的程序,使用一个文本编辑器来准备解释器的输入会使情况有所好转,并且使用文件代替输入来执行它。这就是被熟知的创建一个脚本。随着你的程序变的更长时,你或许想把它分割成几个文件,这样便于维护。你或许想在几个程序里面使用一个常用的函数,而不用把它的定义拷贝到每一个程序里面。

为了支持这些,Python有一个方式来把定义放到一个文件里,并且在一个脚本里或解释器的一个交互实例中使用它们。这样的一个文件叫做模块,一个模块里面的定义可以被导入到其它模块里面或主模块里面(在顶级以计算器方式执行的脚本里面访问的变量集合)。

一个模块就是一个包含Python定义和语句的文件。文件名就是模块名加上后缀.py。在一个模块里面,模块的名字(一个字符串)可以使用全局变量__name__获得。例如,使用你喜爱的文本编辑器在当前的目录里面来创建一个fibo.py的文件,包含一些内容:

然后进入Python解释器,使用下面的命令导入这个模块:

这样做并没有把直接定义在fibo中的函数名称写入到当前符号表里,只是把模块fibo的名字写到了那里。

可以使用模块名称来访问函数:

如果你打算经常使用一个函数,你可以把它赋给一个本地的名称:

6.1 更多有关模块

模块可以包含可执行语句和函数定义。这些语句是打算用来初始化模块的。这些语句只有在模块第一次被导入到其它地方的时候会被执行。

每一个模块有它自己的私有符号表,它被定义在模块里的所有函数用作全局符号表。因此,一个模块的设计者可以在模块里使用全局变量而不用担心会和用户的全局变量产出意外的冲突。换句话来说,如果你知道你正在干什么,你可以触及模块里面的全局变量,使用和以前引用模块函数相同的标记法,modname.itemname。

模块可以引入其它模块。把所有的导入语句放到模块的开始是一种习惯,但不要求非得这样做。被导入模块的名称被放入到引入模块的全局符号表里。

有一个导入语句的变体可以把一个模块里的名称直接导入到另一个模块的符号表里面。例如:

这样不会引入模块的名称,这些导入的内容被放到本地符号表(所以在示例中,fibo没有定义)。

甚至由一个变体可以引入一个模块定义的所有名称:

这将引入所有除了以下划线开头的名称。大多数情况,Python程序员不使用这样的功能,因为它引入了一个不知道的名称集合到解释器里,可能会隐藏掉你已经定义的一些东西。

注意,一般来说,从一个模块或包里面引入所有的名称在实践中是不被赞成的,因为它经常造成可读性差的代码。然而,在交互式会话里可以使用它来节省输入。

注意,出于效率的原因,在一个会话里,每个模块只会被引入一次。如果你改变了你的模块,你必须重新启动解释器,或者如果你想交互测试的仅仅是一个模块,可以使用imp.reload(),例如:import imp; imp.reload(modulename)。

6.1.1 把模块作为脚本执行

当你以下面方式执行Python模块时:

模块里面的代码将会被执行,就好像你引入它一样,但是会把__name__设置为__main__。那就意味着把这些代码添加到模块的尾部:

你可以把文件当作脚本和可导入模块来使用,因为解析命令行的代码只有在模块作为主文件执行时才运行:

如果模块被导入,代码不执行:

这通常用来要么给模块提供一个方便的用户界面,或为了测试(把模块作为脚本运行,来执行一个测试单元)。

6.1.2 模块搜索路径

当一个名为spam的模块被导入时,解释器首先搜索具有这个名称的内建模块。如果没有找到,然后在sys.path这个变量指定的目录列表里面搜索一个名为spam.py的文件。sys.path从这些地方被初始化:

  • 包含输入脚本的目录(或当前目录)。
  • PYTHONPATH(一个目录名称的列表,和shell变量PATH的语法一样)。
  • 取决于安装时的默认值。

在初始化后,Python程序可以修改sys.path。包含正在运行的脚本的目录被放到了搜索路径的开始处,在标准库路径前面。这意味着那个目录里面的脚本会被加载而不是库目录里面的同名模块。这是一个错误除非替换被打算。

6.1.3 “编译”Python文件

作为一个对那些使用了许多标准模块的短的程序的启动时间的重要加速,如果在发现spam.py的目录里面已经存在一个叫spam.pyc的文件,这被假定包含模块spam的一个已按字节编译的版本。用来创建spam.pyc的spam.py的版本修改时间被记录在spam.pyc里面,并且.pyc文件会被忽略如果这两个时间不匹配。

通常,你不需要做任何事情来创建spam.pyc文件。无论什么时候spam.py文件被成功的编译,将会进行一次把编译好的版本写入到spam.pyc的尝试。如果这次尝试失败的话也不算是错误;如果由于任何原因这个文件没有被写完,结果spam.pyc文件被认为是非法的,并且在以后忽略它。spam.pyc文件的内容是平台独立的,所以一个Python模块目录可以被不同架构的机器共享。

一些对专家的提示:

  • 当Python的解释器以-O的标志被调用时,将产生优化的代码并存储在.pyo文件里面。优化器现在帮助不了太多;它仅仅移除assert语句。当-O使用时,所有的字节码都被优化;.pyc文件被忽略和.py文件被编译成优化的字节码。
  • 传递两个-O标志到Python的解释器(-OO)将使字节码编译器来执行优化,这种优化在一些特殊的情况下会导致程序出故障。当前只有文档字符串从字节码中被移除,产生更加压缩的.pyo文件。因为一些程序或许依赖使这些可用,你应该只有在你知道自己在做什么时再使用这个选项。
  • 一个程序从.pyc或.pyo文件读入并不比从.py文件读入运行的快很多;关于.pyc或.pyo文件惟一快的事情是它们被加载的速度。
  • 在命令行使用脚本名称运行脚本时,脚本的字节码从不写入.pyc或.pyo文件。因此,把一个脚本的大多数代码移到一个模块里面,产生一个较小的启动脚本并引入那个模块,可以减少一个脚本的启动时间。也可以在命令行直接命名一个.pyc或.pyo文件。
  • 在同一个模块里面有一个spam.pyc文件(或spam.pyo当-O使用时)而没有一个spam.py文件也是可能的。这可以用于以对逆向工程师有一定难度的形式来发布Python代码库。
  • compileall模块能为一个目录里面的所有模块创建.pyc文件(或.pyo文件当-O使用时)。

6.2 标准模块

Python有一个标准模块的库,在一个单独的文档中描述,Python库参考。一些模块被内建到解释器中;它们提供的访问操作不是语言的核心部分但是仍然被内建其中,要么是为了效率或提供访问操作系统原始的内容如系统调用。这些模块集是一个配置选项,它们也取决于底层的平台。例如,winreg模块只有Windows系统提供。一个特殊的模块应受到一些关注:sys,它被内建到每一个Python解释器中。变量sys.ps1和sys.ps2定义了用作主要和第二命令提示符的字符串:

这两个变量只定义在解释器处于交互模式时。

变量sys.path是一个字符串列表,决定了解释器的模块搜索路径。它使用环境变量PYTHONPATH的值进行初始化为一个默认的路径,或从一个内建默认值如果PYTHONPATH没有设置。你可以使用标准的列表操作修改它:

6.3 dir()函数

内建的函数dir()用来找出一个模块都定义了那些名称。它返回一个已排序的字符串列表:

没有参数的话,dir()列出当前你已经定义的名称:

注意,它列出所有类型的名称:变量,模块,函数等。

dir()并不列出内建的函数和变量的名称。如果你想列出那些名称,它们都定义在标准的模块buildins里面:

6.4 包

包是使用点模块名称来构建Python模块命名空间的一种方式。例如,模块名称A.B表明一个名称为B的子模块在一个名称为A的包里面。就像模块的使用使不同模块的作者不用再担心彼此的全局变量名称,点模块名称的使用使多模块包(像NumPy或Python镜像库)的作者不用再担心彼此的模块名称。

假定你想设计一些模块来统一的处理声音文件和声音数据。有许多不同格式的声音文件,所以或许你需要创建和维护一个持续增长的模块集合在多种不同格式的文件之间进行转换。或许也想在声音数据上执行多种不同的操作,所以除此之外你将写一个永远没有头的模块流来执行这些操作。这是一个可能的包结构:

当引入这个包,Python通过sys.path上的目录来搜索查找包子目录。

__init__.py文件是必须的,它使Python把这些目录作为包含包来对待;这样就阻止了稍后发生在模块搜索路径上的一个具有普通名称的目录无意中隐藏了合法模块。在最简单的情况下,__init__.py可能仅是一个空的文件,但是它也能为包执行初始化代码或设置__all__变量,稍后描述。

包的用户可以从包里面单个的导入模块,例如:

这加载子模块sound.effects.echo。必须使用全名来引用它:

一个导入子模块的可选方式是:

这也加载子模块echo,并且使它不带包前缀也可以使用,所以它能按如下方式使用:

另一种变体是用来直接导入期望的函数或变量:

加载子模块echo,并且使它的函数echofilter()直接可以使用:

注意,当使用from package import item时,item要么是包的子模块(或子包),或者是包里定义的一些其它名称,像函数,类或者变量。import语句首先测试item是否定义在包里;如果没有,就假定它是一个模块并且尝试去加载它。如果没有成功的找到它,一个ImportError异常被激发。

反之,当使用像import item.subitem.subsubitem这样的语法时,除了最后一项的其它项都必须是一个包;最后一项可以是一个模块或一个包,但不能是定义在前一项里面的一个类或函数或变量。

6.4.1 从一个包里导入*

当用户写下from sound.effects import *是会发生什么?理论上讲,一个人希望这以某种方式走出文件系统,找出哪些子模出现在块包里存,并且全部导入它们。这可能花费较长的时间,并且正在导入的子模块可能有不希望的副作用,这个副作用应该只有在这个子模块被显式导入时才发生。

唯一的解决方案就是包的作者提供一个显示的包的索引。import语句使用下面的约定:如果一个包的__init__.py代码定义了一个名为__all__的列表,它被认为是应该导入的模块名称的列表当遇到from package import *时。这取决于包的作者来保持这个列表是最新的当一个包的新的版本被发布时。包的作者们也可以决定不支持它,如果他们没有看到import * from他们的包的使用。例如,文件sounds/effects/__init__.py可能包含下面的代码:

这将意味着from sound.effects import *将导入sound包的三个命名的子模块。

如果__all__没有定义,语句from sound.effects import *并不从sound.effects包里导入所有的子模块到当前的命名空间里;它仅仅确认包sound.effects已经被导入(可能的运行__init__.py里面的任何初始化代码)并且导入包里定义的任何名称。这包含通过__init__.py定义的任何名称(和显式加载的子模块)。这也包括通过上一个import语句被显式加载的包的任何子模块。考虑下面的代码:

在这个例子里,当from...import语句被执行时,模块echo和surround被导入到当前的命名空间,因为它们定义在sound.effects包里。(当__all__被定义时这也起作用。)

当使用import *时,虽然遵从确定的模式,确定的模块被设计为只输出名称,在生产代码里它仍然被认为是坏的实践。

记住,使用from Package import specific_submodule没有错误。事实上,这是建议的写法,除非正在导入的模块需要使用来自不同包的具有相同名称的子模块。

6.4.2 内置包的引用

当包被结构化到子包里(就像例子中的sound包),你可以使用绝对的imports来引用兄弟包的模块。例如,如果模块sound.filters.vocoder需要使用包sound.effects里面的echo模块,可以使用from sound.effects import echo。

你也可以写相对的imports,使用import语句的from module import name形式。这些imports使用前导点(句点儿)来指示在相对import里面包含的当前的和父亲的包。从surround模块,你可以这样使用:

注意,相对imports是基于当前模块的名称。因为主模块的名称总是"__main__",打算用作Python应用程序的主模块的那些模块必须总是使用绝对imports。

多个目录里的包

包多支持一个特别的属性,__path__。它被初始化为一个包含包的__init__.py文件的目录的名称的列表在那个文件里的代码被执行之前。这个变量可以被改变,这样做影响将来对包里的模块和子包的搜索。

当然,这个特性也不常用,它可以被用于扩展一个包里的模块集合。

本文是对官方网站内容的翻译,原文地址:http://docs.python.org/3/tutorial/modules.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值