本篇文章由网友“小黑”投稿发布。上一节中,我们介绍了可以使用函数来实现代码的重利用。此外,还可以将功能相似的函数都写入到一个单独的文件里,这个文件就是一个模块。例如,我们先在hello.py文件中写入下面这段代码...

    页面导航: 前言:

    本篇文章由网友“小黑”投稿发布。

    本篇文章是根据《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 modname import *

    也就是通过指定星号这个通配符来导入所有的名称。不过,需要注意的是:如果当前脚本中存在相同的名称时,那么当前脚本里的名称可能会覆盖掉模块所导入的名称。例如,下面这段代码:

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函数的语句格式为:

reload(module_name)

    参数是模块名(注意不是包含模块名的字符串),例如,想要重新加载hello模块的话,就可以使用下面这条语句:

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语句导入之后,本质上就是一个模块对象:

import Phone

print Phone


    代码的执行结果如下:

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给导入进来:

import Phone.subphone

结束语:

    这世上原本就没有什么神话。所谓的神话,不过是常人的思维所不易理解的平常事。

——  天道

    原作者经常提到的《天道》是一部根据豆豆的长篇小说《遥远的救世主》改编的电视剧。有兴趣的读者可以看一下。
 
上下篇

下一篇: Python基本的I/O操作 (一)

上一篇: Python用户自定义函数

相关文章

Python基本的I/O操作 (三)

Python相关的数学运算函数

Python的基本语法

Python随机数函数

Python字符串类型

Python用户自定义函数