IPython
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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论