从零开始实现一个 pprint 模块

pprint 是Python的标准库之一,它可以用来把Python对象以更加美观、更加可读的格式打印出来。Python标准库的源码是质量非常高的,我们阅读高质量的代码,并模仿这些高质量的代码,是提高编程水平的捷径。

我们今天的任务就是模仿pprint来实现一个我们自己的mypprint模块,从而来学些一个优秀的Python模块都是怎么写的。

pprint能做到什么

我们通过例子来看看pprint能做什么事情呢?
我们都知道普通的print语句可以打印出Python对象,比如下面的例子:

import sys
print sys.path
['', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/Users/potu/Library/Python/2.7/lib/python/site-packages', '/Library/Python/2.7/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC']

你会发现普通print语句的输出格式并不是很容易阅读。我们再来看一下pprint的输出结果:

import pprint
pprint.pprint(sys.path)
['',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload',
 '/Users/potu/Library/Python/2.7/lib/python/site-packages',
 '/Library/Python/2.7/site-packages',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python',
 '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC']

很明显,pprint的输出格式更加的具有可读性。另外,pprint 是 pretty-print 的缩写,所以从名字也可以看出来它就是用来美化输出格式的。

分析pprint的源码

pprint官方文档你可以阅读详细的介绍,并从这里阅读其完整源码pprint source code

我们新建一个名为mypprint.py的Python文件,下面我们就来抄一遍pprint的源码,并做相应的解释。

#  Author:      Fred L. Drake, Jr.
#               fdrake@acm.org
#
#  This is a simple little module I wrote to make life easier.  I didn't
#  see anything quite like it in the library, though I may have overlooked
#  something.  I wrote this when I was trying to read some heavily nested
#  tuples with fairly non-descriptive content.  This is modeled very much
#  after Lisp/Scheme - style pretty-printing of lists.  If you find it
#  useful, thank small children who sleep at night.

"""Support to pretty-print lists, tuples, & dictionaries recursively.

Very simple, but useful, especially in debugging data structures.

Classes
-------

PrettyPrinter()
    Handle pretty-printing operations onto a stream using a configured
    set of formatting parameters.

Functions
---------

pformat()
    Format a Python object into a pretty-printed representation.

pprint()
    Pretty-print a Python object to a stream [default is sys.stdout].

saferepr()
    Generate a 'standard' repr()-like value, but protect against recursive
    data structures.

"""

首先第1行到第9行之间的是文件注释,可以把作者写进去。第11行到第35行之间是一个多行字符串,你可能会觉着奇怪,比如str = 'Hello world',我们说Hello world这个字符串赋值给了str这个变量。而上面这个多行字符串没有赋值给任何一个变量,为什么要定义这样一个没有赋值给任何一个变量的字符串呢?

其实,这个字符串是模块的详细解释,可以使用内置函数help(pprint)打印出这个字符串。