14.2. IPython帮助和文档

如果本章内容让你仅挑选一节来阅读的话,请你读这一节:本节讨论的工具对于其日常工作中使用IPython有着极大的帮助。

当一个技术人员在计算机问题上被朋友、家人或同事请求帮助的时候,大多数情况下,直接知道答案和知道如何迅速的找到答案是一样的。在数据科学领域也是同样的情况:可以搜索到的网络资源例如在线文档、邮件列表以及StackOverflow上的问题答案都含有丰富的信息,特别是这方面的内容你之前已经自己搜索过答案的情况下。作为一个高效的数据科学人员,并不需要你能记得每一个可能场景需要用到的工具或命令,更重要的是你能在不知道答案的情况下迅速地找到信息,无论是通过搜索引擎还是其他的方法。

IPython/Jupyter中最优秀的特性就是弥合了用户与文档以及搜索它们的方式之间的鸿沟,使得用户能够提高他们的工作效率。网络搜索依旧是查找复杂问题答案不可或缺的方式,但是IPython本身就已经提供了许多相关的信息。很多的问题通过在IPython中输入几个字符就可以找到答案,例如:

  • 我该如何调用这个函数?它需要怎样的参数和选项?

  • 这个Python对象的源码是怎样的?

  • 我载入的这个包里面有哪些内容?这个对象有哪些属性或方法?

这里我们会讨论IPython中获取这些信息的方式,即,?符号来查看文档,??符号来查看源码以及使用制表符进行自动补全。

14.2.1. 使用?获取文档

Python语言和数据科学生态系统始终将用户需求放在重要位置,其中一大部分就是获取文档。每一个Python对象都含有一个字符串的说明,称为文档字符串,它是该对象的简要概括以及如何使用的信息。Python中有內建的help()函数能够获取这些信息并打印出来。例如,要获得内建函数len()的文档字符串,你可以这样做:

In [1]: help(len)
Help on built-in function len in module builtins:

len(...)
    len(object) -> integer

    Return the number of items of a sequence or mapping.

取决于你的解释器,这个信息可能出现在内嵌的输出文本中,也可能出现在一个弹出的窗口中。

因为查找一个对象的帮助是如此普遍和有用,IPython引入了 ? 符号来简化 help() 內建函数的操作:

In [2]: len?
Type:        builtin_function_or_method
String form: <built-in function len>
Namespace:   Python builtin
Docstring:
len(object) -> integer

Return the number of items of a sequence or mapping.

这种写法基本上可以应用于任何对象,包括对象的方法:

In [3]: L = [1, 2, 3]
In [4]: L.insert?
Type:        builtin_function_or_method
String form: <built-in method insert of list object at 0x1024b8ea8>
Docstring:   L.insert(index, object) -- insert object before index

或者对象本身,返回的将会是这种类型的文档:

In [5]: L?
Type:        list
String form: [1, 2, 3]
Length:      3
Docstring:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items

更为重要的是,这个符号还能应用在你自己创建的对象和其他对象上,下面我们来定义一个很简单的带有docstring的函数:

In [6]: def square(a):
  ....:     """Return the square of a."""
  ....:     return a ** 2
  ....:

从上例中我们可以看到,我们可以在函数的第一行放置一个字符串来实现docstring。因为docstring通常是多行的文本,习惯上我们会使用Python的三引号记号来代表它。

下面我们就可以使用?符号来查找这个docstring:

In [7]: square?
Type:        function
String form: <function square at 0x103713cb0>
Definition:  square(a)
Docstring:   Return the square of a.

因为有了这么简便的查找docstring的方式,因此这也是你需要养成在每个对象中加入docstring文档习惯的原因之一。

14.2.2. 使用??获取源代码

因为Python非常容易阅读,所以你也可以通过阅读你感兴趣的对象的源代码来获取你需要的帮助信息。IPython提供了使用双问号??的方式获取对象源代码:

In [8]: square??
Type:        function
String form: <function square at 0x103713cb0>
Definition:  square(a)
Source:
def square(a):
    "Return the square of a"
    return a ** 2

对于像这样简单的函数来说,双问号语法可以快速地给你提供对象的内部详细实现机制。

如果你经常使用??,你会发现有些情况下??并不能显示任何源代码:这主要是因为某些对象并不是用Python语言实现的,而是使用C或者其他一个需编译的语言实现的。如果出现这种情况,那么??的作用就与?一致。你可以在很多Python的內建对象和类型中发现这个问题,例如前面的len()函数:

In [9]: len??
Type:        builtin_function_or_method
String form: <built-in function len>
Namespace:   Python builtin
Docstring:
len(object) -> integer

Return the number of items of a sequence or mapping.

使用???可以很快的让你查找到Python函数或模块的文档或代码,这是很强大的一个功能。

14.2.3. 使用Tab补全来探索模块

IPython的另一个有用的功能是使用制表符tab来进行自动补全以及对Python对象、模块和命名空间进行探索。在下面的例子中,我们将会使用<TAB>来表示需要敲击制表符键的地方。

对象内容的Tab补全

所有的Python对象都有着自己的属性和方法。就像Python有着內建的help函数一样,Python也有一个內建的dir函数,会列示出对象的属性和方法的列表,但是在IPython中,使用tab补全会更加简单。查看一个对象所有可用的属性和方法,你需要输入对象的名称和后面的一个点(.),然后点击Tab键:

In [10]: L.<TAB>
L.append   L.copy     L.extend   L.insert   L.remove   L.sort
L.clear    L.count    L.index    L.pop      L.reverse

如果希望缩小列表范围,你可以输入属性或方法的头几个字母,自动补全会找到能匹配这些字符的属性和方法:

In [10]: L.c<TAB>
L.clear  L.copy   L.count

In [10]: L.co<TAB>
L.copy   L.count

如果能匹配的属性或方法只有一个,那么键入Tab的时候,IPython会自动将整个属性或方法的名称补充完整。例如,下面的输入会最终产生L.count

In [10]: L.cou<TAB>

虽然Python并没有明确强制定义公有的和私有的属性和方法,但是习惯上,如果属性或方法名称是以下划线开头的话,就被认为是私有的。为了是列表清晰,默认的情况下,tab补全列表会忽略下划线开头的属性和方法,但如果你需要显示它们,你可以在点后面明确输入一个下划线来展示:

In [10]: L._<TAB>
L.__add__           L.__gt__            L.__reduce__
L.__class__         L.__hash__          L.__reduce_ex__

为了简洁起见,我们这里只展示了前面两行内容。大部分这种命名都属于Python特殊的双下划线方法。

当载入模块是使用tab补全

Tab补全也同样适用于载入模块的时候。下面我们试着从itertools包中查找所有以co开头的内容:

In [10]: from itertools import co<TAB>
combinations                   compress
combinations_with_replacement  count

同样的,你也可以使用tab补全方式来查看系统中所有可以被载入的模块(根据你当前Python进程的上下文不同,第三方包和模块的可见性也会不同,因此列示的内容也会有差别):

In [10]: import <TAB>
Display all 399 possibilities? (y or n)
Crypto              dis                 py_compile
Cython              distutils           pyclbr
...                 ...                 ...
difflib             pwd                 zmq

In [10]: import h<TAB>
hashlib             hmac                http
heapq               html                husl

(当然为了简洁起见,这里肯定没有列出所有399个可用的包和模块出来)

Tab补全进阶:通配符匹配

Tab补全对于你知道对象或属性的头几个字母的情况下非常有效,但是如果你只记得中间或末尾处的字符时,tab补全就无法发挥了。 对于这种情况,IPython提供了一种使用通配符*来匹配内容的方法。

例如,我们可以使用它列出任何末尾为Warning的对象:

In [10]: *Warning?
BytesWarning                  RuntimeWarning
DeprecationWarning            SyntaxWarning
FutureWarning                 UnicodeWarning
ImportWarning                 UserWarning
PendingDeprecationWarning     Warning
ResourceWarning

这里的*号能匹配任何字符串,包括空字符串。

类似的,如果我们希望找到所有名称中含有find字符串的对象内容,我们可以这样做:

In [10]: str.*find*?
str.find
str.rfind

作者发现这种通配符的方式对于在一个新的包中找到你想要的内容,或者你忘记了一个熟悉的包中的内容是特别有效。