官方手册:https://docs.python.org/3/tutorial/modules.htmlhtml
python源代码文件按照功能能够分为两种类型:python
例如文件a.py和b.py在同一目录下,它们的内容分别是:linux
# b.py x="var x in module b" y=5 # a.py: import b import sys print(b.x) print(b.y)
a.py导入其它文件(b.py)后,就可使用b.py文件中的属性(如变量、函数等)。这里,a.py就是可执行文件,b.py就是模块文件,但模块名为b,而非b.py。windows
python提供了一些标准库,是预约义好的模块文件,例如上面的sys模块。app
在此有几个注意点,在后面会详细解释:less
b.x
、b.y
import abc.b
。下一篇文章会详细解释包的导入方式在a.py中导入模块b的时候,python会作一系列的模块文件路径搜索操做:b.py在哪里?只有找到它才能读取、运行(装载)该模块。ssh
在任何一个python程序启动时,都会将模块的搜索路径收集到sys模块的path属性中(sys.path
)。当python须要搜索模块文件在何处时,首先搜索内置模块,若是不是内置模块,则搜索sys.path中的路径列表,搜索时会从该属性列出的路径中按照从前向后的顺序进行搜索,而且只要找到就当即中止搜索该模块文件(也就是说不会后搜索的同名模块覆盖先搜索的同名模块)。函数
例如,在a.py文件中输出一下这个属性的内容:性能
# a.py: import sys print(sys.path)
结果:单元测试
['G:\\pycode', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']
python模块的搜索路径包括几个方面,按照以下顺序搜索:
G:\\pycode
PYTHONPATH
所设置的路径(若是定义了该环境变量,则从左向右的顺序搜索)须要注意,上面sys.path的结果中,除了.zip
是一个文件外,其它的搜索路径全都是目录,也就是从这些目录中搜索模块X的文件X.py是否存在。
这个目录是最早搜索的,且是python自动搜索的,无需对此进行任何设置。从交互式python程序终输出sys.path的结果:
>>> sys.path ['', 'C:\\WINDOWS\\system32', 'C:\\Program Files (x86)\\Python36-32\\Lib\\idlelib', 'C:\\Program Files (x86)\\Python36-32\\python36.zip', 'C:\\Program Files (x86)\\Python36-32\\DLLs', 'C:\\Program Files (x86)\\Python36-32\\lib', 'C:\\Program Files (x86)\\Python36-32', 'C:\\Users\\malong\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Program Files (x86)\\Python36-32\\lib\\site-packages']
其中第一个''
表示的就是程序所在目录。
注意程序所在目录和当前目录是不一样的。例如,在/tmp/目录下执行/pycode中的a.py文件
cd /tmp python /pycode/a.py
其中/tmp为当前目录,而/pycode是程序文件a.py所在的目录。若是a.py中导入b.py,那么将首先搜索/pycode,而不是/tmp。
这个变量中能够自定义一系列的模块搜索路径列表,这样能够跨目录搜索(另外一种方式是设置.pth文件)。但默认状况下这个环境变量是未设置的。
在windows下,设置PYTHONPATH环境变量的方式:命令行中输入:SystemPropertiesAdvanced-->环境变量-->系统环境变量新建
若是是多个路径,则使用英文格式的分号分隔。如下是临时设置当前命令行窗口的PYTHONPATH:
set PYTHONPATH='D:\pypath; d:\pypath1'
在unix下,设置PYTHONPATH环境变量的方式,使用冒号分隔多个路径:另外,必须得export导出为环境变量
export PYTHONPATH=/tmp/pypath1:/tmp/pypath2
若是要永久生效,则写入配置文件中:
echo 'export PYTHONPATH=/tmp/pypath1:/tmp/pypath2' >/etc/profile.d/pypth.sh chmod +x /etc/profile.d/pypth.sh source /etc/profile.d/pypth.sh
在Linux下,标准库的路径通常是在/usr/lib/pythonXXX/下(XXX表示python版本号),此目录下有些分了子目录。
例如:
['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']
其中/usr/lib/python3.5和其内的几个子目录都是标准库的搜索路径。
注意其中/usr/lib/python35.zip,它是ZIP文件组件,当定义此文件为搜索路径时,将自动解压缩该文件,并今后文件中搜索模块。
Windows下根据python安装位置的不一样,标准库的路径不一样。若是以默认路径方式安装的python,则标准库路径为C:\\Program Files (x86)\\Python36-32
及其分类的子目录。
能够将自定义的搜索路径放进一个.pth文件中,每行一个搜索路径。而后将.pth文件放在python安装目录或某个标准库路径内的sitepackages目录下便可。
这是一种替换PYTHONPATH的友好方式,由于不一样操做系统设置环境变量的方式不同,而以文件的方式记录是全部操做系统都通用的。
例如,windows下,在python安装目录C:\\Program Files (x86)\\Python36-32
下新增一个mypath.pth文件,内容以下:
d:\pypath1 d:\pypath2
再去输出sys.path,将能够看到这两个路径已经放进了搜索列表中。
除了上面环境变量和.pth文件,还能够直接修改sys.path或者site.getsitepackages()的结果。
例如,在import导入sys模块以后,能够修改sys.path,向这个列表中添加其它搜索路径,这样以后导入其它模块的时候,也会搜索该路径。
例如:
import sys sys.path.append('d:\\pypath3') print(sys.path)
sys.path的最后一项将是新添加的路径。
python的import是在程序运行期间执行的,并不是像其它不少语言同样是在编译期间执行。也就是说,import能够出如今任何地方,只有执行到这个import行时,才会执行导入操做。且在import某个模块以前,没法访问这个模块的属性。
python在import导入模块时,首先搜索模块的路径,而后编译并执行这个模块文件。虽然归纳起来只有两个过程,但实际上很复杂。
前文已经解释了import的模块搜索过程,因此这里大概介绍import的其它细节。
之前面的a.py中导入模块文件b.py为例:
import b
import导入模块时,搜索到模块文件b.py后:
1.首先在内存中为每一个待导入的模块构建module类的实例:模块对象。这个模块对象目前是空对象,这个对象的名称为全局变量b。
注意细节:module类的对象,变量b。
输出下它们就知道:
print(b) print(type(b))
输出结果:
<module 'b' from 'g:\\pycode\\b.py'> <class 'module'>
由于b是全局变量,因此当前程序文件a.py中不能从新对全局变量b进行赋值,这会使导入的模块b被丢弃。例如,下面是错误的:
import b b=3 print(b.x) # 已经没有模块b了
另外,由于import导入时是将模块对象赋值给模块变量,因此模块变量名不能是python中的一些关键字,好比if、for等,这时会报错。虽然模块文件名能够为list、keys等这样的内置函数名,但这会致使这些内置函数不可用,由于根据变量查找的做用域规则,首先查找全局变量,再查找内置做用域。也就是说,模块文件的文件名不能是这些关键字、也不该该是这些内置函数名。
File "g:/pycode/new.py", line 11 import if ^ SyntaxError: invalid syntax
2.构造空模块实例后,将编译、执行模块文件b.py,并按照必定的规则将一些结果放进这个模块对象中。
注意细节,编译、执行b.py、将结果保存到模块对象中。
模块第一次被导入的时候,会进行编译,并生成.pyc字节码文件,而后python执行这个pyc文件。当模块被再次导入时,若是检查到pyc文件的存在,且和源代码文件的上一次修改时间戳mtime彻底对应(也就是说,编译后源代码没有进行过修改),则直接装载这个pyc文件并执行,不会再进行额外的编译过程。固然,若是修改过源代码,将会从新编译获得新的pyc文件。
注意,并不是全部的py文件都会生成编译获得的pyc文件,对于那些只执行一次的程序文件,会将内存中的编译结果在执行完成后直接丢弃(多数时候如此,但仍有例外,好比使用compileall模块能够强制编译成pyc文件),但模块会将内存中的编译结果持久化到pyc文件中。另外,运行字节码pyc文件并不会比直接运行py文件更快,执行它也同样是一行行地解释、执行,惟一快的地方在于导入装载的时候无需从新编译而已。
执行模块文件(已完成编译)的时候,按照通常的执行流程执行:一行一行地、以代码块为单元执行。通常地,模块文件中只用来声明变量、函数等属性,以便提供给导入它的模块使用,而不该该有其余任何操做性的行为,好比print()操做不该该出如今模块文件中,但这并不是强制。
总之,执行完模块文件后,这个模块文件将有一个本身的全局名称空间,在此模块文件中定义的变量、函数等属性,都会记录在此名称空间中。
最后,模块的这些属性都会保存到模块对象中。因为这个模块对象赋值给了模块变量b,因此经过变量b能够访问到这个对象中的属性(好比变量、函数等),也就是模块文件内定义的全局属性。
假设a.py中导入了模块b和模块sys,在b.py中也导入了模块sys,但python默认对某个模块只会导入一次,若是a.py中先导入sys,再导入b,那么导入b并执行b.py的时候,会发现sys已经导入了,不会再去导入sys。
实际上,python执行程序的时候,会将全部已经导入的模块放进sys.module属性中,这是一个dict,能够经过下面的方式查看已导入的模块名:
>>> import sys >>> list(sys.module.keys())
若是某个程序文件中屡次使用import(或from)导入同一个模块,虽然不会报错,但实际上仍是直接使用内存中已装载好的模块对象。
例如,b.py中x=3,导入它以后修改该值,而后再次导入,发现b.x并不会发生改变:
import b print(b.x) # 3 b.x=33 print(b.x) # 33 import b print(b.x) # 33
可是python提供了reload进行屡次重复导入的方法,见后文。
import导入时,可使用as
关键字指定一个别名做为模块对象的变量,例如:
import b as bb bb.x=3 print(bb.x)
这时候模块对象将赋值给变量bb,而不是b,b此时再也不是模块对象变量,而仅仅只是模块名。使用别名并不会影响性能,由于它仅仅只是一个赋值过程,只不过是从原来的赋值对象变量b变为变量bb而已。
import语句是导入模块中的全部属性,而且访问时须要使用模块变量来引用。例如:
import b print(b.x)
除了import,还有一个from语句,表示从模块中导入部分指定的属性,且使得能够直接使用这些属性的名称来引用这些属性,而不须要加上模块变量名。例如原来import导入时访问变量x使用b.x
,from导入时只需使用x便可。实际上,from导入更应该称为属性的再次赋值(拷贝)。
例如,b.py中定义了变量x、y、z,同时定义了函数f()和g(),在a.py中导入这个模块文件,但只导入x变量和f函数:
# a.py文件内容: from b import x,f print(x) f() # b.py文件内容: x=3 y=4 z=5 def f(): print("function f in b.py") def g(): print("function g in b.py")
注意上面a.py中引用模块b中属性的方式没有加上b.X
,而是直接使用x和f()来引用。这和import是不同的。至于from和import导入时的变量名称细节,在下面的内容中会详细解释。
虽然from语句只导入模块的部分属性,但实际上仍然会完整地执行整个模块文件。
一样的,from语句也能够指定导入属性的变量别名,例如,将b.py中的属性x赋值给xx,将y赋值给yy:
from b import x as xx,y as yy print(xx) print(yy)
from语句还有一个特殊导入统配符号*
,它表示导入模块中的全部属性。
# a.py文件: from b import * print(x,y,z) f() g()
多数时候,不该该使用from *
的方式,由于咱们可能会忘记某个模块中有哪些属性拷贝到了当前文件,特别是多个from *
时可能会出现属性覆盖的问题。
不管时import仍是from,都只导入一次模块,但使用reload()能够强制从新装载模块。
reload()是imp模块中的一个函数,因此要使用imp.reload()以前,必须先导入imp。
from imp import reload reload(b)
reload()是一个函数,它的参数是一个已经成功被导入过的模块变量(若是使用了别名,则应该使用别名做为reload的参数),也就是说该模块必须在内存中已经有本身的模块对象。
reload()会从新执行模块文件,并将执行获得的属性彻底覆盖到原有的模块对象中。也就是说,reload()会从新执行模块文件,但不会在内存中创建新的模块对象,因此原有模块对象中的属性可能会被修改。
例如,模块文件b.py中x=3,导入b模块,修改其值为33,而后reload这个模块,会发现值从新变回了3。
import b print(b.x) # 3 b.x=33 print(b.x) # 33 from imp import reload reload(b) print(b.x) # 3
有时候reload()颇有用,可让程序无需重启就执行新的代码。例如,在python的交互式模式下导入模块b,而后修改python源码,再reload导入:
>>> import b >>> b.x 3 # 不要关掉交互式解释器,直接修改源代码中的b=3333 >>> from imp import reload >>> reload(b) <module 'b' from 'G:\\pycode\\b.py'> >>> b.x 3333
但正由于reload()重载模块会改变原始的值,这多是很危险的行为,必定要清楚地知道它是在干什么。
import导入时,模块对象中的属性有本身的名称空间,而后将整个模块对象赋值给模块变量。
例如,在a.py中导入b:
import b print(b.x)
这个过程惟一和当前文件a.py做用域有关的就是模块对象变量b,b.py中声明的属性和当前文件无任何关系。不管是访问仍是修改,都是直接修改这个模块对象自身做用域中的值。因此,只要模块变量b不出现冲突问题,能够放心地修改模块b中的属性。
另外一方面,由于每一个进程都有本身的内存空间,因此在a.py、c.py中都导入b时,a.py中修改b的属性值不会影响c.py中导入的属性,a.py和c.py中模块对象所保存的属性都是执行b.py后获得的,它们相互独立。
from导入模块时,会先执行完模块文件,而后将指定的部分属性从新赋值给当前程序文件的同名全局变量。
例如,在模块文件b.py中定义了x、y、z变量和f()、g()函数:
# b.py: x=3 y=4 b=5 def f(): print("function f in b.py") def g(): print("function g in b.py")
当在a.py中导入b模块时,若是只导入x、y和f():
# a.py: from b import x, y, f
实际上的行为是构造模块对象后,将这个模块对象对应的名称空间中的属性x、y和f从新赋值给a.py中的变量x、y和f,而后丢弃整个模块对象以及整个名称空间。换句话说,b再也不是一个有效的模块变量(因此和import不同),来自b的x,y,z,f和g也都被丢弃。
这里有几个细节,须要详细解释清楚,只有理解了才能搞清楚它们是怎么生效的。
假设如今模块文件b.py的内容为,而且a.py中导入x,y,f属性:
# b.py: x=3 y=[1,2] z=5 def f(): print("function f in b.py") def g(): print("function g in b.py") # a.py: from b import x,y,f
首先在执行模块文件b.py时,会构造好本身的模块对象,而且模块对象有本身的名称空间(做用域),模块对象构造完成后,它的名称空间大体以下:
而后python会在a.py的全局做用域内建立和导入属性同名的全局变量x,y和f,而且经过赋值的方式将模块的属性赋值给这些全局变量,也就是:
x = b.x y = b.y f = b.f
上面的b只是用来演示,实际上变量b是不存在的。
赋值完成后,咱们和构造的整个模块对象就失去联系了,由于没有变量b去引用这个对象。但须要注意,这个对象并无被删除,仅仅只是咱们没法经过b去找到它。
因此,如今的示意图以下:
由于是赋值的方式传值的,因此在a.py中修改这几个变量的值时,是直接在模块对象做用域内修改的:对于不可变对象,将在此做用域内建立新对象,对于可变对象,将直接修改原始对象的值。
另外一方面,因为模块对象一直保留在内存中,下次继续导入时,将直接使用该模块对象。对于import和from,是直接使用该已存在的模块对象,对于reload,是覆盖此模块对象。
例如,在a.py中修改不可变对象x和可变对象y,以后import或from时,可变对象的值都会随之改变,由于它们使用的都是原来的模块对象:
from b import x,y x=33 y[0]=333 from b import x,y print((x,y)) # 输出(3, [333, 2]) import b print((b.x,b.y)) # 输出(3, [333, 2])
from导入时,因为b再也不是模块变量,因此没法再使用reload(b)去重载对象。若是想要重载,只能先import,再reload:
from b import x,y ...CODE... # 想要重载b import b from imp import reload reload(b)
内置函数dir可用于列出某模块中定义了哪些属性(全局名称空间)。完整的说明见help(dir)
。
import b dir(b)
输出结果:
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'g', 'x', 'y', 'z']
可见,模块的属性中除了本身定义的属性外,还有一些内置的属性,好比上面以__
开头和结尾的属性。
若是dir()不给任何参数,则输出当前环境下定义的名称属性:
>>> import b >>> x=3 >>> aaa=333 >>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'aaa', 'b', 'x']
每一个属性都对应一个对象,例如x对应的是int对象,b对应的是module对象:
>>> type(x) <class 'int'> >>> type(b) <class 'module'>
既然是对象,那么它们都会有本身的属性。例如:
>>> dir(x) ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
因此,也能够直接dir某个模块内的属性:
import b dir(b.x) dir(b.__name__)
dir()不会列出内置的函数和变量,若是想要输出内置的函数和变量,能够去标准模块builtins中查看,由于它们定义在此模块中:
import builtins dir(buildins)
除了内置dir()函数能够获取属性列表(名称空间),对象的__dict__
属性也能够获取对象的属性字典(名称空间),它们的结果不彻底同样。详细说明参见dir()和__dict__属性区别。
总的来讲,获取对象M中一个自定义的属性age,有如下几种方法:
M.age M.__dict__['age'] sys.modules['M'].age getattr(M,'age')
前面说了,py文件分两种:用于执行的程序文件和用于导入的模块文件。当直接使用python a.py
的时候表示a.py是用于执行的程序文件,经过import/from方式导入的py文件是模块文件。
__name__
属性用来区分py文件是程序文件仍是模块文件:
__main__
换句话说,__main__
表示的是当前执行程序文件的默认模块名,想必学过其余支持包功能的语言的人很容易理解:程序都须要一个入口,入口程序所在的包就是main包,在main包中导入其它包来组织整个程序。python也是如此,只不过它是隐式自动设置的。
对于python来讲,由于隐式自动设置,该属性就有了特殊妙用:直接在模块文件中经过if __name__ == "__main__"
来判断,而后写属于执行程序的代码,若是直接用python执行这个文件,说明这个文件是程序文件,因而会执行属于if代码块的代码,若是是被导入,则是模块文件,if代码块中的代码不会被执行。
显然,这是python中很是方便的单元测试方式。
例如,写一个模块文件,里面包含一个函数,用来求给定序列的最大值和最小值:
def minmax(func,*args): res = args[0] for arg in args[1:]: if func(arg,res): res = arg return res def lessthan(x,y): return x < y def greatethan(x,y): return x > y # 测试代码 if __name__ == "__main__": print(minmax(lessthan,3,6,2,1,4,5)) print(minmax(greatethan,3,6,2,1,4,5))