创建你自己的模块很简单,其实你一直在这样做!因为每个python脚本都是一个模块。你只需确保它带有.py扩展名即可。
下面的例子会让你对其有一个清晰的认识:
1 | #!/usr/bin/python |
上面就是一个简单的模块,如你所见,这和我们平时的python程序相比没有什么特别之处。记住模块应该放到导入它的那个程序所在的目录下,或者放到sys.path列出的目录之一中。
可以使用 import 语句导入模块。
1 | #!/usr/bin/python |
1 | $ python mymodule_demo.py |
import 会在哪些目录里查找模块呢?可以通过 sys.path
查看:
1 | import sys |
要添加一个新的目录到 sys.path
列表的第一项(从而使其出现在python搜索路径的开头,并且优先级最高),可以使用sys.path.insert(0, newpath)
。
如果你希望将变量argv直接导入到你的程序中(避免每次输入sys.
),那么可以使用from sys import argv
语句。
如果希望导入sys模块中的所有名字(除了以__
开头的名字),则from sys import *
可以做到。此语句可以用于任何模块。
通常你应该避免使用这个语句并用import语句代替之,因为使用后者可以避免名字冲突,程序的可读性也更好。
下面是一个使用from…import语法的版本:
1 | #!/usr/bin/python |
mymodule_demo2.py
与mymodule_demo
的输出完全相同。
注意,如果导入mymodule的模块中已经存在同名的__version__
,则将发生名字冲突。
事实上这很可能发生,因为每个模块都用__version__
声明它的版本是一种常见的做法。
因此建议你优先考虑import
语句,虽然它可能会让你的程序变的更长一些。
你同样可以使用:
1 | from mymodule import * |
这将导入模块的所有公有名字,例如sayhi
,但是不会导入__version__
因为它以双下划线开头。
导入模块是一个相对昂贵的操作,所以python使用了一些技巧加速这个过程。
一个办法是创建后缀为.pyc的字节编译文件用于将程序转换为中间格式。(还记得介绍python如何工作的那一节吗?)
当你下次从其他文件导入模块时pyc文件会非常有用 – 它将大大增加导入速度,因为导入模块的部分操作已经预先完成了。
并且这个字节编译文件仍然是平台无关的。
__name__
属性每个模块都有一个名字,并且通过模块中的某些语句可以得到这个模块名。在一些想要搞清模块是独立运行还是被导入的情况下,这会非常方便。
如前所述当模块第一次被导入时模块中的代码会被执行。我们可以据此改变模块独立执行时的行为方式。这可以通过模块的__name__
属性做到。(注:独立运行是指程序最开始运行的那个脚本文件(/模块))
1 | #!/usr/bin/python |
1 | $ python using_name.py |
imp.reload()
,例如 import imp; imp.reload(modulename)
你可以使用dir
函数列出一个对象定义的所有标识符。例如对于一个模块,标识符包括函数,类,变量。
当你为dir()
函数提供一个模块名,它将返回定义在其中的所有名字。
当dir()
的参数为空时,返回定义在当前模块中所有名字。
1 | import sys # 得到sys模块的属性列表 |
如今,你必须开始留心组织你的程序层次了。
变量在函数内部,函数和全局变量通常在模块内部。那么如何组织模块呢?这就轮到包登场了。
包仅仅是包含模块的文件夹,并带有一个特殊的文件__init__.py
用于指示python这个文件夹是特殊的,因为它包含python模块。
让我们假设你需要创建一个叫做world
的包,里面包括诸如asia
,africa
等的子包。
下面告诉你应该如何组织文件夹结构:
1 | -<some folder present in the sys.path>/ |
包只是用来有层次的组织模块。你会在标准库中看到它的很多应用。
关于打包的详细信息,可以参考打包。
1 | $ pip install 模块名 |
如果装了 Vitualenv 库,它也自带了 pip installer, 因此也可以使用:
1 | $ ENV/bin/pip 模块名 |
下面简单介绍一些常用的库:
sys
模块包含一些系统相关的功能。
一个常见的功能是利用它获取 argv 变量 —— 具体说sys.argv是一个包含命令行参数的列表,其首项为该程序的名称,第二个参数及后续的参数为该程序的命令行参数(有点像shell语言的 $0
为程序名称,$1
之后为命令行参数)。
1 | #!/usr/bin/python |
1 | $ python using_sys.py we are arguments |
当python执行import sys
语句时,它将查找sys模块。本例中sys是内建模块之一,因此python知道在哪能找到它。如果导入的不是一个编译模块,即不是用python编写的模块,python解释器会在变量sys.path
中列出的目录中查找它。如果模块被找到,这个模块中的语句将被执行然后你就可以使用它了(注: 只有顶级语句才会执行,也就是主块中的语句)。
注意一个模块只有在第一次导入时会被初始化。
sys模块中的argv通过点号引用即sys.argv
。它清晰的指出这个名字是sys模块中的一部分。这种语法的另一个优势是不会和你的程序中的同名argv变量发生冲突。
sys.path
包含一个目录名列表指示从哪里导入模块。观察程序输出,sys.path的第一个字符串为空 —— 其指出当前目录也是sys.path
的一部分,这与PYTHONPATH
环境变量是相同的。这意味着你可以直接导入当前目录下的模块,否则你就必须将你的模块放到sys.path
列出的目录中的一个了。
注意程序在哪个目录运行的,这个目录就是这个程序的当前目录。运行import os; print(os.getcwd())
可以看到你的程序的当前目录。
假设我们想要检查所使用的python命令行的版本,比方说我们需要确定正在使用的版本不低于3。
诸如此类的功能正是sys模块所提供的。
1 | import sys |
sys
模块含有一个version_info
元组用于提供版本信息。其第一个元素为主版本。
因此我们可以通过检查它确保程序只会运行在python 3.0和3.0以上:
1 | #!/usr/bin/python |
1 | $ python2.5 versioncheck.py |
Python 3 带有一个模块叫做os
,代表 “操作系统(operating system)。” os
模块 包含非常多的函数用于获取(和修改)本地目录、文件进程、环境变量等的信息。Python 尽最大的努力在所有支持的操作系统上提供一个统一的API, 这样你就可以在保证程序能够在任何的计算机上运行的同时尽量少的包含平台特定的代码。
os
模块提供了两个函数处理当前工作目录
1 | import os |
os.path
模块包含了操作文件名和目录名的函数。
函数名 | 用途 |
---|---|
abspath(path) |
返回绝对路径。 |
basename(p) |
返回路径名最后的部分。 |
commonprefix(m) |
给定一个路径列表,返回从起始算起最长的公共部分。 |
dirname(p) |
返回给定路径名所在的目录的路径。 |
exits(p) |
测试一个文件是否存在。如果不存在,返回False 。 |
expanduser(path) |
扩展~ 和~user 为完整的路径。如果user 或$HOME 不存在,不做任何事情。 |
expandvars(path) |
扩展形式为$var 和${var} 的shell变量,未知变量将保持不变。 |
getatime(filename) |
返回指定文件的最近访问时间。 |
getctime(filename) |
返回指定文件的元数据的修改时间。 |
getmtime(Filename) |
返回指定文件的最近修改时间。 |
getsize(Filename) |
返回文件的大小。 |
isabs(s) |
测试一个路径是否为绝对路径。 |
isdir(s) |
测试一个路径名是否指向一个存在的目录。 |
isfile(s) |
测试一个路径是否为一个常规的文件。 |
islink(path) |
测试一个路径是否为一个符号链接。 |
ismount(path) |
测试一个路径是否为一个挂载点。 |
join(a, *p) |
将两个或多个路径部分连接。必要时自动插入/ 。 |
lexists(path) |
测试一个路径是否不存在。如果不存在,返回True 。 |
normcase(s) |
将路径名转为标准形式。在Posix标准文件系统下没有什么变化。 |
normpath(path) |
将路径名标准化,例如消除双重斜杠,等。 |
realpath(Filename) |
返回文件所在的真实路径,消除路径中遇到的任何符号连接。 |
relpath(path, start=None) |
返回给定路径的相对路径。 |
samefile(f1, f2) |
测试两个路径是否引用同一个文件。 |
sameopenfile(fp1, fp2) |
测试两个打开的文件对象是否引用同一个文件。 |
samestat(s1, s2) |
测试两个stat缓冲是否引用同一个文件。 |
split(p) |
分割路径名,返回一个形式为(head, tail) 的元组,其中,tail 是最后一个斜杠后的所有内容,其他部分则为head 。任一部分都允许为空。 |
splitdrive(p) |
将路径名分割成驱动名drive 和路径path 。在Posix标准的文件系统下,drive 部分为空。 |
splitext(p) |
从路径名分割出扩展名和其他部分。返回(root, ext) 元组。ext 部分可能为空。 |
1 | import os |
os.path.expanduser()
用来将包含~
符号(表示当前用户Home 目录)的路径扩展为完整的路径。在任何有 Home 目录概念的操作系统上(包括 Linux,Mac OS X 和 Windows),这个函数都能工作。返回的路径不以斜杠结尾,但是 os.path.join()
并不介意这一点。
os.path 也包含用于分割完整路径名,目录名和文件名的函数:
os.path.split
:分割一个完整路径并返回目录和文件名。os.path.splitext
: 分割一个文件名并返回短文件名和扩展名。1 | pathname = |
每一个现代文件系统都对文件存储了元信息: 创建时间,最后修改时间,文件大小等等。Python 单独提供了一个的 API 用于访问这些元信息。你不需要打开文件。知道文件名就足够了。
os.stat(文件名)
:返回一个包含多种文件元信息的对象。os.st_mtime()
:获取最后修改时间os.st_size
:返回文件大小1 | import os |
当你希望构造一个从根目录开始或者是包含盘符的绝对路径时,可以使用os.path.realpath()
函数。
1 | import os |
time
模块可用于获取当前日期和时间,比如 time.strftime
方法
1 | time.strftime('%Y-%m-%d',time.localtime(time.time())) |
time.strftime里面有很多参数,可以让你能够更随意的输出自己想要的东西:
%y
两位数的年份表示(00-99)%Y
四位数的年份表示(000-9999)%m
月份(01-12)%d
月内中的一天(0-31)%H
24小时制小时数(0-23)%I
12小时制小时数(01-12)%M
分钟数(00=59)%S
秒(00-59)%a
本地简化星期名称%A
本地完整星期名称%b
本地简化的月份名称%B
本地完整的月份名称%c
本地相应的日期表示和时间表示%j
年内的一天(001-366)%p
本地A.M.或P.M.的等价符%U
一年中的星期数(00-53)星期天为星期的开始%w
星期(0-6),星期天为星期的开始%W
一年中的星期数(00-53)星期一为星期的开始%x
本地相应的日期表示%X
本地相应的时间表示%Z
当前时区的名称%%
%号本身glob 模块是 Python 标准库中的另一个工具,它可以通过编程的方法获得一个目录的内容,并且它使用熟悉的命令行下的通配符。 glob 模块使用 shell 风格的通配符。
1 | '/Users/pilgrim/diveintopython3/') os.chdir( |
正则表达式模块,常见的正则式有:
模式 | 说明 |
---|---|
. |
匹配任意字符 |
^ |
匹配字符串开始位置。 |
$ |
匹配字符串结束位置。 |
\b |
匹配一个单词边界。 |
\d |
匹配一个数字。 |
\D |
匹配一个任意的非数字字符。 |
x? |
匹配可选的 x 字符。换句话说,就是 0 个或者 1 个 x 字符。 |
x* |
匹配 0 个或更多的 x。 |
x+ |
匹配 1 个或者更多 x。 |
x{n,m} |
匹配 n 到 m 个 x,至少 n 个,不能超过 m 个。 |
(a|b|c) |
匹配单独的任意一个 a 或者 b 或者 c。 |
(x) |
把括号里匹配得到的内容作为一个组,它会记忆它匹配到的字符串。你可以用re.search 返回的匹配对象的groups() 函数来获取到匹配的值。 |
更多支持的正则式请参见 help(re)
。常用的正则式可以参见正则表达式。
查找地址中的单词 ‘ROAD’:
1 | '100 BROAD ROAD. APT 3' s = |
也可以先构造一个模式的类然后用它进行查找:
1 | '100 BROAD ROAD. APT 3' s = |
替换地址中的 ‘ROAD’ 字符串为 ‘RD.’:
1 | '100 BROAD ROAD. APT 3' s = |
在 python 中,比较复杂的是 \
字符必须被转义,这有的时候会导致 \
字符传染(想想可能还要对\
字符做转义的情况)。
为了解决 \
字符传染的问题,可以使用原始字符串。这只需要在字符串的前面添加一个字符r
。它告诉 python,字符串中没有任何字符需要转义。\t
是一个制表符,但 r\t
只是一个字符 \
紧跟着一个字符 t。
python 允许你使用松散正则表达式来达到目的。松散正字表达式和普通紧凑的正则表达式有两点不同:
\
来转义。#
开头直到行尾。它可以在多行正则表达式中增加注释信息,这就避免了在 python 代码中的多行注释。他们的工作方式是一样的。判断一串字符是否为合法的罗马数字:
1 | ''' pattern = |
注意,如果要使用松散正则表达式,需要传递一个叫 re.VERBOSE
的参数。就像你看到的那样,正则表达式中有很多空白符,他们都被忽略掉了。还有一些注释信息,当然也被正则表达式忽略掉。当空白符和注释信息被忽略掉后,这个正则表达式和上面的是完全一样的,但是它有更高的可读性。
如果你希望得到存储在某处的重要信息或调试信息,以便检查程序是否如期运行时该咋办呢?你如何将这些信息存储在某处呢?
这些可以通过logging
模块做到。
1 | #!/usr/bin/python |
1 | $python use_logging.py |
如果我们查看生成的日志文件 test.log ,它的内容如下:
1 | 2008-09-03 13:18:16,233 : DEBUG : Start of the program |
我们使用了3个标准库模块 —— os
模块与系统交互,platform
模块取得平台信息,即操作系统信息。而logging
模块用于记录日志信息。
首先,我们通过platform.platform
(详见help(platform)
)返回的字符串检测操作系统类型。如果为windows系统,则分别计算出主驱动器,主目录与文件名,这个文件用于存储相关信息。然后将这三部分合并得到文件的全路径。对于其他平台,我们只需得到用户的主目录就能计算出文件的全路径了。
我们之所以没有简单的使用字符串连接合并这三部分而是利用os.path.join
,原因在于这个特殊的函数可以确保路径格式符合特定系统的规范。
之后我们配置logging模块,指示在我们指定的文件中以特殊的格式写入所有信息。最后,我们就能写入信息了,它们可以是调试信息,警告信息甚至是危机信息(critical messages)。
一但程序开始运行,我们就可以检查这个文件以了解程序发生了什么,而用户并不会看到这些信息。
执行import this
可以看到《Python 之禅》。
1 | import this |
这里的讨论列出了每个原则的范例。
模块名 | 用途 |
---|---|
re | 实现了对正则表达式(regular expression)的支持 |
array | 数组 |
queue | 队列 |
copy | 复制对象 |
string | 定义了一些有用的常量,比如 ascii_letters 与ascii_lowercase |
textwrap | 提供了用于包裹与填充字符串的函数与方法 |
unicodedata | 获取unicode字符和相关信息 |
logging | 记录日志信息 |
pdb | 调试 |
timeit | 对代码进行即时 |
cProfile | 用于比较函数与方法的性能。精确的展示了有什么被调用及每个调用耗费了多少时间。 |
itertools | 为创建和使用迭代器提供很多便利工具,例如排列组合等。 |
unittest | 单元测试模块 |
模块名 | 用途 |
---|---|
pickle | 以二进制格式存储/读取对象 |
ElementTree | 解析XML,详见解析XML。 |
lxml | 非标准库,提供比 ElementTree 更完善的XML Xpath支持 |
json | 读写json |
模块名 | 用途 |
---|---|
fractions | 使用分数以及分数的运算 |
decimal | 更精确的进行小数运算(速度慢一些) |
random | 产生随机数、随机取样等 |
math | 补充了一些重要的数学常数和数学函数 |
cmath | 专门提供复数使用的数学函数 |
numpy | 非标准库中的包,但它的数组运算的良好支持,让它在基于Python的科研和计算方面得到相当广泛的应用。 |
模块名 | 用途 |
---|---|
sys | 管理Python自身的运行环境 |
os | 包含非常多用于获取(和修改)本地目录、文件进程、环境变量等的信息 |
time | 管理时间 |
datetime | 管理日期和时间 |
glob、shutil | 文件管理 |
difflib | 用于比较文件(或字符串)之间的区别 |
subprocess | 执行外部命令,其功能相当于我们在操作系统的命令行中输入命令以执行 |
platform | 获取平台信息 |
threading | 多线程 |
multiprocessing | 多进程 |
getopt | 处理命令行选项 |
humansize | 非标准库,获取文件大小信息,带kiB等单位 |
模块名 | 用途 |
---|---|
socket | 网络可编程部分的底层 |
asyncore | 实现异步处理 |
urlparse | URL理解和操作 |
sqlite3 | 数据库 |
http.client, BaseHTTPServer、urllib、urllib2 | http服务 |
httplib2 | http服务第三方开源库 |
进一步研究标准库的最好办法就是阅读Python Module of the Week系列,或者Python的官方文档。