那些Python中的模块

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了那些Python中的模块相关的知识,希望对你有一定的参考价值。

参考技术A

Python的解释环境是很好用,但是如果我们需要编写一个大型的程序的时候,解释环境就完全不够用了。这个时候我们需要将python程序保存在一个文件里。通常这个文件是以.py结尾的。

对于大型的应用程序来说,一个文件可能是不够的,这个时候我们需要在文件中引用其他的文件,这样文件就叫做模块。

模块是一个包含Python定义和语句的文件。文件名就是模块名后跟文件后缀 .py 。在模块内部,模块名可以通过全局变量 __name__ 获得。

还是之前的斐波拉赫数列的例子,我们在fibo.py文件中存放了函数的实现:

编写完毕之后,我们可以在Python的解释环境中导入它:

然后直接使用即可:

常用的函数,我们可以将其赋值给一个变量:

或者,我们在导入的时候,直接给这个模块起个名字:

或者导入模块中的函数:

每个模块都有它自己的私有符号表,该表用作模块中定义的所有函数的全局符号表。因此,模块的作者可以在模块内使用全局变量,而不必担心与用户的全局变量发生意外冲突。

前面我们提到了可以使用import来导入一个模块,并且 __name__ 中保存的是模块的名字。

和java中的main方法一样,如果我们想要在模块中进行一些测试工作,有没有类似java中main方法的写法呢?

先看一个例子:

在模块中,我们需要进行一个判断 __name__ 是不是被赋值为 "__main__"。

我们这样来执行这个模块:

以脚本执行的情况下,模块的 __name__ 属性会被赋值为 __main__ , 这也是例子中为什么要这样写的原因。

看下执行效果:

如果是以模块导入的话,那么将不会被执行:

使用import导入模块的时候,解释器首先会去找该名字的内置模块,如果没找到的话,解释器会从 sys.path变量给出的目录列表里寻找。

sys.path的初始目录包括:

要想查看模块中定义的内容,可以使用dir函数。

上面的例子列出了当前模块中定义的内容,包括变量,模块,函数等。

我们可以给dir加上参数,来获取特定模块的内容:

java中有package的概念,用来隔离程序代码。同样的在Python中也有包。

我们看一个Python中包的例子:

上面我们定义了4个包,分别是sound,sound.formats, sound.effects, sound.filters。

__init__.py 可以是一个空文件,也可以执行包的初始化代码或设置 __all__ 变量。

当导入的时候, python就会在 sys.path 路径中搜索该包。

包的导入有很多种方式,我们可以导入单个模块:

但是这样导入之后,使用的时候必须加载全名:

如果不想加载全名,可以这样导入:

那么就可以这样使用了:

还可以直接导入模块中的方法:

然后这样使用:

如果一个包里面的子包比较多,我们可能会希望使用 * 来一次性导入:

那么如何去控制到底会导入effects的哪一个子包呢?

我们可以在 __init__.py 中定义一个名叫 __all__ 的列表,在这个列表中列出将要导出的子包名,如下所示:

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

如果没有定义 __all__,from sound.effects import * 语句 不会 从包 sound.effects 中导入所有子模块到当前命名空间;它只会导入包 sound.effects。

Import 可以指定相对路径,我们使用 . 来表示当前包, 使用 .. 来表示父包。

如下所示:



python中的模块

一、概论

模块支持从逻辑上组织python代码。

当代码量变得相当大的时候,我们最好把代码分成一些有组织的代码段,前提是保证它们的彼此交互。

把其他模块中属性附加到你的模块中的操作叫做导入(import),那些自我包含并且有组织的代码片段就是模块(module)。

 

如果说模块是按照逻辑来组织python代码的方法,那么文件便是物理层上组织模块的方法。

因此,一个文件被看作是一个独立的模块,一个模块也可以被看作是一个文件,模块的文件名就是模块的名字加上扩展名.py.

 

一个名称空间就是一个从名称到对象的关系映射集合,模块名称是它们的属性名称中一个重要的部分。

向名称空间添加名称的操作过程涉及绑定标识符到指定对象的操作(以及给该对象的引用计数加1)。

改变一个名字的绑定叫做重新绑定,删除一个名字叫做重新绑定。如果在执行期间调用了一个函数,那么将创建局部名称空间。

给定一个模块名之后,只可能有一个模块被导入到python解释器,所以在不同的模块间不会出现名称交叉的现象,故而每个模块都定义了它自己的唯一的名称空间。

 

模块可以包含可执行的语句和函数定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行。

import语句可以在程序中的任意位置使用的。

第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载到内存中的模块对象增加一次引用。所以当以重复导入的时候,并不会出现问题。

我们可以从sys.modules中找到当前已经加载的模块,sys.modules是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

 

首次导入模块my_module时会做三件事:

(1)为源文件(my_module模块)创建新的名称空间,在my_module中定义的函数和方法若是使用到了global时访问的就是这个名称空间。

(2)在新建的名称空间中执行模块中包含的代码,即初始导入import my_module。这个过程就是将模块中的函数名放入模块全局名称空间表。

(3)创建名字my_module来引用该命名空间

 

二、导入

1.import语句

使用import语句导入模块

>>> import os
>>> import math

也可以在一行之内导入多个模块,就像这样

>>> import os,time,random

下面这样导入,代码的可读性不如多行导入语句。

解释器执行完导入语句之后,如果在搜索路径找到了指定的模块,就会加载它。

该过程遵循作用域原则,如果在一个模块的顶层导入,那么它的作用域是全局的,如果在函数中导入,那么它的作用域是局部的。

如果模块是第一次被导入,它将被加载并执行。

 

2.from-import语句

from语句相当于import,也会创建新的名称空间,但是是将模块的名字直接导入到当前的名称空间中,直接使用名字就可以了

>>> from time import time,localtime
>>> time()
1510832882.9023538
>>> localtime()
time.struct_time(tm_year=2017, tm_mon=11, tm_mday=16, tm_hour=19, tm_min=48, tm_sec=7, tm_wday=3, tm_yday=320, tm_isdst=0)

还可以导入所有,就像这样

>>> listdir(/)
[boot, dev, proc, run, sys, etc, root, var, tmp, usr, bin, sbin, lib, lib64, home, media, mnt, opt, srv, backup, Python-3.6.3.tar.xz, Python-3.6.3, script, test, zuoye]
>>> getcwd()
/root

from my_module import *把my_module中所有的不是下划线(_)开头的名字都会导入到当前位置,这不是一个好的选择。

它会“污染”当前名称空间,而且很有可能覆盖当前名称空间中现有的名字。

在两种场合下建议使用这样的方法,一个场合是:目标模块的属性非常多,反复键入模块名很不方便;

还有一个场合就是在交互式解释器下,因为这样可以减少输入次数。

 

3.扩展的import语句(as)

有时候你导入的模块或是模块属性名称已经在你的程序中使用了,或者你不想使用导入的名字,你想使用自己想要的名字替换掉模块的原始名称,这时你可以使用as。

>>> import random as ra
>>> ra.randint(10,100)
30

使用场景:

(1)模块的名称太长

(2)名称空间有冲突

(3)当兼容多个模块的相同操作的时候。

 

三、编译

1.模块的搜索路径

python解释器在启动时会自动加载一些模块,可以使用sys.modules查看。

>>> import sys
>>> sys.modules
{builtins: <module builtins (built-in)>, sys: <module sys (built-in)>, _frozen_importlib: <module _frozen_importlib (frozen)>, _imp: <module _imp (built-in)>, _warnings: <module _warnings (built-in)>, _thread:.....

在第一次导入摸个模块的时候(比如my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有,直接引用。

如果没有,解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中一次寻找my_module.py文件。

模块查找的顺序:内存中已经加载的模块——》内置模块——》扩展模块——》自定义模块。

在初始化后,python程序可以修改sys.path路径,路径放在前面的优先加载

 

2.编译python文件

为了提高加载模块的速度,提高的是加载速度而绝非运行速度。

python解释器会在__pycache__目录下缓存每个模块编译后的版本格式为:module.version.pyc。

python检查源文件的修改时间与编译时间的版本进行对比,如果过期就需要重新编译。

这是完全自动的过程。并且编译的模块是平台独立的,所以相同的库可以在不同的架构的系统之间共享,即pyc是一种跨平台的字节码。

python解释器会在一下两种情况下不检查缓存。

1.如果是在命令行中被直接导入模块,则按照这种方式,每次导入都会重新编译,并且不会存储编译后的结果(python3.3以前)。

2.如果源文件不存在,那么缓存的结果也不会被使用,如果想在没有源文件的情况下来使用编译后的结果,则编译后的结果必须在源目录之下。

 

注意事项:

(1)模块名区分大小写,foo.py与FOO.py代表的是两个模块。

(2)你可以使用-O或者--OO转换python命令来减少编译模块的大小。

(3)在速度上从.pyc文件中读取指令来执行不会比.py文件中读指令执行更快,只有在模块被夹在时,.pyc文件才是更快的。

(4)只有使用import语句才会将文件自动编译为.pyc文件,在命令行或标准输入中指定运行脚本则不会生成这类文件,因而我们可以使用compieall模块为一个目录中的所有模块创建.pyc文件。

(5)如果在模块中使用__name__这个变量,则在调用的时候要指定名字。

 

以上是关于那些Python中的模块的主要内容,如果未能解决你的问题,请参考以下文章

python中的模块

python 中的匿名函数lamda和functools模块

pandas python 怎么删除表格中的某一行

盘点 Python 中的那些冷知识

python中的那些“神器”

Python中的内置函数