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__这个变量,则在调用的时候要指定名字。

 

posted @ 2017-11-26 03:37  明王不动心  阅读(261)  评论(0编辑  收藏  举报