模块的编写

创建你自己的模块很简单,其实你一直在这样做!因为每个python脚本都是一个模块。你只需确保它带有.py扩展名即可。

下面的例子会让你对其有一个清晰的认识:

示例

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# Filename: mymodule.py

def sayhi():
print('Hi, this is mymodule speaking.')

__version__ = '0.1'

# 结束mymodule.py编写

上面就是一个简单的模块,如你所见,这和我们平时的python程序相比没有什么特别之处。记住模块应该放到导入它的那个程序所在的目录下,或者放到sys.path列出的目录之一中。

模块的导入

使用 import 语句

可以使用 import 语句导入模块。

示例

1
2
3
4
5
6
7
#!/usr/bin/python
# Filename: mymodule_demo.py

import mymodule

mymodule.sayhi()
print ('Version', mymodule.__version__)

输出

1
2
3
$ python mymodule_demo.py
Hi, this is mymodule speaking.
Version 0.1

import 会在哪些目录里查找模块呢?可以通过 sys.path 查看:

1
2
3
4
5
6
>>> import sys
>>> sys.path
['', '/usr/bin', '/usr/lib/python33.zip', '/usr/lib/python3.3
', '/usr/lib/python3.3/plat-linux', '/usr/lib/python3.3/lib-d
ynload', '/usr/lib/python3.3/site-packages', '/usr/lib/python
3.3/site-packages/setuptools-0.6c11.egg-info']

要添加一个新的目录到 sys.path 列表的第一项(从而使其出现在python搜索路径的开头,并且优先级最高),可以使用sys.path.insert(0, newpath)

使用 from…import…语句

如果你希望将变量argv直接导入到你的程序中(避免每次输入sys.),那么可以使用from sys import argv语句。

如果希望导入sys模块中的所有名字(除了以__开头的名字),则from sys import *可以做到。此语句可以用于任何模块。

通常你应该避免使用这个语句并用import语句代替之,因为使用后者可以避免名字冲突,程序的可读性也更好。

示例

下面是一个使用from…import语法的版本:

1
2
3
4
5
6
#!/usr/bin/python
# Filename: mymodule_demo2.py

from mymodule import sayhi, __version__
sayhi()
print('Version', __version__)Python en:Modules 59

mymodule_demo2.pymymodule_demo的输出完全相同。

注意,如果导入mymodule的模块中已经存在同名的__version__,则将发生名字冲突。

事实上这很可能发生,因为每个模块都用__version__声明它的版本是一种常见的做法。

因此建议你优先考虑import语句,虽然它可能会让你的程序变的更长一些。

你同样可以使用:

1
from mymodule import *

这将导入模块的所有公有名字,例如sayhi,但是不会导入__version__因为它以双下划线开头。

字节编译文件 .pyc

导入模块是一个相对昂贵的操作,所以python使用了一些技巧加速这个过程。

一个办法是创建后缀为.pyc的字节编译文件用于将程序转换为中间格式。(还记得介绍python如何工作的那一节吗?)

当你下次从其他文件导入模块时pyc文件会非常有用 – 它将大大增加导入速度,因为导入模块的部分操作已经预先完成了。

并且这个字节编译文件仍然是平台无关的。

.pyc文件一般被创建在与其对应的.py文件所在的相同目录下。如果python没有这个目录的写权限,则.pyc文件不会被创建。

模块的__name__属性

每个模块都有一个名字,并且通过模块中的某些语句可以得到这个模块名。在一些想要搞清模块是独立运行还是被导入的情况下,这会非常方便。

如前所述当模块第一次被导入时模块中的代码会被执行。我们可以据此改变模块独立执行时的行为方式。这可以通过模块的__name__属性做到。(注:独立运行是指程序最开始运行的那个脚本文件(/模块))

示例

1
2
3
4
5
6
7
#!/usr/bin/python
# Filename: using_name.py

if __name__ == '__main__':
print('This program is being run by itself')
else:
print('I am being imported from another module')

输出

1
2
3
4
5
6
7
8
9
10
$ python using_name.py
This program is being run by itself

$ python

>>> import using_name

I am being imported from another module

>>>

重新加载模块

因为效率的原因, 每个模块在每个解释器会话中只被导入一次. 一旦你修订了你的模块, 就需要重启解释器 —— 或者, 若你只是想交互式地测试一个模块,使用 imp.reload(),例如 import imp; imp.reload(modulename)

查看已定义的所有标识符

你可以使用dir函数列出一个对象定义的所有标识符。例如对于一个模块,标识符包括函数,类,变量。

当你为dir()函数提供一个模块名,它将返回定义在其中的所有名字。

dir()的参数为空时,返回定义在当前模块中所有名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import sys  # 得到sys模块的属性列表
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions']
>>> del sys # 删除sys模块
>>> dir(sys)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'sys' is not defined
>>> dir() # 得到当前模块的属性列表
['__builtins__', '__doc__', '__loader__', '__name__', '__package__']
>>> a = 5 # create a new variable 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']
>>> del a # 删除名字a
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']

模块的组织:包

如今,你必须开始留心组织你的程序层次了。

变量在函数内部,函数和全局变量通常在模块内部。那么如何组织模块呢?这就轮到包登场了。

包仅仅是包含模块的文件夹,并带有一个特殊的文件__init__.py用于指示python这个文件夹是特殊的,因为它包含python模块

让我们假设你需要创建一个叫做world的包,里面包括诸如asiaafrica等的子包。

下面告诉你应该如何组织文件夹结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
-<some folder present in the sys.path>/
- world/
- __init__.py
- asia/
- __init__.py
- india/
- __init__.py
- foo.py
- africa/
- __init__.py
- madagascar/
- __init__.py
- bar.py

包只是用来有层次的组织模块。你会在标准库中看到它的很多应用。

模块的发布

关于打包的详细信息,可以参考打包

第三方模块的安装

1
$ pip install 模块名

如果装了 Vitualenv 库,它也自带了 pip installer, 因此也可以使用:

1
$ ENV/bin/pip 模块名

常用库

下面简单介绍一些常用的库:

sys模块

sys模块包含一些系统相关的功能。

获取参数列表

一个常见的功能是利用它获取 argv 变量 —— 具体说sys.argv是一个包含命令行参数的列表,其首项为该程序的名称,第二个参数及后续的参数为该程序的命令行参数(有点像shell语言的 $0 为程序名称,$1 之后为命令行参数)。

示例
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
# Filename: using_sys.py

import sys

print('The command line arguments are:')

for i in sys.argv: # 注意通过sys.argv引用
print(i)

print('\n\nThe PYTHONPATH is', sys.path, '\n')
输出
1
2
3
4
5
6
7
8
9
$ python using_sys.py we are arguments
The command line arguments are:
using_sys.py
we
are
arguments


The PYTHONPATH is ['', '/usr/lib/python33.zip', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-linux', '/usr/lib/python3.3/lib-dynload', '/usr/lib/python3.3/site-packages', '/usr/lib/python3.3/site-packages/setuptools-0.6c11.egg-info']
说明

当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())可以看到你的程序的当前目录。

Windows用户注意: windows下sys.path[0]可能不为空,而是显式指出当前路径。

检查python版本

假设我们想要检查所使用的python命令行的版本,比方说我们需要确定正在使用的版本不低于3。

诸如此类的功能正是sys模块所提供的。

示例
1
2
3
4
5
>>> import sys
>>> sys.version_info
(3, 0, 0, 'beta', 2)
>>> sys.version_info[0] >= 3
True
说明

sys模块含有一个version_info元组用于提供版本信息。其第一个元素为主版本。

因此我们可以通过检查它确保程序只会运行在python 3.0和3.0以上:

示例
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/python
# Filename: versioncheck.py

import sys, warnings

if sys.version_info[0] < 3:
warnings.warn("Need Python 3.0 for this program to run",
RuntimeWarning)
else:
print('Proceed as normal')
输出
1
2
3
4
5
$ python2.5 versioncheck.py
versioncheck.py:6: RuntimeWarning: Need Python 3.0 for this program to run
RuntimeWarning)
$ python3 versioncheck.py
Proceed as normal

os 模块

Python 3 带有一个模块叫做os,代表 “操作系统(operating system)。” os 模块 包含非常多的函数用于获取(和修改)本地目录、文件进程、环境变量等的信息。Python 尽最大的努力在所有支持的操作系统上提供一个统一的API, 这样你就可以在保证程序能够在任何的计算机上运行的同时尽量少的包含平台特定的代码。

示例1: 处理当前工作目录

os 模块提供了两个函数处理当前工作目录

1
2
3
4
5
6
>>> import os
>>> print(os.getcwd())
C:\Python31
>>> os.chdir('/Users/pilgrim/diveintopython3/examples') # 即使在windows上也可以使用Linux风格的路径
>>> print(os.getcwd())
C:\Users\pilgrim\diveintopython3\examples

os.path

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部分可能为空。
示例2: 处理文件名和目录名
1
2
3
4
5
6
7
8
9
>>> import os 
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples/', 'humansize.py'))
/Users/pilgrim/diveintopython3/examples/humansize.py
>>> print(os.path.join('/Users/pilgrim/diveintopython3/examples', 'humansize.py'))
/Users/pilgrim/diveintopython3/examples\humansize.py # Windows下会添加一个反斜杠。*nix下则是正斜杠'/'
>>> print(os.path.expanduser('~'))
c:\Users\pilgrim
>>> print(os.path.join(os.path.expanduser('~'), 'diveintopython3', 'examples', 'humansize.py'))
c:\Users\pilgrim\diveintopython3\examples\humansize.py

os.path.expanduser()用来将包含~符号(表示当前用户Home 目录)的路径扩展为完整的路径。在任何有 Home 目录概念的操作系统上(包括 Linux,Mac OS X 和 Windows),这个函数都能工作。返回的路径不以斜杠结尾,但是 os.path.join() 并不介意这一点。

示例3: 分割完整路径名,目录名和文件名

os.path 也包含用于分割完整路径名,目录名和文件名的函数:

  1. os.path.split:分割一个完整路径并返回目录和文件名。
  2. os.path.splitext: 分割一个文件名并返回短文件名和扩展名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> pathname = 
'/Users/pilgrim/diveintopython3/examples/humansize.py'
>>> os.path.split(pathname)
('/Users/pilgrim/diveintopython3/examples', 'humansize.py')
>>> (dirname, filename) = os.path.split(pathname) # 将多变量值传给一个元组
>>> dirname
'/Users/pilgrim/diveintopython3/examples'
>>> filename
'humansize.py'
>>> (shortname, extension) = os.path.splitext(filename)
5
>>> shortname
'humansize'
>>> extension
'.py'
示例4: 获取文件元信息

每一个现代文件系统都对文件存储了元信息: 创建时间,最后修改时间,文件大小等等。Python 单独提供了一个的 API 用于访问这些元信息。你不需要打开文件。知道文件名就足够了。

  1. os.stat(文件名):返回一个包含多种文件元信息的对象。
  2. os.st_mtime():获取最后修改时间
  3. os.st_size:返回文件大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> import os 
>>> print(os.getcwd())
c:\Users\pilgrim\diveintopython3\examples
>>> metadata = os.stat('feed.xml') # 调用 os.stat() 函数
>>> metadata.st_mtime # 获取最后修改时间
1247520344.9537716
>>> import time # 用于在不同时间格式中转换
>>> time.localtime(metadata.st_mtime)
time.struct_time(tm_year=2009, tm_mon=7, tm_mday=13,
tm_hour=17,
tm_min=25, tm_sec=44, tm_wday=0, tm_yday=194, tm_isdst=1)
>>> metadata.st_size # 返回文件大小
3070
>>> import humansize
>>> humansize.approximate_size(metadata.st_size)
'3.0 KiB'
示例5:构造绝对路径

当你希望构造一个从根目录开始或者是包含盘符的绝对路径时,可以使用os.path.realpath()函数。

1
2
3
4
5
>>> import os 
>>> print(os.getcwd())
c:\Users\pilgrim\diveintopython3\examples
>>> print(os.path.realpath('feed.xml'))
c:\Users\pilgrim\diveintopython3\examples\feed.xml

time 模块

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模块

glob 模块是 Python 标准库中的另一个工具,它可以通过编程的方法获得一个目录的内容,并且它使用熟悉的命令行下的通配符。 glob 模块使用 shell 风格的通配符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> os.chdir('/Users/pilgrim/diveintopython3/') 
>>> import glob
>>> glob.glob('examples/*.xml')
['examples\\feed‐broken.xml',
'examples\\feed‐ns0.xml',
'examples\\feed.xml']
>>> os.chdir('examples/')
>>> glob.glob('*test*.py')
['alphameticstest.py',
'pluraltest1.py',
'pluraltest2.py',
'pluraltest3.py',
'pluraltest4.py',
'pluraltest5.py',
'pluraltest6.py',
'romantest1.py',
'romantest10.py',
'romantest2.py',
'romantest3.py',
'romantest4.py',
'romantest5.py',
'romantest6.py',
'romantest7.py',
'romantest8.py',
'romantest9.py']

re 模块

正则表达式模块,常见的正则式有:

模式 说明
. 匹配任意字符
^ 匹配字符串开始位置。
$ 匹配字符串结束位置。
\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
2
3
4
5
6
7
8
9
10
11
12
13
>>> s = '100 BROAD ROAD. APT 3'
>>> re.search(r'\bROAD\b',s)
<_sre.SRE_Match object at 0x7f0f85cb17e8>
>>> pattern = '^M{0,3}$' # 匹配0到3个'M'字符
>>> re.search(pattern, 'M')
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MM')
<_sre.SRE_Match object at 0x008EE090>
>>> re.search(pattern, 'MMM')
<_sre.SRE_Match object at 0x008EEDA8>
>>> re.search(pattern, 'MMMM')
>>> re.findall('[0-9]+', '16 2-by-4s in rows of 8') # 找出字符串中出现该模式的所有地方
['16', '2', '4', '8']

也可以先构造一个模式的类然后用它进行查找:

1
2
3
4
>>> s = '100 BROAD ROAD. APT 3'
>>> mypattern = re.compile(r'\bROAD\b')
>>> mypattern.search(s)
<_sre.SRE_Match object at 0x7f141e9cb7e8>
示例二:替换

替换地址中的 ‘ROAD’ 字符串为 ‘RD.’:

1
2
3
>>> s = '100 BROAD ROAD. APT 3'
>>> re.sub(r'\bROAD\b', 'RD.', s)
'100 BROAD RD. APT 3'
说明

在 python 中,比较复杂的是 \ 字符必须被转义,这有的时候会导致 \ 字符传染(想想可能还要对\字符做转义的情况)。

为了解决 \ 字符传染的问题,可以使用原始字符串。这只需要在字符串的前面添加一个字符r。它告诉 python,字符串中没有任何字符需要转义。\t是一个制表符,但 r\t 只是一个字符 \ 紧跟着一个字符 t。

建议:在处理正则表达式的时候总是使用原始字符串。否则,会因为理解正则表达式而消耗大量时间(本身正则表达式就已经够让人困惑的了)。

松散表达式

python 允许你使用松散正则表达式来达到目的。松散正字表达式和普通紧凑的正则表达式有两点不同:

  • 空白符被忽略。空格、制表符和回车在正则表达式中并不会匹配空格、制表符、回车。如果你想在正则表达式中匹配他们,可以在前面加一个\来转义。
  • 注释信息被忽略。松散正字表达式中的注释和 python 代码中的一样,都是以#开头直到行尾。它可以在多行正则表达式中增加注释信息,这就避免了在 python 代码中的多行注释。他们的工作方式是一样的。
示例

判断一串字符是否为合法的罗马数字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> pattern = ''' 
^ # beginning of string
M{0,3} # thousands ‐ 0 to 3 Ms
(CM|CD|D?C{0,3}) # hundreds ‐ 900 (CM), 400 (CD), 0‐300 (0 to 3 Cs),
# or 500‐800 (D, followed by 0 to 3 Cs)
(XC|XL|L?X{0,3}) # tens ‐ 90 (XC), 40 (XL), 0‐30 (0 to 3 Xs),
# or 50‐80 (L, followed by 0 to 3 Xs)
(IX|IV|V?I{0,3}) # ones ‐ 9 (IX), 4 (IV), 0‐3 (0 to 3 Is),
# or 5‐8 (V, followed by 0 to 3 Is)
$ # end of string
'''


>>> re.search(pattern, 'M', re.VERBOSE) # 注意必须传递 re.VERBOSE 参数
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MMMDCCCLXXXVIII', re.VERBOSE)
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'M')
说明

注意,如果要使用松散正则表达式,需要传递一个叫 re.VERBOSE 的参数。就像你看到的那样,正则表达式中有很多空白符,他们都被忽略掉了。还有一些注释信息,当然也被正则表达式忽略掉。当空白符和注释信息被忽略掉后,这个正则表达式和上面的是完全一样的,但是它有更高的可读性。

logging模块

如果你希望得到存储在某处的重要信息或调试信息,以便检查程序是否如期运行时该咋办呢?你如何将这些信息存储在某处呢?

这些可以通过logging模块做到。

示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python
# Filename: use_logging.py

import os, platform, logging

if platform.platform().startswith('Windows'):
logging_file = os.path.join(os.getenv('HOMEDRIVE'),os.getenv('HOMEPATH'), 'test.log')
else:
logging_file = os.path.join(os.getenv('HOME'), 'test.log')
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s : %(levelname)s : %(message)s',
filename = logging_file,
filemode = 'w',
)

logging.debug("Start of the program")
logging.info("Doing something")
logging.warning("Dying now")
输出
1
2
$python use_logging.py
Logging to C:/Users/swaroop/test.log

如果我们查看生成的日志文件 test.log ,它的内容如下:

1
2
3
2008-09-03 13:18:16,233 : DEBUG : Start of the program
2008-09-03 13:18:16,233 : INFO : Doing something
2008-09-03 13:18:16,233 : WARNING : Dying now
说明

我们使用了3个标准库模块 —— os模块与系统交互,platform模块取得平台信息,即操作系统信息。而logging模块用于记录日志信息。

首先,我们通过platform.platform(详见help(platform))返回的字符串检测操作系统类型。如果为windows系统,则分别计算出主驱动器,主目录与文件名,这个文件用于存储相关信息。然后将这三部分合并得到文件的全路径。对于其他平台,我们只需得到用户的主目录就能计算出文件的全路径了。

我们之所以没有简单的使用字符串连接合并这三部分而是利用os.path.join,原因在于这个特殊的函数可以确保路径格式符合特定系统的规范。

之后我们配置logging模块,指示在我们指定的文件中以特殊的格式写入所有信息。最后,我们就能写入信息了,它们可以是调试信息,警告信息甚至是危机信息(critical messages)。

一但程序开始运行,我们就可以检查这个文件以了解程序发生了什么,而用户并不会看到这些信息。

Python之禅

执行import this可以看到《Python 之禅》。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

这里的讨论列出了每个原则的范例。

常用模块不完全列表

Python增强

模块名 用途
re 实现了对正则表达式(regular expression)的支持
array 数组
queue 队列
copy 复制对象
string 定义了一些有用的常量,比如 ascii_lettersascii_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服务第三方开源库

Module of the Week

进一步研究标准库的最好办法就是阅读Python Module of the Week系列,或者Python的官方文档

Comments