模块
在开发过程中,一个文件里代码越长就越不容易维护。
为了编写易于维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。
在Python中,一个.py文件就称之为一个模块(Module)。
使用模块的几个好处
- 大大提高了代码的可维护性。
- 编写代码不必从零开始。当一个模块编写完毕,就可以被其他模块引用。
- 避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中。 但还是要注意变量名尽量不要与BIF(built-in functions,内建函数)名字冲突。
<br>
模块名和包名
由于不同的人编写的模块名可能会相同,为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。

假设图中abc和xyz两个模块的名字和外面其他模块名字冲突了,我们可以通过包来组织模块,避免冲突。 只要顶层包名(也即这里的mycompany文件夹)不同即可。
此时abc的模块名变为 mycompany.abc
, xyz的模块名变为 mycompany.xyz
。
Notice:
- 注意区分模块和模块名!两者不一定相同!(比如上面的模块
abc
和它的模块名 mycompany.abc
)
- 每个包目录下都必须有一个
__init__.py
文件,否则Python就不会把这个文件夹当作一个包。 __init__.py
可以是空文件,也可以有Python代码,它本身就是一个模块,并且它的模块名就是包名(这里是 mycompany
)。

包结构可以是多级的。比方说这里www.py的模块名就是 mycompany.web.www
。 两个utils.py的模块名分别是 mycompany.utils
和 mycompany.web.utils
,它们不会冲突 。 mycompany.web
这个模块名对应的就是web目录下的 __init__.py
模块。
Notice:
自己创建的模块的模块名不要和Python自带的模块的模块名冲突! 比如系统自带sys模块,自己的模块就不要命名sys.py,否则无法会无法正确import自带的sys模块。
<br>
使用模块
Python本身就内置了很多模块可以直接import使用。 下面我们自己编写一个Hello模块作为例子:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'This is the docstring(document comment) of this module '
__author__ = 'Lincoln Deng'
import sys
def test():
args = sys.argv
if len(args)==1:
print('No argument is passed.')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__':
test()
例子解析
Python模块的标准文件模板
文件的第1行和第2行是标准注释:
- 第1行注释是用于声明使用什么程序来执行这个脚本,它可以使得这个脚本能在Unix/Linux/Mac上直接运行(也即可以使用
./Hello.py
的形式执行而不是 python Hello.py
的形式)。如果系统装了多个版本的python,#!/usr/bin/env python3
会保证调用环境变量 $PATH
中的第一个叫python3的程序来执行脚本。又因为在一些系统中python能被重定向到python3,也即默认使用python3,所以直接用 #!/usr/bin/env python
也是可以的。还有一种写法是 #!/usr/bin/python
,这样写就是指定一个路径,兼容性不如使用env的写法好。注意,如果我们使用 python Hello.py
或者 python3 Hello.py
的方式直接指定解释器来执行的话,这句注释就没用了。另外,在Windows下这句注释也是被忽略的,但为了代码的兼容性最好还是写上。
- 第2行注释表示(解释器和编辑器)应使用UTF-8编码来读取这个.py脚本文件中的代码文本。在Python2中,默认源代码编码方式为ASCII,要用到中文就得采用很别扭的escape写法,直接写中文会出错。后来有了PEP 0263标准,规定了显式声明代码文本编码的方法。一般有三种格式,包括:能被大部分编辑器识别的
# -*- coding: utf-8 -*-
,最简单的 # coding=utf-8
以及vim的 # vim: set fileencoding=utf-8
(其实还可以写成别的方式,主要看编辑器怎样正则匹配这一行)。当然这里的utf-8可以换为其他编码。注意编码声明必须放在代码文件的第一行或第二行。另外,这里只是声明读取时采用的编码方式,保存代码时用什么方式要自己设置编辑器。实测由于Python3默认用utf-8编码,所以只要我们正确使用utf-8编码保存代码文件,那么读取时不声明也没关系。当然,为了代码的兼容性最好还是写上。
文件的第4行是一个字符串,表示模块的文档注释,任何模块的第一个字符串都被视为模块的文档注释;
文件的第6行使用 __author__
变量记录模块作者的名字。
以上就是Python模块的标准文件模板,在Windows下使用Python3时,其实不写也没关系,但养成良好的书写习惯更好。
正式代码部分
- 导入sys模块
如果想使用Python内置的sys模块,就要先导入该模块: import sys
。 导入之后,相当于创建了一个变量sys
,该变量指向sys模块,通过这个变量可以访问sys模块的全部功能。
- 使用argv变量获取参数列表
sys模块有一个 argv
变量,这个变量属于list类型,存储着命令行的所有参数(即我们在命令行执行该脚本时使用的参数)。argv
列表中至少包含一个元素,因为第一个参数永远是该脚本文件的名称,例如:
在命令行执行 python3 Hello.py
获得的 sys.argv
就是 ['Hello.py']
;
在命令行执行 python3 Hello.py Michael
获得的 sys.argv
就是 ['Hello.py', 'Michael]
。 要获取字符串 'Michael'
只需要调用 sys.argv[1]
。
- 条件判断
if __name__=='__main__':
test()
在Hello模块中我们定义了一个test函数,这个条件判断的意思就是,如果程序执行到这里,__name__
变量的值是 '__main__'
的话就执行 test
函数。
其中 __name__
变量是个特殊变量,当我们在命令行执行Hello.py时,Python解释器就会把 __name__
变量赋值为 __main__
。
但如果在别的文件中导入模块,__name__
的值就是模块的名字而非 __main__
,此时if判断结果为 False
。 借助这个特性,我们可以在if判断中编写一些额外的代码,用于测试模块的功能,而在使用(导入)模块时,if判断里的代码不会被执行。
在命令行下执行
保存代码文件后,打开命令行,先把路径切换到保存 Hello.py
的目录,然后执行:
C:\Users\Administrator\Desktop>python Hello.py
No argument is passed.
C:\Users\Administrator\Desktop>python Hello.py Lincoln
Hello, Lincoln!
在交互环境下执行
比方说使用IDLE或者在命令行中输入python进入:
C:\Users\Administrator\Desktop>python
Python 3.5.1 |Anaconda 4.0.0 (64-bit)| (default, Feb 16 2016, 09:49:46) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import Hello
>>> Hello.__name__
'Hello'
>>> Hello.test()
No argument is passed.
导入Hello模块时,它的 __name__
变量会被赋值为模块名 Hello
,而不是 __main__
,所以if判断为 False
,if判断里的代码不会被执行。如果要使用 test
函数,就要通过 模块名.函数名
的方式进行调用,使用模块内的其他变量/函数同理。
命名规范和作用域
在模块中我们会定义很多函数和变量,有些是希望给别人用的,有些则希望仅仅在模块内部使用。
公开(public)的变量和函数
命名格式如: abc
,x123
,PI
, 如果是有特殊用途则在名称前后各加上两个下划线,如:__author__
,__name__
。
>>> Hello.__doc__
'This is the docstring(document comment) of this module '
>>> Hello.__name__
'Hello'
>>> Hello.__author__
'Lincoln Deng'
>>> Hello.__file__
'C:\\Users\\Administrator\\Desktop\\Hello.py'
可以看到 __doc__
变量返回了我们前面模块例子的代码中第一个字符串,也即文档注释(DocString)。什么是文档注释呢?其实就是一个模块/类/函数/方法的定义中第一个声明的字符串,使用这些对象的 .doc
属性即可访问。关于文档注释的书写标准可以查看PEP 0257。
私有(private)的变量和函数
命名格式是在名称前加一个或两个下划线,如 _xxx
和 __xxx
。这样的函数或变量不应该被外部直接引用(即通过 模块名.变量名
的方式调用)。比方说下面定义的 _private_1
函数和 _private_2
函数,我们不希望使用这个模块的人调用它们:
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
虽然不希望用户调用私有函数,但我们可以暴露给用户一个接口(公开的函数),也即这里的 greeting
函数,把调用两个私有函数的代码和逻辑封装在里面,用户直接调用 greeting
函数,然后让 greeting
函数决定怎样调用私有函数。
当用户使用模块时不需要关心私有的变量和函数,直接使用公开的变量和函数就可以了。 这是一种常用的代码封装和抽象的方法。
注意:
这里说私有函数和变量 不应该被直接引用,而不是 不能被直接引用。 因为Python没有方法可以限制用户调用私有函数和变量(没有),所以这样命名只是一种约定的编程习惯,使用者怎么做就要看他自己怎么决定了。
良好的习惯是外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
<br>
安装第三方库
在Python中,安装第三方库(从而使用第三方库中提供的第三方模块),可以通过包管理工具pip(也有其他的包管理工具)完成。
安装Python时选择了安装pip的话,就可以直接在命令行中使用pip工具了。
pip install Pillow
在命令行键入 pip install 第三方库的库名
后, pip就会自动帮用户下载并安装第三方库。


这里安装的是Python Imaging Library这个第三方库,是一个Python下非常强大的图像处理工具库。 因为PIL只支持到Python2.7,所以这里用的是基于PIL开发的支持Python3的Pillow。
安装完成后打开 F:\Python35\Lib\site-packages
文件夹(具体路径看安装Python的位置而定)就会发现多了两个文件夹,一个是 Pillow-3.1.1.dist-info
, 另一个是 PIL
。 前者包含该库的一些基本信息,后者就是我们需要用到的包了,里面是有 __init__.py
文件的。
安装好的包我们可以在文件中直接用 from 包名 import 模块名
来导入要使用的模块,而不需要先转到模块所在的目录下再导入,也不需要把模块复制到我们的工程文件夹中。
举一个使用Pillow包中利用Image模块生成图片缩略图的例子:
>>> from PIL import Image
>>> im = Image.open('C:/Users/Administrator/Desktop/test.png') # 打开指定路径下的一张照片
>>> print(im.format, im.size, im.mode) # 打印照片的文件格式&尺寸&颜色模式
PNG (400, 300) RGB
>>> im.thumbnail((200, 100)) # 创建缩略图
>>> im.save('thumb.jpg', 'JPEG') # 保存缩略图
>>> im.show() # 查看图片
其他常用的第三方库还有MySQL的驱动:mysql-connector-python,用于科学计算的NumPy库:numpy,用于生成文本的模板工具Jinja2,等等。
<br>
模块搜索路径
在Python中导入模块时,Python解释器会从指定好的路径中进行搜索。我们可以使用sys模块的变量 path
来查看模块的搜索路径,导入模块时会从这些路径中查找.py文件:
C:\Users\Administrator>python
Python 3.5.1 |Anaconda 4.0.0 (64-bit)| (default, Feb 16 2016, 09:49:46) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', 'F:\\Anaconda3\\python35.zip', 'F:\\Anaconda3\\DLLs', 'F:\\Anaconda3\\lib', 'F:\\Anaconda3', 'F:\\Anaconda3\\lib\\site-packages',
'F:\\Anaconda3\\lib\\site-packages\\Sphinx-1.3.5-py3.5.egg',
'F:\\Anaconda3\\lib\\site-packages\\win32', 'F:\\Anaconda3\\lib\\site-packages\\win32\\lib',
'F:\\Anaconda3\\lib\\site-packages\\Pythonwin', 'F:\\Anaconda3\\lib\\site-packages\\setuptools-20.3-py3.5.egg']
sys模块的 path
变量是一个列表,它会在启动Python时被初始化,初始赋值(按顺序)由三个部分组成,一是当前目录(即列表中的空字符串),二是环境变量 PYTHONPATH
中的路径,三是一些默认的路径(包含内置模块和一些通过pip安装的模块)。由于我没有设置环境变量 PYTHONPATH
,所以上面只有一和三两部分。尝试新建一个名为 PYTHONPATH
的环境变量,添加一条路径指向F盘,重新启动Python程序,此时就会发现 path
变量的初始赋值中多了F盘的路径了:
C:\Users\Administrator>python
Python 3.5.1 |Anaconda 4.0.0 (64-bit)| (default, Feb 16 2016, 09:49:46) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', 'F:\\', 'F:\\Anaconda3\\python35.zip', 'F:\\Anaconda3\\DLLs', 'F:\\Anaconda3\\lib', 'F:\\Anaconda3', 'F:\\Anaconda3\\lib\\site-packages',
'F:\\Anaconda3\\lib\\site-packages\\Sphinx-1.3.5-py3.5.egg',
'F:\\Anaconda3\\lib\\site-packages\\win32', 'F:\\Anaconda3\\lib\\site-packages\\win32\\lib',
'F:\\Anaconda3\\lib\\site-packages\\Pythonwin', 'F:\\Anaconda3\\lib\\site-packages\\setuptools-20.3-py3.5.egg']
如果需要使用自己编写的模块,可以把它们放到这些目录中。 也可以自己增加搜索路径。具体来说分为两种方法:
- 方法一:直接使用
apeend
往 sys.path
列表添加搜索路径,这种方法只在该次运行时有效,重启Python交互环境后会恢复原来的路径:
>>> import sys
>>> sys.path.append('C:/Users/Administrator/Desktop')
- 方法二:配置环境变量PYTHONPATH,只需要增加自己的搜索路径,默认的路径是不会被覆盖掉的,使用这种方法就不需要每次都修改
sys.path
了。
关于 sys.path
的详情可以查看官方文档。
<br>
文件搜索路径
这一节与模块无关,但是觉得有必要区分好文件搜索路径和模块搜索路径! 文件搜索路径是当前工作目录,如果我们不指定路径,直接使用文件名访问文件的时候,Python会从当前路径中进行查找;模块搜索路径则是像上一节提及的那样,指 sys.path
列表中包含的路径。
要获取当前工作路径可以使用os模块的 getcwd
函数,也即get current work directory:
>>> import os
>>> os.getcwd()
'C:\\Users\\Administrator\\Desktop'
要调用的文件如果放在当前工作路径上就可以直接用 文件名.文件格式
指定,比方说我在桌面放了一张 test.jpg
,那么访问时直接用 im = Image.open('test.jpg')
就可以打开了,无须使用完整的路径,也即 im = Image.open('C:/Users/Administrator/Desktop/test.jpg')
。
注意Python中路径字符串里斜杠的使用, 如果使用反斜杠划分就必须转义,也即写作双反斜杠 \\
;如果使用左斜杠 /
则不需要进行转义。Python默认路径字符串都使用双反斜杠。
如果文件不在当前工作路径,那么我们写路径时可以有两种写法:
im = Image.open('C:/Users/Administrator/Desktop/test.jpg')
这里使用了左斜杠,所以不需要转义。
im = Image.open('./test.jpg')
相对路径即相对当前工作路径而言的路径,这是为了避免路径过长而设计的,可以用 .
符来代替当前工作路径。