一但習慣某個東西的好處後, 久了就會忘了它的重要性。今天重操舊業寫了久違的 python, 覺得能用 python 寫程式已很爽了, 還能用 ipython 寫 python, 更是爽上加爽!特此記錄一下, 分享給還未試過的人。
ipython 是 python 的互動式 shell, 功能相當強大, 而且預設設定就相當好用。如今 ipython 已發展到令人難以想像的地步, 以下只是基本的使用情境。
試用別人的模組
$ ipython
Python 2.7.3 (default, Aug 1 2012, 05:14:39)
Type "copyright", "credits" or "license" for more information.
IPython 0.13.1 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: import os
In [2]: os.pa<TAB>
os.pardir os.pathconf os.pathsep
os.path os.pathconf_names
In [2]: os.path.join?
Type: function
String Form:<function join at 0x7f67f9c09ed8>
File: /usr/lib/python2.7/posixpath.py
Definition: os.path.join(a, *p)
Docstring:
Join two or more pathname components, inserting '/' as needed.
If any component is an absolute path, all previous path components
will be discarded.
In [3]: os.path.join('a', 'b')
Out[3]: 'a/b'
In [4]: os.path.join??
Type: function
String Form:<function join at 0x7f67f9c09ed8>
File: /usr/lib/python2.7/posixpath.py
Definition: os.path.join(a, *p)
Source:
def join(a, *p):
"""Join two or more pathname components, inserting '/' as needed.
If any component is an absolute path, all previous path components
will be discarded."""
path = a
for b in p:
if b.startswith('/'):
path = b
elif path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
首先我先 import os, 第二行打了 os.pa<TAB> 看看有那些名稱開頭為 "pa" 的子物件或函式可用。也可以用 os.<TAB> 看 os 下的全部物件和函式。
接著找到 os.path.join, 在它後面加上 "?", 表示想知道 os.path.join 的 python doc。了解用法後就在第三行試試, ipython 預設會輸出 expression 回傳的結果 (而且是用 pretty-print 的方式呈現)。
最後第四行用 "??" 查詢 os.path.join 的實作, 有時沒有文件或看不懂文件的話, 直接看原始碼也滿方便的。另外讀別人程式發覺 import module 順序太複雜時, 也可以先用 ipython import 進來, 再用 "??" 看最後 import 到的是那一份實作。也可從裡面提到的檔名再用編輯器打開來看。
有了 <TAB>、? 和 ?? 後, 不太需要從外部找參考手冊, 直接在 ipython 裡試比較快。
開發片段小程式
試了一些片段小程式, 了解怎麼使用別人的模組後, 再來會想拼湊一些小程式。這時可用 edit 進入編輯器 (會選用環境變數 EDITOR 設的值):
In [1]: edit
IPython will make a temporary file named: /tmp/ipython_edit_bAOgl9.py
Editing... done. Executing edited code...
Out[1]: 'class Rect(object):\n def __init__(self, x, y, w, h):\n self.x = x\n self.y = y\n self.w = w\n self.h = h\n'
In [2]: r = Rect(0, 0, 100, 50)
In [3]: r.w
Out[3]: 100
In [4]: r.area()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-8-83a71df0c49c> in <module>()
----> 1 r.area()
AttributeError: 'Rect' object has no attribute 'area'
In [5]: edit -p
IPython will make a temporary file named: /tmp/ipython_edit_stFwGi.py
Editing... done. Executing edited code...
Out[5]: 'class Rect(object):\n def __init__(self, x, y, w, h):\n self.x = x\n self.y = y\n self.w = w\n self.h = h\n\n def area(self):\n return self.w * self.h\n'
In [6]: r2 = Rect(0, 0, 100, 50)
In [7]: r2.area()
Out[7]: 5000
在第一行按 edit 後, 畫面會切到編輯器, 寫好內容存檔離開後, ipython 會載入剛才打的內容到 ipython 裡, 也就是第一行輸出 'class Rect(object):\n def __init__ ...' 那一串。這裡我定義了 class Rect, 然後試一下 Rect 看看有沒有寫錯。接著第五行 edit -p 表示回去編輯上一次的內容, 執行後一樣會進入編輯器, 不過內容會先有上一次打過的東西。這回我多補了 area() 函式, 於是新增的 Rect 物件就有這函式了。
拼湊差不多後, 可用 edit -p 取得內容, 另外存到檔案, 或是打 hist 看打過的內容, 手動剪貼到檔案裡。
開發完整程式
想法明確時, 直接用編輯器編寫, 再輔以 ipython 測試更有效率。
首先, 終端機A的編輯器畫面如下所示, 寫了一個階乘函數:
# foo.py
def factorial(n):
s = 1
for i in range(n):
s *= i
return s
然後在終端機B的 ipython 測試:
In [1]: import foo
In [2]: foo.factorial(3)
Out[2]: 0
In [3]: reload(foo)
Out[3]: <module 'foo' from 'foo.py'>
In [4]: foo.factorial(3)
Out[4]: 6
第二行測完發覺結果不對, 於是回終端機A修改, 將 range(n) 改為 range(1, n + 1), 再回來終端機B執行 reload(foo) 重新載入 foo。接著第四行重測就得到正確的結果了。
當然, 發覺結果不對時, 也可以在 ipython 測測 range() 的用法, 或用 range? 看看說明, 很快就會找到答案。
結語
以上只是基本的 ipython 用途, 相信開發速度已可以比單用編輯器寫 python 快上兩倍以上。強烈建議有寫 python 的人一定要用看看 ipython。