Python 学习 第九篇:模块
模块是把程序代码和数据封装的Python文件,也就是说,每一个以扩展名py结尾的Python源代码文件都是一个模块。每一个模块文件就是一个独立的命名空间,用于封装顶层变量名;在一个模块文件的顶层定义的所有的变量名(函数名也是一个变量名),称作模块的属性。导入模块给予了对模块的全局作用域中的变量名的读取权,也就是说,在模块导入时,模块文件的全局作用域变成了模块内对象的命名空间。
导入一个模块之后,可以使用模块中定义的属性,例如,在模块moduleA中导入一个模块moduleB,那么moduleB就变成了命名空间,在moduleA中可以使用moduleB中的全局变量,引用的格式是:moduleB.attribution_name。
概括来说,Python中的模块拥有三个大的作用:
- 代码重用:模块用于保存代码,按照需要导入或重新导入模块,以使用模块中的代码。
- 变量封装:模块把变量名封装起来,模块变成了一个独立的命名空间,模块封装的变量名变成了属性,这就避免了变量名的冲突。
- 代码共享:把通用的代码组织成模块,分享给他人。
一,Python程序的结构
一般来说,一个Python程序结构是由一个主文件(又称为顶层文件、脚本)和多个模块构成的,其中主文件包含了程序的控制流程,也就是启动之后能够运行整个程序的文件,而模块文件是用于提供工具的库。
在Python中,一个文件导入了一个模块来获得该模块内定义的工具的访问权,因此,通用的Python程序的结构就是:在顶层文件中导入模块,并引用模块中的工具。
例如,在脚本文件main.py中导入functools模块中的reduce()函数,使用该工具函数实现列表元素的加和:
from functools import reduce
a=reduce((lambda x,y:x+y),range(0,5))
print(a)
二,模块的创建
定义模块,只需要使用文本编辑器,把一些Python代码输入到文本文件中,然后以".py"为扩展名名进行保存,任何此类文件都会被自动认为是Python模块。在模块顶层指定的变量名是模块的属性。注意:模块的文件名必须以扩展名 .py 结尾。
例如,创建一个strtools模块,文件名必须是strtools.py,文件的内容是Python代码,其中append_suffix是模块的属性名,引用该属性的格式是:strtools.append_suffix(arg):
def append_suffix(str):
return str+'- py'
三,导入模块
导入从本质上来讲,就是为了载入另一个文件,并能够读取该文件的内容。模块的导入是由两个语句来处理:
- import语句:以一个整体导入一个模块;
- from语句:从一个模块文件中导入特定的变量名;
import语句和from语句都会加载模块,加载的过程包括搜索、编译和执行模块文件。两者的差别在于:import语句 会读取整个模块,所以必须进行定义后才能读取它的属性;form语句直接获取模块中特定的变量名。
在一个导入语句中的模块名起到两个作用:识别加载的外部文件;把模块名作为命名空间,用于封装模块内的顶层变量名。在导入模块时,模块定义的对象也会在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
参考文档:
Python 学习 第九篇:模块的更多相关文章
- Python学习第九篇——while和for的区别
pets = ['dog','cat','dog','goldfish','cat','rabbit','cat'] print(pets) for pet in pets: print(pet) # ...
- Python学习 Part4:模块
Python学习 Part4:模块 1. 模块是将定义保存在一个文件中的方法,然后在脚本中或解释器的交互实例中使用.模块中的定义可以被导入到其他模块或者main模块. 模块就是一个包含Python定义 ...
- python学习第九讲,python中的数据类型,字符串的使用与介绍
目录 python学习第九讲,python中的数据类型,字符串的使用与介绍 一丶字符串 1.字符串的定义 2.字符串的常见操作 3.字符串操作 len count index操作 4.判断空白字符,判 ...
- python学习之argparse模块
python学习之argparse模块 一.简介: argparse是python用于解析命令行参数和选项的标准模块,用于代替已经过时的optparse模块.argparse模块的作用是用于解析命令行 ...
- Python学习day19-常用模块之re模块
figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...
- Python学习day18-常用模块之NumPy
figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...
- python学习之random模块
Python中的random模块用于生成随机数.下面介绍一下random模块中最常用的几个函数. random.random random.random()用于生成一个0到1的随机符点数: 0 < ...
- Python学习笔记之模块与包
一.模块 1.模块的概念 模块这一概念很大程度上是为了解决代码的可重用性而出现的,其实这一概念并没有多复杂,简单来说不过是一个后缀为 .py 的 Python 文件而已 例如,我在某个工作中经常需要打 ...
- Python学习第一篇
好久没有来博客园了,今天开始写自己学习Python和Hadoop的学习笔记吧.今天写第一篇,Python学习,其他的环境部署都不说了,可以参考其他的博客. 今天根据MachineLearning里面的 ...
随机推荐
- recovery log直接输出到串口
我们在调试recovery升级的时候,我们经常需要查看recovery的log,google的原始逻辑中,recovery的log并非直接输出到串口,我们需要输入命令才能获取,我们有三种方式: 第一种 ...
- MySQL GTID复制错误处理之跳过错误
某Slave报错信息: mysql> show slave status\G; mysql> show slave status\G; ************************** ...
- jQuery选择元素的方法大全
jQuery选择器其实是一个有些地方比较费解的,如果没有经过多次实验的话,很难得出它的每个操作符到底是干什么的,很容易出错,经过 我的多次测试,终于对一些比较难理解或容易出错的选择操作进行总结,既方便 ...
- SQL SERVER2008判断文件夹是否存在并创建文件夹
原文地址:https://www.cnblogs.com/iiwen/p/7650118.html DECLARE @PATH VARCHAR(255) --路径 DECLARE @DATE VARC ...
- MySQL使用索引的场景分析、不能使用索引的场景分析
一.MySQL中能够使用索引的典型场景 1.匹配全值.对索引中的列都有等值匹配的条件.即使是在and中,and前后的列都有索引并进行等值匹配. 2.匹配值的范围查询,对索引的值能够进行范围查找. 3. ...
- U890采购入库单修改供应商
采购入库单表头 SELECT *FROM RdRecordWHERE (cCode = '0000051801') 采购入库单表体 SELECT *FROM RdRecordsWHERE (cPOID ...
- Linux 小知识翻译 - 「桌面环境」
这次聊聊桌面环境. 上次聊了 X Window System 相关的内容,虽然令人意外,但X Window System 和桌面环境不是一回事.请大家稍微考虑一下. X Window System 是 ...
- 17秋 软件工程 团队第五次作业 Alpha Scrum12
各个成员今日完成的任务 Alpha版本完成. 项目的发布说明 本版本的新功能 1.部门人员管理,包括纳新申请与审核: 2.部门活动发布与查看: 3.部门活动相册: 4.子部门信息录入. 软件对运行环境 ...
- 从源码的角度分析List与Set的区别
很多时候我们在讨论List与Set的异同点时都在说: 1.List.Set都实现了Collection接口 2.List是有序的,可以存储重复的元素,允许存入null 3.Set是无序的,不允许存储重 ...
- JavaScript中的typeof操作符用法实例
在Web前端开发中,我们经常需要判断变量的数据类型.鉴于ECMAScript是松散类型的,因此需要有一种手段来检测给定变量的数据类型——typeof就是负责提供这方便信息的操作符. 对一个值使用t ...