那些Python中的模块
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了那些Python中的模块相关的知识,希望对你有一定的参考价值。
参考技术APython的解释环境是很好用,但是如果我们需要编写一个大型的程序的时候,解释环境就完全不够用了。这个时候我们需要将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中的模块的主要内容,如果未能解决你的问题,请参考以下文章