Python 学习 第九篇:模块

模块是把程序代码和数据封装的Python文件,也就是说,每个以扩展名py结尾的Python源代码文件都是一个模块。每个模块文件就是一个独立的命名空间,用于封装顶层变量名;在一个模块文件的顶层定义的全部的变量名(函数名也是一个变量名),称做模块的属性。导入模块给予了对模块的全局做用域中的变量名的读取权,也就是说,在模块导入时,模块文件的全局做用域变成了模块内对象的命名空间。app

导入一个模块以后,可使用模块中定义的属性,例如,在模块moduleA中导入一个模块moduleB,那么moduleB就变成了命名空间,在moduleA中可使用moduleB中的全局变量,引用的格式是:moduleB.attribution_name。编辑器

归纳来讲,Python中的模块拥有三个大的做用:函数

  • 代码重用:模块用于保存代码,按照须要导入或从新导入模块,以使用模块中的代码。
  • 变量封装:模块把变量名封装起来,模块变成了一个独立的命名空间,模块封装的变量名变成了属性,这就避免了变量名的冲突。
  • 代码共享:把通用的代码组织成模块,分享给他人。

一,Python程序的结构

通常来讲,一个Python程序结构是由一个主文件(又称为顶层文件、脚本)和多个模块构成的,其中主文件包含了程序的控制流程,也就是启动以后可以运行整个程序的文件,而模块文件是用于提供工具的库。工具

在Python中,一个文件导入了一个模块来得到该模块内定义的工具的访问权,所以,通用的Python程序的结构就是:在顶层文件中导入模块,并引用模块中的工具。spa

例如,在脚本文件main.py中导入functools模块中的reduce()函数,使用该工具函数实现列表元素的加和:命令行

from functools import reduce a=reduce((lambda x,y:x+y),range(0,5)) print(a) 

二,模块的建立

定义模块,只须要使用文本编辑器,把一些Python代码输入到文本文件中,而后以".py"为扩展名名进行保存,任何此类文件都会被自动认为是Python模块。在模块顶层指定的变量名是模块的属性。注意:模块的文件名必须以扩展名 .py 结尾。code

例如,建立一个strtools模块,文件名必须是strtools.py,文件的内容是Python代码,其中append_suffix是模块的属性名,引用该属性的格式是:strtools.append_suffix(arg):orm

def append_suffix(str): return str+'- py'

三,导入模块

导入从本质上来说,就是为了载入另外一个文件,并可以读取该文件的内容。模块的导入是由两个语句来处理:对象

  • import语句:以一个总体导入一个模块;
  • from语句:从一个模块文件中导入特定的变量名;

import语句和from语句都会加载模块,加载的过程包括搜索、编译和执行模块文件。二者的差异在于:import语句 会读取整个模块,因此必须进行定义后才能读取它的属性;form语句直接获取模块中特定的变量名。blog

在一个导入语句中的模块名起到两个做用:识别加载的外部文件;把模块名做为命名空间,用于封装模块内的顶层变量名。在导入模块时,模块定义的对象也会在import语句执行时建立,经过module_name.attribution_name来引用模块内定义的变量名。

例如,在主文件中导入strtools模块,并使用append_suffix()函数:

import strtools mystr=strtools.append_suffix('It is a cat') print(mystr)

1,import语句

import语句使用一个变量名来引用整个模块对象,所以,必须经过模块名称来得到模块的属性:

import strtools strool.append_suffix('xx')

2,from语句

from语句把变量名复制到另外一个做用域,所以,能够直接在脚本中使用变量名,而不须要经过模块:

from strtools import append_suffix append_suffix('xx')

因为模块中的变量名可能由不少,可使用*,取得模块顶层的全部变量名,把模块顶层的变量名都复制到当前的做用域内:

from strtools import *

从技术上来讲,import和from * 语句都会使用相同的导入操做,from *  形式只是多加了步骤,把模块中全部的属性名复制到了导入的做用域以内。

3,导入只发生依次

模块会在第一次import或from时载入并执行,而且只在第一次如此。默认状况下,Python只对每一个文件作一次导入操做,以后的导入操做操做都只是取出已加载的模块对象。

4,import和from是赋值语句

import和from是可执行的语句,而不是声明语句,也就是说,被导入的模块和变量名,直到它们所对应的import或from语句执行后,才可使用。

import和from是隐式的赋值语句:

  • import把整个模块对象赋值给一个变量名,模块文件的名称就是变量名;
  • form把一个或多个变量名称赋值给另外一个模块中同名的变量名;以from语句复制的变量名会变成对共享对象的引用。

以from语句复制而来的变量名和其来源的文件之间没有联系,为了实际修改另外一个文件中的全局变量名,必须使用import语句。

例如,使用from导入变量x,修改变量不会影响模块中x的值,这是由于from语句把模块的对象复制到本地做用域的变量,因此,本地做用域对变量从新赋值,会建立一个新的对象。

from module import x,y x=1   # change the variable

可是,对可变类型对象的修改,会影响模块的对象的值,这是由于list_names支持原处修改,这跟赋值不一样:

from module import list_names list_names[0]='vic'

5,import和from的对等性

from只是把变量名从一个模块复制到另外一个模块,并不会对模块名自己进行赋值。

从概念上来说,一个像这样的from语句:

from module import name1,name2

与下面这些语句是等效的:

import module name1=module.name1 name2=module.name2 del module

就像全部的赋值语句,from语句会在导入者中建立新变量,而这些变量初始化时引用了被导入文件中的同名对象。不过,只有同名的变量名(并非全部的变量名)被赋值出来,而非模块自己,from未指定的变量名都没有复制出来。

三,模块命名空间

从上文可知,模块是变量名的封装。Python会把每个.py文件看做一个模块,建立模块对象,以包含模块内所复制的全部顶层变量名,这些顶层变量名就是模块对象的属性。

1,模块语句会在首次导入时执行

模块在第一次导入时,Python都会创建空的模块对象,按照文件从头至尾的顺序,逐一执行模块内的语句。

2,顶层的赋值语句会建立为模块属性

在导入时,在模块顶层的赋值语句左侧的变量,成为模块对象的属性,赋值的变量名会存在模块的命名空间内。

3,模块的命名空间可以经过属性__dict__或dir(module)函数获取

在导入时,Python会把创建模块的命名空间的字典,经过模块对象的属性__dict__来读取,也能够经过dir(module)函数来读取。

4,模块是一个独立的做用域

模块内的命名空间是一个独立的做用域,经过点号 (  .  )来引用属性,引用的格式是:object.attr_name。

四,重载模块

从上文可知,导入只发生一次,要想重载模块,须要 调用 reload()函数。

  • 导入(不管是经过import或from语句)只会在模块第一次导入时,加载和执行模块的代码;
  • 以后的导入只会使用已加载的模块对象,不会从新执行模块的代码;
  • imp.reload()函数在不停止Python程序的状况下,强制Python从新加载和执行模块的代码。

 注意,在使用reload()函数重载模块以前,模块必定是已经预先导入了。

通常的用法是:导入一个模块,在文本编辑器中修改其源代码,而后将其重载。

import module ... use module.attributes ... # now, go change the module file

from imp import reload reload(module) # get the changes
... use module.attributes

当调用reload()函数时,Python会从新读取模块问价的源代码,从新执行其顶层代码,在适当的地方修改已导入的模块对象,reload()不会删除并重建模块对象。

在调用reload()函数以后,程序中任何引用该模块的对象,自动会受到重载的影响。

1,重载模块会致使模块代码的从新执行

reload()函数会使模块从新执行模块文件的新代码,从新执行模块文件的代码,会覆盖现有的命名空间,但不是把模块对象删除并进行重建。

2,模块文件中顶层的赋值语句会使得变量名从新赋值

在调用reload()函数时,赋值语句会从新执行,进而模块的顶层变量会被从新赋值,这会致使顶层变量引用的对象会从新建立。

例如,模块重载会致使顶层的def语句从新执行,对函数名进行从新赋值,这使得函数对象会被从新建立,函数名引用的对象不是模块以前的版本。

3,重载对import语句的影响

模块重载会影响全部使用import语句读取了模块的脚本,这是由于使用import语句的脚本,须要经过点号运算获取属性,在模块重载后,模块的属性变成了新值。

4,重载对from语句的影响

模块重载只会对之后使用from语句的脚本形成影响,以前使用from语句来读取属性的变量名不会受到重载的影响,其引用的是值依然是重载以前所获取的对象。

这是由于,模块重载会致使模块顶层变量的从新赋值,这使得模块顶层变量引用的对象会从新建立,模块顶层变量引用的是新对象,而在重载以前执行的from语句,变量引用的是旧版本的对象,这个旧版本的对象不会被销毁,但和重载以后的新对象不是同一个对象了。

5,重载不会传递到导入的模块

重载不会影响模块已经导入的模块,例如,若是要重载模块A,而且模块A导入模块B和C,那么重载只适用于A,而不适用于B和C。在重载模块A时,模块B和C因为已经加载了,模块A只会获取已经载入的模块B和C对象。

若是要重载已经导入的模块,那么必须显式重载这些模块:

from imp import reload # firstly reload imported modules
reload(C) reload(B) # then reload the outer module
reload(A)

五,模块的其余主题

在这一节中,咱们探索模块的使用标志,模块内变量的前向引用,和点号运算符的特性等。

1,使用模式的标志

每一个模块都有个名为__name__的内置属性,Python会自动设置该属性:

  • 若是文件是以主程序文件执行的额,在启动时,__name__就会设置为字符串"__main__"
  • 若是文件被导入,__name__就会设置模块名

 模块能够检测本身的__name__属性,以肯定本身是在执行(run)仍是导入(import)。

2,命令行参数

在Python中,sys.argv列表包含了命令行参数,它是反映在命令行上录入的单词的一个字符串列表,其中,第一项老是将要运行的脚本的名称。

import sys

3,import语句和from语句的as扩展

import语句使用as扩展,能够为模块设置别名:

from module import attr_long_name as short_name

from语句把从模块导入的变量名,复制给脚本中的不一样的变量名,当模块中的变量名和当前做用域的变量名重名时,使用as扩展,能够避免变量名冲突:

import module_long_name as short_name

4,前向引用

当模块收入导入(或重载)时,Python会从头至尾执行语句,也就是说,语句只会引用前面已经定义的变量,这就是变量的前向引用,所以,模块顶层代码的语句顺序是很是重要的。

  • 在导入时,模块文件顶层的代码(不在函数内),在Python运行到时,就会被当即执行,这意味着,语句没法引用文件后面定义的变量名。
  • 位于函数主体内的代码直到函数被调用时才会执行,由于函数内的变量名在函数实际执行前都不会解析,一般能够引用模块文件内任意地方的变量。

通常来讲,前向引用只对当即执行的模块顶层代码有影响,函数能够引用任意一个模块顶层的变量名。

5,from是变量名的赋值,而import是引用对象

其实,from语句是一个普通的赋值运算,把模块内的变量名赋值给导入者的做用域内的变量名,实际上就是把变量名赋值给变量名,两个不一样做用域内的变量共享对象的引用,也就是说,一个对象的引用位于不一样的文件中。

例如,有模块mod_1,有两个属性,变量x和函数printer:

x=1
def printer():print(x)

在文件mod_2中,使用from导入模块mod_1,对变量x进行赋值,会致使变量x引用新的对象,而不是修改mod_1.py中的变量x的值:

from mod_1 import x, printer x=2 printer() # print 1

在文件mod_3中,使用import导入整个模块,当使用点号运算符修改模块内的属性时,就会修改mod_1.py中的变量的值,点号运算符把Python定向到了模块内的变量名,对变量进行修改,而不是赋值:

import mod_1 as m m.x=2 printer() # print 2

注意:点号运算符是引用变量,并修改变量的值。

6,reload()函数不会影响from导入

由于from语句在执行时会赋值变量名,因此,不会连接到变量名所在的模块。经过from语句导入的变量名就简单地变成了对象的引用。

当重载模块时,模块会从新建立对象,变量名引用新建的对象,然而位于重载以前的from引用的原始对象并不会改变,重载会影响后面的from语句。

from module import x ... from imp import reload() reload(module) #changes module in-place
x                    #still references old object

reload()函数会影响import语句,这是由于,重载不会删除和新建模块对象,也就是说,import语句引用的模块对象不变,可是,模块对象中的属性会被删除重建。当经过点号来引用模块的对象时,object.attr 会引用模块的最新建立的变量。

import module ... from imp import reload() reload(module) #changes module in-place
module.x             #get current x: reflects module reloads

 

参考文档:

相关文章
相关标签/搜索