返回介绍

IPython

发布于 2025-04-20 18:52:17 字数 11371 浏览 0 评论 0 收藏

Python 工程师需要快速验证代码运行结果是否符合预期。最快捷方便的做法就是使用 Python 自带的交互模式,但是这个 Python Shell 有非常多的弊端:

  • 不能在退出时保存历史记录以备未来查询。
  • 不支持 Tab 自动补全。
  • 不能快速获得模块/函数/类的信息,如参数、文档、原始代码等。
  • 不方便在交互环境下执行 Shell 命令。

IPython 是一个基于 Python Shell 的交互式解释器,但是拥有比默认 Shell 强大得多的编辑和交互功能。笔者在开发中,有时候在 IPython 交互环境下的时间甚至比使用编辑器的时间还长。学好 IPython 的必要性不亚于学习编辑器和熟悉 Linux 系统。

2015 年 8 月 12 日发布的 IPython 4.0 做了很多的拆分和组件化工作,IPython 已经分离成了两个组织 IPython 和 Jupyter,Notebook 和其他语言无关的部分(如 QtConsole、Notebook 相关)都以独立的包的方式托管到 Jupyter 组织下;IPython 只保留 IPython Shell 和 Jupyter 内核等相关功能,而且其中核心组件也都拆分成包托管到 IPython 组织下,如 ipykernel、traitlets、ipyparallel 等。

我们先安装 IPython:

> pip install ipython
> ipython -V
5.0.0

当前 IPython 面向用户的组件主有 5 种,如表 12.1 所示。

表 12.1 IPython 面向用户的组件

组件说明
IPython Shell交互解释器。也就是直接在终端输入 ipython,就进入了 REPL(Read-Eval-Print-Loop)模式
Jupyter Notebook网页版的记事本应用,可以在页面上交互地运行程序,通常用作数据可视化,Wiki 系统等
Jupyter Console使用 Jupyter 协议的 IPython 终端交互解释器
Jupyter QtConsole一个 Qt 的富编辑器,通常选用跨平台的 PySide 作为 Qt 的 Python 绑定库
IPython ipyparallel用来进行交互的并行计算

现在安装 IPython 时已经不再安装全部组件了,比如想要使用并行计算功能,就得用如下命令安装:

> pip install ipython[parallel]

IPython 交互模式

IPython 交互模式下有多个有用的功能。

1.获得对象信息:输入你想要查看的对象,然后加上一个或者两个问号,就能获得多种对象信息。比如“exit?”,然后回车,就可以看到 exit 函数的文档字符串、文件路径、类型等。如果输入“exit??”回车,就可以看到更多的信息,如对象的源代码。

2.Magic 函数:IPython 有很多 Magic 函数,分为两种类型。一种是 Line magics,单行函数,需要使用“%”开头;另一种是 Cell magics,多行函数或者希望执行其他语言的代码,需要使用“%%”开头。

下面的例子将使用内置的 timeit 函数:

In : %timeit range(1000)
100000 loops, best of 3:9.2 □s per loop
In : %%timeit x=range(10000)
....: max(x)
....:

可以使用“%lsmagic”获得全部可用的 Magic 函数。

3.调用系统 Shell 命令。只需要在命令前加!即可:

In : !uptime
16:31:11 up 1:41, 3 users, load average:0.02, 0.06, 0.09

4.Tab 自动补全。IPython 可以自动检查对象的属性,通过 object_name.<TAB>列出全部的子属性,再使用 Tab 切换到对应的属性上,然后回车就可以了。

5.历史记录。IPython 把输入的历史记录存放在个人配置目录下的 history.sqlite 文件中,并且可以结合%rerun、%recall、%macro、%save 等 Magic 函数使用。尤为有意义的是,它把最近的三次执行记录绑定在_、__和___这三个变量上。搜索历史记录时,还支持 Ctrl-r、Ctrl-n 和 Ctrl-p 等快捷键。

常用的 Magic 函数

1.%quickref:可以快速了解 IPython 的功能和用法。

2.%alias:设置别名。

In : %alias lg ls-la|grep%s
In : lg Vagrantfile
-rw-r--r-- 1 ubuntu ubuntu 534 Jun 7 10:18 Vagrantfile

3.%automagic:Line magics 函数在执行“%automagic 1”之后就不需要在使用时输入“%”前缀了。

4.%pwd:作用类似于 Linux 的 pwd 命令,输出当前目录地址。

5.%cd:作用类似于 Linux 的 cd 命令,切换目录。如果不加参数会切换到 Home 目录。

6.%bookmark:加书签,如果目录非常常用,可以为当前目录创建书签,之后就可以用书签切换目录进来了:

In : pwd
/home/ubuntu/web_develop/chapter12
In : bookmark chapter12
In : cd ~
/home/ubuntu
In : cd -b chapter12
(bookmark:chapter12)-> /home/ubuntu/web_develop/chapter12
/home/ubuntu/web_develop/chapter12

7.%debug:激活交互的调试器。

In : b=0
    
In : 1/b
------------------------------------------------------
ZeroDivisionError    Traceback (most recent call last)
<ipython-input-2-1fcdc364a293>in<module>()
---->1 1/b
    
ZeroDivisionError:integer division or modulo by zero
    
In : %debug
> <ipython-input-2-1fcdc364a293>(1)<module>()
----> 1 1/b
    
ipdb> p b
0

8.%edit:使用编辑器打开,但需要设定 EDITOR 这个环境变量。假如写了一个很复杂的函数,代码很长,执行后发现不符合预期,用历史记录找到这个函数,再用鼠标移到对应的位置修改就很不方便。其实这时应该使用 edit 来编辑:

In : def a(num):
.....:   return num+1
.....:
In : edit a
Editing In[12]
IPython will make a temporary file named:/tmp/ipython_edit_A5CGLB/
    ipython_edit_26bIsN.py
Editing... done. Executing edited code...
Out: 'def a(num):\n    return num+2\n'
In : a(1)
Out: 3

9.%hist:%history 的别名,查看历史记录。

10.%load:把外部代码加载进来。

11.%macro:把历史记录、文件等封装为宏,以便未来重新执行。

In : %hist-n
 ...
    5: x=1
    6: y=2
    7: print x+y
In : %macro my_macro 5-7
===Macro contents:===
x=1
y=2
print x + y
In : my_macro
3

12.%rehashx:把$PATH 中的可执行命令都更新进别名系统,这样就可以在 IPython 中不加感叹号而调用了:

In : echo 1
  File "<ipython-input-1-334d5669e1fb>", line 1
echo  1
      ^
SyntaxError:invalid syntax
    
In : %rehashx
In : echo 1
1

13.%timeit:获得程序执行时间。timeit 是 Python 内置的库,用来测量小代码片的执行时间。

In : %timeit pass # 默认执行 100,000,000 次,显示最快的三次结果
100000000 loops, best of 3:9.8 ns per loop
    
In : %timeit-n 100-r 4 pass
100 loops, best of 4:9.54 ns per loop

14.%save:把某些历史记录保存到文件中。

In : hist-n
   ...
   7:x=1
   8:y=2
In : save 1.py 7-8
The following commands were written to file`1.py`:
x=1
y=2
In : !cat 1.py
# coding:utf-8
x=1
y=2

15. logstart/logoff:记录会话。退出 IPython 后还可以回到之前的状态。

   In : logstart # 不指定文件名字则默认使用 ipython_log.py
In : a=1
In : logoff

现在可以新开一个 IPython:

> ipython-i ipython_log.py
In : a
Out: 1

16.%time:作用类似于 Linux 的 time 命令,计算代码的执行时间。

In : %time range(10)
CPU times:user 0 ns, sys:4 □s, total:4 □s
Wall time:8.11 □s
Out: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

配置和自定义 IPython

默认的 IPython 配置目录是~/.ipython/profile_default。如果使用了多个 Python 解释器版本或者希望使用不一样的配置,则可以使用如下命令创建配置:

> ipython profile create web_develop

这样就创建了一个叫作 web_develop 的配置。如果要使用特殊的配置,方式如下:

> ipython --profile=web_develop

web_develop 配置目录是~/.ipython/profile_web_develop,目录下有一个 ipython_config.py 文件,存放 IPython 的配置(默认配置都是注释的)。下面展示一个配置:

> cat ~/.ipython/profile_web_develop/ipython_config.py
c=get_config() # 不用担心 get_config 没有定义
c.TerminalIPythonApp.display_banner=False # 不显示 banner
c.InteractiveShell.automagic=True # 让 magic 函数无须输入前缀“%”就可以执行
c.AliasManager.user_aliases=[ # 添加默认的别名
    ('la', 'ls-al')
]
c.InteractiveShell.colors='Linux' # 使用 Linux 颜色主题
c.InteractiveShell.logstart=True # 自动记录会话
c.TerminalInteractiveShell.confirm_exit=False # 直接退出,不需要确认

IPython 的扩展系统

IPython 有强大的扩展系统,定义一个扩展非常容易。比较好用的两个内置扩展分别是 autoreload 和 storemagic。storemagic 可以持久化宏、变量和别名。可以添加如下配置到 ipython_config.py 实现自动保存:

c.StoreMagics.autorestore=True

举个例子,第一次在 IPython 中执行如下命令:

In : l=['hello', 10, 'world']
In : %store l
Stored 'l' (list)
In : exit()

这样就把 l 存储下来了。现在退出 IPython 后重新进入:

> ipython
In : l
Out: ['hello', 10, 'world']

可以看到,l 能直接使用。我们还可以用这个功能保存一些重要的资源,这样即使退出 IPython 也能找回来。

autoreload 可以让我们不退出 IPython 就动态修改代码,在执行代码前 IPython 会帮我们自动重载改动的模块,这种思想在多种 Web 框架中都可见其踪影。

先看一个简单的例子:

> cd ~/web_develop/chapter12/section1
> cat py_autoreload.py
def a():
    return 1

在 IPython 里面执行它:

In : %load_ext autoreload
In : %autoreload 2
In : from py_autoreload import a
In : a()
Out: 1

然后在其他终端上修改 py_autoreload.py,把 a 的返回值改成 2:

def a():
    return 2

在之前的 IPython 中重新调用 a 函数:

In : a()
Out: 2

可以看到返回值动态地改变了。

官方扩展索引页(http://bit.ly/1UxMmRL )列出了一些扩展,其中也包含笔者写的一个 db.py 的扩展。db.py 是一个使用 Pandas 操作数据的工具。我们先安装它:

> pip install ipython-db

下载测试用的数据库文件:

> wget http://t.cn/R5ow5s6 -O/tmp/baseball.sqlite

在 IPython 中加载并使用它:

In : %load_ext idb
In : %db_connect sqlite:///tmp/baseball.sqlite
In : %tables allstarfull playerID unique count
Out: 1637
In : df1=%query select*from allstarfull limit 1;
In : df1
Out:
    playerID  yearID  gameNum      gameID teamID lgID GP startingPos
0  aaronha01    1955        0 NLS195507120   ML1   NL  1        None

事实上,Pandas 对象在 Jupyter Notebook 文档上的展示效果更好。可以打开 db-example.ipynb(http://bit.ly/1Uzzivm )看到效果。

使用 IPython 调试复杂代码

开发人员经常遇到程序执行时报错,如果程序非常复杂,用 pdb 模块调试不方便,一般是通过不断在对应的代码行上加一些 print 语句去调试问题。

假设现在有一个非常简单的脚本(py_debug.py):

a=1
b=0
a/b

执行“python chapter12/section1/py_debug.py”肯定会报错,但是无法调试上下文。使用 IPython 会直接执行到出错的地方,并进入 pdb 环境:

> ipython chapter12/section1/py_debug.py --pdb
WARNING: InteractiveShell.prompt_out is deprecated, use PromptManager.out_template
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
/home/ubuntu/web_develop/chapter12/section1/py_debug.py in<module>()
      3 b=0
      4
----> 5 a/b
ZeroDivisionError:integer division or modulo by zero
> /home/ubuntu/web_develop/chapter12/section1/py_debug.py(5)<module>()
      3 b=0
      4
---->  5 a/b
ipdb>  p a
1
ipdb>  p b
0

双进程模型

Jupyter Console 和 IPython Shell 在终端显示的效果很像,都是一个 While True 的 REPL 模式循环。但是,二者却有本质的区别:直接执行不带参数的 ipython 启动一个进程,而使用 Jupyter Console 启动了两个进程。这个区别也体现在 QtConsole 和 Notebook 上。

这种双进程模型的启动方式都使用了 ZeroMQ 作为消息队列来解耦应用。也就是说,当 Jupyter Console 启动后,还启动一个使用 ipykernel 模块的子进程。主进程用来发送命令和接收从子进程内核返回的结果:

ubuntu  8213  3922  0  15:37 pts/2  00:00:05 /home/ubuntu/.virtualenvs/venv/bin/
    python/home/ubuntu/.virtualenvs/venv/bin/jupyter-console
ubuntu  8250  8213  0  15:37 ?      00:00:04/home/ubuntu/.virtualenvs/venv/bin/
    python -m ipykernel -f /run/user/1000/jupyter/kernel-8213.json

可以开启第三个进程连接这个子进程内核:

> jupyter console --existing kernel-8213.json

这个新的 Console 和进程 ID 为 8213 的 Console 具备相同的环境。也就是说,你在 ID 为 8213 的 Console 中执行“a=1”,那么这个新的 Console 中的 a 也等于 1 了。双进程模型的用途还远不止本地不同进程中的互通,还可以把 kernel-8213.json 拷贝到其他服务器上,让其他服务器来连接你本地的 Console 进程。

并行计算

IPython 另一个非常有用的功能是支持并行计算。IPython 的并行计算组件叫作 ipyparallel,从 IPython 4.0 开始被拆分出来了,需要独立安装:

> pip install ipyparallel

我们先启动计算集群:

> ipcluster start -n 4

现在启动了 4 个引擎的集群。通过 IPython 连接它:

In : import ipyparallel as ipp
In : c=ipp.Client() # 客户端用来向引擎发送任务
In : c.ids # IPython 引擎的 id 列表
Out: [0, 1, 2, 3]
In : %autopx # 开启 autopx 之后,每个交互模式下的命令会在各自的引擎上执行
In : import os
In : print os.getpid()
[stdout:0] 10282
[stdout:1] 10281
[stdout:2] 10283
[stdout:3] 10280
In : %autopx # 再执行一次就是关闭 autopx,因为 Magic 函数 pxconfig 需要在 autopx 关闭时才
     能用
In : %pxconfig--targets 1 # 指定目标对象,这样下面执行的代码就只会在第 2 个引擎下运
     行
In : %%px--noblock # 执行异步的代码
....: import time
....: time.sleep(1)
....: os.getpid()
....:
Out: <AsyncResult:execute>
    
In : %pxresult # 获得异步执行的结果
Out[1:7]:10281
In : with c[:].sync_imports(): # 给每个引擎都引入 time 模块
....:     import time
....:
importing time on engine(s)
In : def f(x):
....:    time.sleep(1)
....:    return x*x
....:
In : v=c[:]
In : v
Out:<DirectView [0, 1, 2, 3]>
In : v.map_sync(f, range(10)) # 同步的执行任务
Out: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In : view=c.load_balanced_view([1, 2]) # 让并行计算实现负载均衡,这里指定只使用
     第二个和第三个引擎
In : view
Out: <LoadBalancedView [1, 2]>
In : r=view.map(f, range(10)) # 异步执行任务
In : r
Out: <AsyncMapResult:f>
In : r.ready(), r.elapsed # 查看是否执行结束,以及花费的时间
Out: (True, 3.023197)
In : r.get() # 获得执行的结果
Out: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

还可以把 view 作为装饰器加在希望并行处理的函数上:

In : @view.parallel()
...: def f(x):
...:     return x*x
...:
In : f.map(range(10))
Out: <AsyncMapResult:f>
In : r.get()
Out: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

启动集群后,在~/.ipython/profile_default/security/目录下会生成 ipcontroller-client.json 文件。

将这个文件拷贝到其他服务器上,就可以在远程调用本机的集群资源了:

In : c=ipp.Client('/tmp/ipcontroller-client.json', ssh='192.168.0.210')

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。