页面导航:
前言:
本篇文章由网友“小黑”投稿发布。
本篇文章是根据《Python Tutorial》第15章的内容来写的学习笔记。该英文教程的下载地址如下:
百度盘地址:
http://pan.baidu.com/s/1c0eXSQG
DropBox地址:
点此进入DropBox链接
Google Drive:
点此进入Google Drive链接
文章中的测试代码都是通过python 2.7.x的版本来执行的。
通过import导入模块:
上一节中,我们介绍了可以使用函数来实现代码的重利用。此外,还可以将功能相似的函数都写入到一个单独的文件里,这个文件就是一个模块。例如,我们先在hello.py文件中写入下面这段代码:
def print_func(par):
print 'hello : ', par
return
def print_func2(par):
print 'hello2s : ', par
return
|
在这里hello.py文件对应的模块名就是hello,在其他程序中就可以通过import之类的语句将hello模块导入进来,import的语法格式如下:
import module1[, module2[,... moduleN]] |
当Python执行到import语句时,会先在搜索路径中搜索模块文件,当搜索到文件时就会将其导入进来,搜索路径默认包含了当前目录在内,完整的搜索路径信息在稍后会进行介绍。以下是test.py文件中的测试代码:
import hello
hello.print_func('black')
hello.print_func2('black')
|
这里假设test.py与hello.py位于同一个目录内,test.py脚本的执行结果如下:
black@slack:~/test$ python test.py
hello : black
hello2s : black
black@slack:~/test$
|
需要注意的是:如果不使用后面会介绍的reload函数的话,那么,不管你执行了多少次import,整个程序中单个模块只会被导入一次。这样可以避免模块被重复的加载执行。
from...import语句:
我们还可以使用from...import语句将模块中指定的名称(可以是函数名,类名,变量名等)导入到当前的namespace(命名空间)中。from...import的语句格式如下:
from modname import name1[, name2[, ... nameN]] |
例如,我们只想导入前面hello模块中的print_func函数的话,就可以使用下面这段代码:
from hello import print_func
print_func('black')
|
它不会将整个模块都导入进来,只会将print_func导入到当前的命名空间,此外,由于print_func存在于当前的命名空间中了,因此,就不需要使用hello.print_func这种方式来调用函数了,直接通过函数名就可以执行对应的函数。
如果想将模块中的所有名称都导入到当前的命名空间中的话,可以使用下面这个语法:
也就是通过指定星号这个通配符来导入所有的名称。不过,需要注意的是:如果当前脚本中存在相同的名称时,那么当前脚本里的名称可能会覆盖掉模块所导入的名称。例如,下面这段代码:
from hello import *
def print_func(par):
print 'test : ', par
return
print_func('black')
print_func2('black')
|
上面这段代码的执行结果如下:
black@slack:~/test$ python test.py
test : black
hello2s : black
black@slack:~/test$
|
上面执行的是test.py文件里的
print_func函数。这是因为test.py文件里的函数名称覆盖掉了hello模块中的同名函数,如果先在test.py文件里定义print_func函数,再从hello模块中导入名称时,那么,hello模块里的函数就会反过来覆盖掉test.py中的同名函数:
def print_func(par):
print 'test:', par
from hello import *
print_func('black')
print_func2('black')
|
这段代码的执行结果如下:
black@slack:~/test$ python test.py
hello: black
hello2s: black
black@slack:~/test$
|
模块文件的搜索路径:
当导入模块时,Python会在以下路径中搜索模块文件:
-
首先会在当前目录中搜索。
-
如果当前目录没找到,则在PYTHONPATH环境变量指定的路径中进行搜索。
-
如果上面两个路径都没找到,则会在Python的默认路径中进行搜索,例如,作者的默认路径是 /usr/local/lib/python2.7 。
完整的搜索路径信息都存储在sys(系统模块)的path变量中:
import sys
print sys.path
|
上面代码在作者的系统中的执行结果如下:
black@slack:~/test$ python test.py
['/home/black/test',
'/usr/local/lib/python27.zip',
'/usr/local/lib/python2.7', ......]
black@slack:~/test$
|
作者并没有设置PYTHONPATH环境变量,因此,上面的结果中只包含了当前目录与Python的默认路径。此外,默认路径在不同的Python版本以及不同的Python安装方式下都可能会不相同。
PYTHONPATH环境变量:
通过指定PYTHONPATH环境变量,我们可以指定额外的搜索路径。
在windows系统下,可以使用set命令来设置环境变量,例如下面这个命令:
set PYTHONPATH=G:\Python27\mytest\Phone |
在Linux系统中,如果和作者一样使用的是bash的shell,那么可以使用export命令,例如:
export PYTHONPATH=/home/black/test/Phone |
至于其他的shell,如csh之类的,就需要使用set命令,具体可以参考
http://www.cyberciti.biz/faq/unix-linux-adding-path/ 该链接对应的文章,这篇文章中介绍了如何在各个shell中设置PATH环境变量,我们可以将其设置方法应用到PYTHONPATH环境变量上。
以下是一个简单的例子:
black@slack:~/test$ export PYTHONPATH=/home/black/test/Phone
black@slack:~/test$ python test.py
['/home/black/test',
'/home/black/test/Phone',
'/usr/local/lib/python27.zip',
'/usr/local/lib/python2.7', ......]
black@slack:~/test$
|
可以看到,sys.path对应的搜索路径中,就将PYTHONPATH环境变量指定的路径给包含进来了。
命名空间:
命名空间其实就是一个词典,该词典中的每个成员的key对应为名称(可以是变量名,函数名,模块名等),而成员的value则对应为具体的对象(可以是列表对象,元组对象,整数对象,函数对象,模块对象等)。在Python中使用某个名称时,内部会先在命名空间里查找该名称所映射的对象,当找到对象时,就可以使用该对象去执行各种操作了。
Python程序中主要存在两个命名空间,一个local namespace(局部命名空间)和一个global namespace(全局命名空间)。可以通过locals与globals函数来查看到:
a = 123
b = 456
print 'local namespace:', locals(), '\n'
print 'global namespace:', globals()
|
上面代码的执行结果如下:
black@slack:~/test$ python test.py
local namespace: {'a': 123, 'b': 456, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', '__doc__': None}
global namespace: {'a': 123, 'b': 456, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', '__doc__': None}
black@slack:~/test$
|
命名空间中,双下划线开头与结尾的名称通常是Python预设的名称。可以看到,在全局范围内,局部与全局命名空间中的成员是一致的。下面再看下,函数的局部范围内,这两个命名空间的成员情况:
a = 123
b = 456
def print_func(par):
myname = 'black'
print myname, ':', par, '\n'
print 'local namespace:', locals(), '\n'
print 'global namespace:', globals()
print_func('test')
|
这段代码的执行结果如下:
black@slack:~/test$ python test.py
black : test
local namespace: {'par': 'test', 'myname': 'black'}
global namespace: {'a': 123, 'print_func': <function print_func at 0xb75f8e94>, 'b': 456, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'test.py', '__package__': None, '__name__': '__main__', '__doc__': None}
black@slack:~/test$
|
在函数内,局部命名空间中就只包含了局部变量在内(函数的参数也属于局部变量),全局命名空间中则包含了外部的全局名称(全局变量,定义过的函数,导入过的模块等)。
在一个函数里,既可以使用局部命名空间中的名称,又可以使用全局命名空间中的名称,当局部与全局命名空间中存在相同的名称时,起作用的会是局部命名空间中的名称。
函数内被赋予了值的变量,都会被Python当作局部变量。例如下面这段代码:
total = 2000;
def AssignTotal():
total = 100
print 'inner total: ', total
print 'global total: ', total
AssignTotal()
print 'global total: ', total
|
这段代码的执行结果如下:
black@slack:~/test$ python test.py
global total: 2000
inner total: 100
global total: 2000
black@slack:~/test$
|
由于total在AssignTotal函数中进行了赋值操作,因此就被Python假定为了局部变量。因此,默认情况下我们无法对全局变量进行设置操作,如果要在函数里对全局变量进行设置的话,就必须使用global关键字:
total = 2000
def AssignTotal():
global total
total = 3000
print 'inner total:', total
print 'global total:', total
AssignTotal()
print 'global total:', total
|
代码的执行结果如下:
black@slack:~/test$ python test.py
global total: 2000
inner total: 3000
global total: 3000
black@slack:~/test$
|
可以看到,使用global关键字后,就可以对全局变量进行设置操作了。
dir内建函数:
如果你想查看导入的模块中,包含了哪些名称的话,可以使用dir内建函数:
import math
print dir(math)
|
执行这段代码,可以得到如下结果:
black@slack:~/test$ python test.py
['__doc__', '__file__', '__name__', '__package__',
'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2',
'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees',
'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial',
'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot',
'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10',
'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh',
'sqrt', 'tan', 'tanh', 'trunc']
black@slack:~/test$
|
可以看到,math模块中包含了很多与数学运算相关的函数,通过dir(math)就可以得到这些函数的名称。
此外,在这些名称中,__doc__,__file__,__name__,__package__是Python预设的名称,分别表示:模块的说明介绍信息,模块的文件名(或文件路径),模块的名称,模块的包名(包的概念后面会介绍):
import math
print 'math.__file__:', math.__file__, '\n'
print 'math.__doc__:', math.__doc__, '\n'
print 'math.__name__:', math.__name__, '\n'
print 'math.__package__:', math.__package__, '\n'
|
这段代码的执行结果如下:
black@slack:~/test$ python test.py
math.__file__: /usr/local/lib/python2.7/lib-dynload/math.so
math.__doc__: This module is always available. It provides access to the
mathematical functions defined by the C standard.
math.__name__: math
math.__package__: None
black@slack:~/test$
|
reload内建函数:
前面提到过,当使用import导入模块时,模块只会被加载执行一次(不管你用import导入相同的模块多少次),如果我们想再次加载同一个模块的话,就可以使用reload函数,reload函数的语句格式为:
参数是模块名(注意不是包含模块名的字符串),例如,想要重新加载hello模块的话,就可以使用下面这条语句:
你不可以写成reload("hello")。
假设hello模块中的代码如下:
def print_func(par):
print 'hello : ', par
return
def print_func2(par):
print 'hello2s : ', par
return
print 'hello loaded!'
|
主程序中就可以使用reload函数,重复加载执行该模块:
import hello
reload(hello)
hello.print_func('test')
hello.print_func2('test')
|
代码的执行结果如下:
black@slack:~/test$ python test.py
hello loaded!
hello loaded!
hello : test
hello2s : test
black@slack:~/test$
|
package(包):
如果把前面介绍过的模块看成是单个python文件,那么这里的package(包)就可以看成是包含了很多模块文件的目录。通过导入包,就可以将对应目录下的模块都导入进来(当然包在被导入之后,其本质上也是一个模块对象,这在下面会进行介绍)。
每个包所在的目录都需要包含一个__init__.py脚本文件,当通过import语句导入某个包时,就会执行该脚本文件。
假设主程序是test.py,在主程序所在的目录中存在一个Phone目录,Phone目录内又存在三个模块文件:G3.py,Isdn.py,Pots.py 和一个 __init__.py的包初始化脚本文件,如下所示:
black@slack:~/test$ ls
Phone/ test.py
black@slack:~/test$ ls Phone
G3.py __init__.py Isdn.py Pots.py
black@slack:~/test$
|
Phone目录中的三个模块文件的代码如下:
black@slack:~/test$ cat Phone/G3.py
def G3():
print "I'm G3 Phone"
black@slack:~/test$ cat Phone/Isdn.py
def Isdn():
print "I'm Isdn Phone"
black@slack:~/test$ cat Phone/Pots.py
def Pots():
print "I'm Pots Phone"
black@slack:~/test$
|
接着,可以在包的初始化脚本__init__.py文件里,将这三个模块的函数都导入到包中:
from Pots import Pots
from Isdn import Isdn
from G3 import G3
|
最后,主程序在导入了Phone包后,就可以直接通过Phone.Pots,Phone.G3以及Phone.Isdn来执行不同模块文件中定义的函数了:
import Phone
Phone.Pots()
Phone.G3()
Phone.Isdn()
|
主程序的测试结果如下:
black@slack:~/test$ python test.py
I'm Pots Phone
I'm G3 Phone
I'm Isdn Phone
black@slack:~/test$
|
包里面其实还可以导入子包(子目录),或者子包的子包(子目录的子目录)。
当然,包在通过import语句导入之后,本质上就是一个模块对象:
代码的执行结果如下:
black@slack:~/test$ python test.py
<module 'Phone' from '/home/black/test/Phone/__init__.pyc'>
black@slack:~/test$
|
可以看到,Phone就是一个module(模块对象),该模块对应的脚本文件为__init__.pyc,模块的原脚本文件会被Python解析为pyc结尾的字节码文件,如果存在同名的字节码文件,就会加载执行字节码文件,否则就会去加载原脚本文件。
如果Phone目录中还包含一个subphone的子目录的话,而且subphone目录内也包含相关模块文件与__init__.py初始化脚本文件的话,那么可以使用以下语句,将subphone给导入进来:
结束语:
这世上原本就没有什么神话。所谓的神话,不过是常人的思维所不易理解的平常事。
—— 天道
原作者经常提到的《天道》是一部根据豆豆的长篇小说《遥远的救世主》改编的电视剧。有兴趣的读者可以看一下。