模块
一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。
其实import加载的模块分为四个通用类别:
1 使用python编写的代码(.py文件)
2 已被编译为共享库或DLL的C或C++扩展
3 包好一组模块的包
4 使用C编写并链接到python解释器的内置模块
引入导入模块的概念
退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。
随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。
导入模块
import
现在有一个spam.py的文件。
#spam.py
print('from the spam.py')
money=1000
def read1():
print('spam->read1->money',money)
def read2():
print('spam->read2 calling read')
read1()
def change():
global money
money=0
spam.py
在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句)。
导入模块要做三件事:
第一件事:创建名称空间,用来存放spam.py中定义的名字,
第二件事:基于刚刚创建的名称空间来执行spam.py,
第三件事:创建名字spam指向该名称空间,spam.名字的操作,都是以spam.py为准。
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
3 import spam
4 import spam
5 import spam
运行结果:
from the spam.py
导入多次
sys.module是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。
每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突。
import spam
money=1
spam.change()
print(money)
进行上述操作money的值任然是1,spam模块里的函数定义也是“被执行”的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,用globals()可以查看,并不会影响导入模块的名称空间。
import spam as sm#改个名字,没什么实际的影响
print(sm.money)
if file_format == 'xml':
import xmlreader as reader
elif file_format == 'csv':
import csvreader as reader
data=reader.read_date(filename)#不管是什么格式我只要知道reader就好了
起别名
可以用反射查看是否模块有这个属性。
print(hasattr(spam,'money'))
from ... import...
对比import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式
而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了。
这种导入方法需要注意几个地方,
from spam import read1
money=1000
read1()
#此时执行的read1来自spam模块,read1取得的值仍是spam里的money
#导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
from spam import read2
def read1():
print('==========')
read2()
#当前定义的名称会覆盖模块里的名称
from spam import read1
def read1():
print('==========')
read1()
执行结果:
from the spam.py
==========
from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000
这种方式也支持as。
from spam import read1 as read
多行导入。
from spam import (read1,
read2,
money)
from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。
使用__all__可以控制*的内容。
__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字
如果spam.py中的名字前加_,即_money,则from spam import *,则_money不能被导入。
导入的过程中修改模块是无效的,python不支持重载。
if __name__ == '__main__':用来控制.py文件在不同的应用场景下执行不同的逻辑。
模块的查找顺序是:内存中已经加载的模块(sys.modules)->内置模块->sys.path路径中包含的模块。
python程序可以修改sys.path,路径放到前面的优先于标准库被加载。
import sys
sys.path.append(r'/a/b/c/d')
sys.path.insert(0,r'/x/y/z') #排在前的目录,优先被搜索
注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理。
import sys
sys.path.append('module.zip')
import foo,bar
#也可以使用zip中目录结构的具体位置
sys.path.append('module.zi