第二章

在本章中,我们将详细学习IPython相对于Python控制台带来的多种改进。特别的,我们将会进行下面的几个任务:

  • 从IPython中使用系统shell以在shell和Python之间进行强大的交互式操作;
  • 在甚至不看一个新的Python包的文档的情况下,使用动态自省功能探索Python对象;
  • 在IPython中轻松调试、测度你的代码;
  • 学习如何使用Notebook来提升你交互使用Python的能力。

扩展的shell

IPython不仅是一个扩展的Python控制台,它还提供了多种方式使你可以在一个Python交互会话中无需推出控制台就能和操作系统进行交互。IPython的shell功能不是打算着要代替Unix的shel,因为IPython停工的shell功能相对来说少很多。然而,依然可以在Python会话中反问文件系统或偶尔在IPython中调用系统命令。甚至,IPython提供了很多有用的魔法命令,极大地提升了工作效率,大大减少了交互会话中的重复输入。

访问文件系统

这一节我们见展示在IPython中怎样从互联网下载压缩文件并将其解压,按层次访问文件系统,打开文本文件。为了实现这些操作,我们将要使用来自真是社交网站Facebook中数百个匿名用户的真实数据样例(这些匿名用户支援匿名分享他们的数据给计算机科学家进行研究)。这些基于BS协议的数据由来自斯坦福大学的SNAP项目免费提供。(HTTP://SNAP.STANFORD.EDU/DATA

【下载样例代码:可以从作者的网站http://ipython.rossant.net下载本书的样例代码】

首先,我们需要从作者的网站下载包含我们需要的数据的ZIP文件。我们是用Python自带的urllib2模块进行文件下载,zipfile模块进行文件解压。

In [1]: import urllib2, zipfile
In [2]: url = 'http://ipython.rossant.net/'
In [3]: filename = 'facebook.zip'
In [4]: downloaded = urllib2.urlopen(url + filename)

这里我们将http://ipython.rossant.net/facebook.zip 文件下载到内存中,接着再将其保存到硬盘。

现在,我们在当前目录下创建一个名为data的文件夹,进入这个文件夹。$符号允许我们在系统或魔法命令中使用Python变量。

In [5]: folder = 'data'
In [6]: mkdir $folder
In [7]: cd $folder

这里,mkdir是一个将模仿命令重定向到一个shell命令的特殊IPython alias 。可以使用%alias命令获得aliaes列表。我们将会把刚下载的文件保存在这个文件夹内,并在当前文件夹内将其解压(在第8行,我们直接将facebook.zip文件保存在了data文件夹内,在第9行我们使用zip的extractall方法和一个ZipFile对象对文件进行解压操作)

In [8]: with open(filename, 'wb') as f:
            f.write(downloaded.read())
In [9]: with zipfile.ZipFile(filename) as zip:
            zip.extractall('.')

让我们浏览一下我们刚才下载了什么

In [10]: ls
facebook facebook.zip
In [11]: cd facebook
In [12]: ls
0.circles 0.edges [...]

在这个示例中,每个号码标记一个Facebook用户(被称为ego用户)。.edges文件包含他的社交图,也就是说每一个节点都是他的一个朋友,如果两个ego互为朋友,那么这两个用户就会被链接在一起。这张图被存储在为一个edges列表,文件中每一行包含两个链接的结点,这两个结点由空格进行分割。.circles文件包含手工创建的朋友列表,也就是,从用户的观点看拥有共同属性的朋友团体。

最后,我们将当前的facebook目录保存问一个书签,这样后面我们就能很容易地进入到这个目录。

In [13]: %bookmark fbdata

之后再使用相同的IPython配置文件进行的会话中,我们就可以输入cd fbdata进入到这个目录了。-l-d选项允许显示所有定义的书签和删除一个指定的书签。输入`%bookmark?显示所有的选项列表。这个魔法命令在我们需要在不同文件夹下进行切换的时候极其有用。

IPython中另一个和访问相关的功能是Tab补全。如果我们按下Tab键,IPython将会自动我们正在输入的文件或文件夹。如果有多种可选项,IPython将会可选项列表展示出来。对于文件名他也是有效的,例如,在Python内建函数open中:

In [1]: pwd
/home/me/data/
In [2]: cd fa<TAB>
/home/me/data/facebook/
In [2]: cd facebook
In [3]: with open('0<TAB>
0.circles 0.edges
In [3]: with open('0.edges', 'r') as f:
print(f.readline())
236 186

在IPython中使用系统shell

我们可以在IPython直接使用系统shell,并获取结果作为一个Python字符串列表。为了实现这种功能,我们需要使用作为shell命令的前缀。例如,假设我们在使用Unix系统

In [1]: cd fbdata
/home/me/data/facebook
In [2]: files = !ls -1 -S | grep edges

Unix的 ls -l -S命令将会将当前目录的文件以文件大小递减排序一行一个文件列出,管道`| grep edges```只过滤包含edgesdes的文件(这些文件包含不同网络的社交图)。接着,Python变量files将存储有所有文件名字的列表,就像下面示例这样:

In [3]: files
Out[3]: ['1912.edges',
'107.edges',
[...]
'3980.edges']

我们也能在系统命令中使用Python变量,针对单个变量使用$语法,针对Python表达式使用{},就像相面这样:

In [4]: !head -n5 {files[0]}
2290 2363
2346 2025
2140 2428
2201 2506
2425 2557

head -n5 {files[0]}命令显示files列表中第一个文件的前五行,也就是说,数据集中.edges文件最大的五行。

如果我们发现我们总是一遍又一遍的重复使用相同的命令,我们可以使用%alias魔法命令创建alias来保存一些重复的输入。如,在下面的示例中,我们创建了一个用于显示一个单列(-1)带有他们体积(-hs)的被称作largest的alias,这些文件被一个指定的字符串过滤(grep)并递减排序(-S)。

In [5]: %alias largest ls -1sSh | grep %s
in [6]: largest circles
6.0K 1912.circles
4.0K 1684.circles
[...]

注意一下地五行%s处为largest alias设置的占位符,在使用这个alias时,他将会被任意给定的参数所替代(就像第六行那样)。

注意,这个alias默认情况下在下一次重启IPython会话时是无效的。我们需要使用%store魔法命令将其保存,如下:

In [7]: %store largest
Alias stored: largest (ls -1sSh | grep %s)

除此之外,在以后的会话中如果我们想要覆盖一存储的alias或变脸,需要使用%store -r

扩展的Python控制台

我们现在将要探索IPython控制台中与Python相关的功能。

探索历史

IPython将会记录我们贯穿整个会话的历史输入。由于在使用IPython几月甚至几年后这个输入历史会变得很大,这里有一些方法可以快速浏览他。

首先,我们可以在IPython代理中的任意时刻使用上下键线性访问我们最近的输入历史。如果我们在按上下键之前输入写什么东西,我们只访问浏览与我们已输入的内容相匹配的。按Ctrl+R开启一个代理来搜索我们在此代理中所有输入。

%history魔法命令(简短的%hist也行)接收多个何时的选项来显示输入历史中我们感兴趣的一部分。默认情况下,%history显示我们在当前会话中的所有输入。我们可以使用一个简单的语法来指定一个特定的范围内的内容,如,hist 4-6 8 从第4行到第6行和第八行。我们也可以使用hist 243/4-8(在243次绘画中的第4行到第八行)的语法形式来显示之前会话中的历史输入。最后,我们使用%hist ~1/7这样的语法来对与当前会话相关的会话进行标号.(该命令显示之前会话的第七行)。

除了显示输入,其他有用的选项还有% historyinclude - o显示输出,;- n,显示行号,- f,将历史保存到一个文件中; - p,显示典型的>>>提示。例如,自动从历史输入输出中创建一个doctest文件是很有用的。此外,-g选项允许 指定一个字符串过滤历史输入(如grep)。考虑下面的例子:

In [1]: 2 + 3
Out[1]: 5
In [2]: _ * 2
Out[2]: 10
In [3]: %hist -nop 1-2
1: >>> 2 + 3
5
2: >>> _ * 2
10

在这个例子中,我们以带行号的形式显示的前两行的历史输入,输出和默认的Python提示符。

最后一个相关的命令是%store,这个命令用于保存未来交互式会话中使用的任何Python变量的内容。%store name 命令保存name变量,% - d name用来删除它。使用%store -r 命令来覆盖原来已存储的变量。

Python代码的导入与导出

在下一节中,我们将首先看到如何从Python脚本导入代码到交互式控制台,然后如何从历史导出代码到一个外部文件。

在IPython中导入代码

在IPython导入代码的第一种可能性是从文件中复制代码并粘贴到IPython。使用Ipython控制台时 ,可以使用%paste 魔法命令 导入和执行剪贴板中包含的代码。IPython自动规整代码并删除每行开始的>、 +字符,且允许直接从电子邮件粘贴diff和doctest文件。

此外,% run魔法命令可以在控制台中执行Python脚本,默认情况下,代码运行在一个空的命名空间。这意味着在互动式命名空间中定义的任何变量在执行脚本中都是不可用的。然而,在脚本代码执行结束后,脚本中定义的变量都会导入到交互式命名空间中。这是在探索脚本执行结束时所有变量的状态时是很方便的。这种行为可以采用-i选项进行改变,开启此选项将会采用交互式的命名空间来执行脚本代码。交互名称空间中在脚本运行前定义的变量在脚本中亦可用。

例如,我们可以写一个列出Facebook的data文件夹下所有ego(自我)标识符的/home/me/data/ egos.py脚本。由于每个文件名的形式是< egoid >.<extension>(译者注:<自我ID>.<后缀扩展名>`)我们列出所有的文件,删除扩展名,并将所有唯一标识符进行列表排序。脚本应该包含以下代码:

import sys
import os
# we retrieve the folder as the first positional argument
# to the command-line call
if len(sys.argv) > 1:
folder = sys.argv[1]
# we list all files in the specified folder
files = os.listdir(folder)
# ids contains the sorted list of all unique idenfitiers
ids = sorted(set(map(lambda file: int(file.split('.')[0]), files)))

这里解释一下最后一行做了什么。lambda函数采用一个< egoid >.<extension>模板的文件名作为参数,返回以整数形式返回egoid ID。这里使用了字符串类型带有的split方法来根据给定的字符来切分一个字符串并返回一个子串列表。列表的第一个元素就是egoid这一部分。Python的自带函数map将这个lambda函数应用到所有的文件名。set函数将这个列表转化为一个集合对象,从而消除重合而只保存唯一的标识符(因为两种不同的扩展名下每个标识符出现了两次)。最后,sorted函数将集合对象以递增的形式转换成一个列表。

假设IPython当前所在的目录是 /home/me/data,下面就是执行这个脚本所需的命令:

In [1]: %run egos.py facebook
In [2]: ids
Out[2]: [0, 107, ..., 3980]

egos.py脚本中,文件名facebook是由命令行参数获得的,就像在一个标准的命令行Python脚本中使用的sys.argv[1]。当这个脚本执行后,脚本中定义的ids变量在交互式命名空间中就变成可用的了,这个变量包含唯一标识符列表。

如果我们不给脚本提供一个文件名作为参数,就会出现下面这种状况:

In [3]: folder = 'facebook'
In [4]: %run egos.py
NameError: name 'folder' is not defined
In [5]: %run -i egos.py
In [6]: ids
Out[6]: [0, 107, ..., 3980]

由于folder是未定义的,在第四行抛出了一个异常。如果我们想要让这个脚本使用folder这个在命名空间定义的变量,我们需要使用-i选项。

【探索性研究中的交互工作流程:探索性研究或数据分析中的标准工作流程是在一个或多个Python模块中实现算法编写一个脚本来执行整个过程。这个脚本可以使用%run命令来运行,并允许对脚本变量的进一步探索。这个交互式过程牵涉到文本编辑器和IPython控制台之间的切换。一个更现代和使用的方法是使用将在“使用IPython Notebook”这一章节中看到的IPython Notebook,】

导出代码到文件

%run魔法命令可以从文件导入代码到控制台,%edit魔法命令的功能恰恰与它相反。默认情况下,%edit打开系统的文本编辑器当我们关闭文本编辑器是就执行代码。如果我们给%edit提供一个参数,这个命令将会携带我们提供的代码尝试打开文本编辑器。。参数可以是下面这样:

  • 一个Python脚本文件名
  • 包含Python代码的字符串
  • 一个行号范围,就像%history语法那样
  • 任意Python对象,IPython尝试打开定义过这个对象的文件

旧话重提,IPython的多行编辑的更为现代和强大的方式是使用IPython Notebook。

动态内省

IPython提供针对动态探查明明空间中的Python对象提供了多种特性

Tab补全

在任何时候,我们都可以在控制台按下Tab键让IPython补全或建议一个匹配我们之前键入的名称或命令。特别是这允许动态检查任何Python对象的属性和方法。

Tab补全对于交互式命名空间中、模块中、和当前目录下的文件中的变量也是有效的。默认状态下,以下划线_开头的变量不会显示,因为以下划线为前缀在Python中表示是私有变量。然而,可以在按下Tab前输入下划线强制IPython输出所有的私有变量。

NetworkX的Tab补全示例

这里我们通过Tab补全来找出怎样使用NetworkX包来加载和操纵一个图。这个包一般情况下用来解决图问题。让我们运行下面的命令来到如这个包:

In [1]: import networkx as nx

为了寻找打开一个图的合适选项,我们可以寻找以read为前缀的方法,如下:

In [2]: nx.read<TAB>
nx.read_adjlist nx.read_dot nx.read_edgelist [...]

由于.edges文件包含一个边的列表,我们尝试一下下面的命令(假设我们现在在fbdata文件夹内)

In [3]: g = nx.read_edgelist('0.edges')

既然图g已经被加载,我们可以探索这个新对象所提供的方法,如下:

In [4]: g.<TAB>
g.add_cycle [...] g.edges [...] g.nodes
In [5]: len(g.nodes()), len(g.edges())
Out[5]: (333, 2519)

0 ego用户拥有333个好友,这些朋友之间有2519个连接。

让我们在探索一下这个图的结构。在这个途中任意两个用户之间的链接情况是怎样的呢?小世界图理论预测在一个社会网络中任意两个人之间大约有六个间隔链接。这里我们可以计算这个图的半径和直径,也就是两个结点之间的最小和最大路径长度。Tab补全显示NetworkX保重有一个radius方法。所以,我们尝试一下下面的命令:

In [6]: nx.radius(g)
[...]
NetworkXError: Graph not connected: infinite path length

我们的图被提示没有链接。这是因为半径和直径没有被明确的定义。要解决这个问题,我们可以找一个图的连接组建。如下:

In [7]: nx.connected<TAB>
nx.connected nx.connected_component_subgraphs [...]

第二个建议看起来是一个好的选择(因此,创建包是一个很好的名称是十分重要的!)结果如下:

In [8]: sg = nx.connected_component_subgraphs(g)
In [9]: [len(s) for s in sg]
Out[9]: [324, 3, 2, 2, 2]
In [10]: sg = sg[0]
In [11]: nx.radius(sg), nx.diameter(sg)
Out[11]: (6, 11)

共有5个连接组件;我们采用其中最大的一个并计算他的半径和直径。因此,任意两个好友之间的连接少于11层,有一个好友和其他好友之间少于6个连接。

自定义类的Tab补全

如果我们定义自己的类,我们就可以定制他们的实例和IPython Tab补全的工作形式。我们所要做的重写__dir__方法来返回属性列表。示例如下:

In [12]: class MyClass(object):
def __dir__(self):
return ['attr1', 'attr2']
In [13]: obj = MyClass()
In [14]: obj.<TAB>
obj.attr1 obj.attr2

这个特性在一个实例的有趣属性是动态定义的一些场景下是非常有用的。

源代码的自省

IPython也能狗显示一个变量的内部信息,尤其是定义在一个文件的源代码中的变量。首先,在一个变量的前面或后面输入可以打印关于它的有用信息。输入??能获得更为详细的信息,尤其是一个定义在文件中的对象的源代码。

除此自外,很多魔法命令能够显示特定的变量信息,比如,函数的源代码(%psource)或文件的源代码(%pfile),文档字符串(%pdoc)、定义头部(%pdef

%pfile魔法命令也可以接收一个Python文件名,将会以代码高亮的形式打印文件的内容。凭借这个功能,IPython可以充当带有的代码阅读器。

使用交互式调试器

对于我们中的大多数,调试时编程工作中很重要的一部分。IPython使得调试一个脚本或者一个完整的应用变得很是方便。它提供了对Python调试器的增强版的访问。

首先,当我们遇到异常时,我们可以使用那个%debug魔法命令在异常抛出点启动IPython调试器。如果我们激活了%pdb魔法命令,调试器在异常的附近自动启动。我们也可以以ipython --pdb的形式启动IPython来达到同样的效果。最后,我们可以使用%run -d命令来在调试器的控制下运行整个脚本。这个命令在执行指定的脚本时,会在第一行设置断点,所以我们能控制脚本的执行流程。我们也可以专门指定在哪里设置断点;输入%run -d -b29 script.py将会在script.py的第29行暂停执行。我们首先需要敲击c来开始脚本的运行。

当调试器启动之后,提示符将变成ipdb>。程序的执行将会在代码的断点处暂停。我们可以使用w命令来显示调试器暂停处所在的栈回溯行和位置。在这一点,我们可以访问任意本地变量且能精准地控制我们想要怎样继续运行。在调试器中有几种命令可以进行调试跟踪:

  • ①u/d 上下调用栈
  • ②s步进下一个语句
  • ③n执行直到当前函数的next line
  • ④r 执行知道当前函数返回
  • ⑤c执行知道下一个断点或异常

其他的一些有用的命令:

  • ①p 评估并打印任何描述
  • ②a 获得当前函数的参数
  • ③ !前缀 在调试器模式下执行任意Python代码

完整的命令列表可以在Python的pdb模块的文档中找到

交互式测度与分析

唐纳德说:过早的优化是一切邪恶的根源。

这句话所要表达的意思是优化应该只发生在绝对必要的情况下,如果代码已被彻底地分析好,你就知道代码的哪些部分需要优化。IPython使得这个基准测试和分析过程十分简单。

控制命令的执行时间

首先,%timeit魔法命令功能使用Python的timeit模块来评估任意Python语句的执行时间。如果我们已经定义了一个函数fun(x),%timeit fun(x) 会执行这个命令多次,并返回平均执行时间。调用的次数自动确定的;每n次执行会执行r次循环。这些数码据可以通过 -r-n选项专门指定给%timeit 。同时,我们也可以使用%run -t 命令来评估一个脚本的执行时间。

在接下来的示例中,我们将计算sg的centre,也就是偏心距等于半径的节点的集合(ego的社交圈中和其他所有好友都有良好连接的好友),并评估其运行时间:

In [19]: %timeit nx.center(sg)
1 loops, best of 3: 377 ms per loop
In [20]: nx.center(sg)
Out[20]: [u'51', u'190', u'83', u'307', u'175', u'237', u'277', u'124']

我们可以看到上面的示例花费了337毫秒来计算sg的centre。centre函数被调用了3次(19次输出中最好的三次),最短执行时间已经被自动的选出(最早的一次执行需要更长的时间,比如需要惊醒Python模块的导入)。

分析一个脚本

为了获得一个程序的执行时间的更多信息,我们可以可以在一个profiler(事件探查器)的控制下运行它,就像Python 的profile模块所提供的功能那样。分析是一个复杂的话题,我们只准备展示一个基本的使用样例。更多关于profile模块的细节能够在Python的官方文档中找到。

为了能在一个profiler(事件探查器)的控制下运行一个脚本,我们可以使用IPython中的%run -p或等价的%prun模仿命令来运行它。

这里我们将写一个计算图的中心的脚本,而不是使用NetworkX自带的centre函数。让我们创建一个centre.py脚本,代码如下:

import networkx as nx
g = nx.read_edgelist('0.edges')
sg = nx.connected_component_subgraphs(g)[0]
center = [node for node in sg.nodes() if nx.eccentricity(sg, node) == 
nx.radius(sg)]
print(center)

现在,使用下面的命令运行它来评估其运行时间。

In [21]: %run -t center.py
[u'51', u'190', u'83', u'307', u'175', u'237', u'277', u'124']
IPython CPU timings (estimated):
User : 128.36 s.

这个脚本运行所花费的时间超过了两小时;这看起来十分糟糕!我们可以使用%run -p centre.py命令启动事件探查器来什么使其花费了那么长的时间。

事件探查器了每一个直接或间接使用到的Python的调用的细节。例如,cumtime那一列打印出了每一个函数的积累花费时间。从上面的示例中我们很容易发现eccentricity和radius是主要的瓶颈,因为他们分别调用了648次和324次!再次仔细查看代码,我们确实做了一些很愚蠢的事——我们在一个循环中重复调用了他们。细思量一下,我们是可以讲这些的进行缓存的。让我们在centre2.py这个脚本中做一些修改:

import networkx as nx
g = nx.read_edgelist('data/facebook/0.edges')
sg = nx.connected_component_subgraphs(g)[0]
# we compute the eccentricity once, for all nodes
ecc = nx.eccentricity(sg)
# we compute the radius once
r = nx.radius(sg)
center = [node for node in sg.nodes() if ecc[node] == r]
print(center)

这里,在循环之前我们计算的所有结点的偏心距与一次eccentricit调用,我们只计算一次曲线的半径。让我们检查一下该改进的脚本的表现,使用下面的名利执行脚本:

In [23]: %run -t center2.py
[u'51', u'190', u'83', u'307', u'175', u'237', u'277', u'124']
IPython CPU timings (estimated):
User : 0.88 s.

通过修改,我们的计算时间只有不到1秒而不是两分钟!当然,这只是一个短小的示例,想想这类错误对于任何程序员都可能在长期项目中出现。也许之痛股哦阅读代码不是那么容易找到瓶颈所在。最好的方式还是使用事件探查器,IPython使这样的任务变得相当简单。

【使用逐行事件探查器:对于更细粒度的分析,我们可以使用一个逐行 探查器。这个工具可以分析程序员选择的一组函数中每一行所花费的时间。在Python中line_profiler包就是干这个的。这个函数有一个@profile装饰器进行声明. 他的用处是比IPython事件探查器更为直接, 我们邀请对其感兴趣的读者访问 http://packages.python.org/line_profiler/. 获得更多细节。我们在第六章还会再提到它】

使用IPython Notebook

IPython Notebook的使用在Python社区中越来越普遍,特别是在科学研究与教育领域。它给IPython带来了一个强大的HTML用户界面,以JSON格式在记事本文件保存整个交互式会话的方法。后者的功能带来了科学研究中的一个重要特点——可重复性的互动计算。Notebook在浏览器中运行,不仅可以包含Python代码,而且可以文本标记语言,如markdown,以及图片,视频,或丰富的媒体内容。Notebook可以转换成其他格式,如Python脚本,HTML,PDF。很多课程,博客帖子,和书籍都是用Notebook写的。

还有一个基于Qt而不是HTML的类似notebook的富IPython前端。你可以在http://ipython.org/ipython-doc/ stable/interactive/qtconsole.html找到关于它的更多信息

安装

IPython notebook服务器程序需要很多依赖项。如果你使用的是一个完整集成的发行版或从预先编译好的二进制包安装的,就无需再进行其他的操作。如果你是手动逐步安装的IPython,你就需要安装PyZMQ和Tornado。PyZMQ是一个ZMQ socket库的封装包,Tornado是notebook使用的Python HTTP 服务器程序库。你可以使用easy_install、pip或源码安装他们。

otebook仪表盘

在shell中输入ipython notebook是否一切所需要的都正确安装。如果正确安装的话,此命令将在默认在本地8888端口启动一个web服务。在浏览器打开http://127.0.0.1:8888/来检查你是否看到下面的页面(译者注:127.0.0.1是IPV4本地回环地址;页面根据安装的版本和形式的不同稍有差异): 此处输入图片的描述

浏览器对notebook的兼容性:IPython notebook可以正常兼容Chrome、Safari、Firefox6及其以后的版本,IE10及其以后的版本.这些浏览器支持notebook使用的WebSocket协议(译者注:WebSocket协议是HTML5中提出通信协议)

上面截图页面就是notebook的仪表盘;它列出了我们启动ipython notebook的那一目录下的所有notebook。IPython notebook文件以.ipynb作为后缀,是JSON格式组织的结构化数据文本文件。 文件包含交互绘画中的输入和输出和Ipython内部使用的元数据。

在线浏览notebook:IPython notebook可以通过IPython Notebook Viewver(http://nbviewer.ipython.org/)在线浏览和分享 。

让我们点击页面右上角的New Notebook按钮创建一个新的notebook。

工作框/小格

我们现在已经进入一个notebook。用户工作区域现在是很干净的。页面的最上方是提供操作和命令的菜单和工具箱。其下方是工作区,默认情况下是一个输入框。输入框的一个很重要的特性是按下Enter键不会运行框内的命令而是会添加新的一行。在输入框/小格内编写代码就如同在文本编辑器中一般,这一点适和传统IPython控制台的工作方式是不一样的。

就像下面截图那样输入命令,注意Tab补全是怎么实现的: 此处输入图片的描述

一个输入框/小格内的命令可以由两种方式运行。Shift+Enter小格内的所有代码将会在当前的IPython交互命名空间中运行。运行结果将会立即在输入框下面的输出区域显示,且一个新的输入框会出现。Ctrl+Enter只会显示运行结果而不会创建新的输入框。一般情况下,如果我们只是想验证一写Python语句且不想保存这些框内的内容到notebook文件中,我们会采用这种方法进行快速的插入式实验。(尽管我们之后可以随时删除notebook中这些小格内容)

最后,一个notebook包含输入和输出的线性序列的输入和输出, 这些输入和输出代表一个连贯的可重复交互式会话。典型地,单个小格 包含一组指令,它执行需要连续几个命令的高级别动作。

界面接口提供对小格编辑、删除】分隔、合并等操作的命令。这些命令可以使用菜单、工具箱中的按钮或快捷键实现。我们可以使用Ctrl+M再加H显示全部的快捷键列表。notebook的大多数命令的快捷键是Ctrl+M后面再加上一个不同的单字母组成的序列。

小格魔法(Cell magics) 小格魔法是应用于整个小格而不是单行的特定魔法命令。他们以%%带替%为前缀,既可以在IPython控制台使用,也可以在Ipython notebook使用。可以使用%lsmagic命令获取全部的小格魔法命令。两个重要的命令是:从IPython执行多个系统shell命令的%%!;创建一个文本文件的%%file。示例如下:

In [1]: %%file test.txt
Hello World!
Writing test.txt
In [2]: %%!
more test.txt
Out[2]: ['Hello World!']

管理notebooks

我们可以随时点击菜单中的Save按钮或Ctrl+S(Ctrl+M+S)快捷键命令来保存我们正在使用的notebook。默认情况下,notebook文件名是Untitle0,但是我们可以使用菜单File下的rename(重命名)命令来给对其进行行重命名。

我们可以将Python脚本文件拖进IPython仪表盘来创建一个新的notebook。这将会创建一个和我们的脚本文件相同文件名的notebook,只不过后缀是.ipynb。一个notebook可以下载为一个Python脚本或.ipynb文件。

多媒体和富文本编辑

notebook一个很有用的功能是可以使用Markdown语法在小格内进行文本编辑。(详细描述参考http:// daringfireball.net/projects/markdown/syntax)。可以使用简单的语法实现如加粗、倾斜、置标题等编辑功能。为了要在小格内使用Markdown语法需要使用Cell >命令进行模式转换。

接下来我们就可以使用Markdown语法来编辑我们的文本了。如果我们使用Shift+Enter快捷键,文本将会自动格式化,双击它就可以继续编辑。下面的截屏展示了Markdown代码和其格式化后的文本: 此处输入图片的描述

图形/图像绘制

让我们使用我们的社交网络的示例来展示notebook的绘图功能。我们将绘制sg这个图。首先,我们需要使用ipython notebook --pylab inline命令来启动notebook。这个选项的细节将会在第四章详细讲述。它允许我们在notebook中使用Matplotlib插入图像。NetworkX也提供了一些基于Matplotlib的绘图命令。在接下来的示例中,我们使用draw_networkx函数来绘制图sg,同时添加一些参数来增强图像的可读性。(完整的选项列表可以在networkX文档网站找到):

nx.draw_networkx(sg,node_size=15),
edge_color='y',with_labels=False,alpha=.4,linewidth=0)

此处输入图片的描述

总结

我们现在对IPython简化和扩展我们每天的编程工作中与IPython的交互操作的功能有了一个更广阔的认识。IPython提供的history、动态自省等功能极大的简化于方便了我们的交互式操作。notebook也为我们提供了一种现代的IPython使用方式以使得其应用范围得到更大的扩展。如仅仅记录一个交互会话、创建一个编程课程、一个展示、甚至编写一本书。

然而,IPython所提供的功能远不止这些。当使用NumPy、SciPy、Matplotlib等数字计算与数据可视化的扩展包时才会真正显示出其强大的功能。当然这些包没有IPython也能使用,然而是因为有了IPython才使得使用Python进行交互式计算和数据可视化有了实际意义。这些工具组合在一起构成了一个开源的科学计算的可选平台。这个平台甚至可以媲美广泛使用的商业产品。在下一章,我们将会介绍这个平台的数字计算功能。