IPython,讓Python顯得友好十倍的外套——windows XP/Win7安裝詳解


 
 

前言

     學習python,官方版本其實足夠了。但是如果追求更好的開發體驗,耐得住不厭其煩地折騰。那么我可以負責任的告訴你:IPython是我認為的唯一顯著好於原版python的工具。

   整理了《Python 二三事》:http://pre-sence.com/archives/python-intro   《Python 四五事》:http://pre-sence.com/archives/python-misc 並加入安裝IPython部分。

     寫這篇隨筆的原因是:忽然醒悟之前我安裝IPython折騰許久不成功可能是我未能想起pip或easy_install這兩個python的上帝工具。參考:Python包管理工具pip與easy_install

     個人經驗總結:IPython,是學習python的利器,是讓Python顯得友好十倍的外套,是我唯一的強烈推薦。

 

安裝IPython

任何Linux發行版對編程者都十分友好

  Ubuntu為例: sudo apt-get install ipython

windows環境:

     1、下載ez_setup.py ,右擊左邊鏈接,另存為,使用python ez_setup.py運行,或直接雙擊。

     2、步驟1成功后,cmd下輸入命令easy_install -h可以測試,正常反應說明已經可以使用easy_install了。

     3、cmd下輸入easy_install pip安裝pip,這是因為pip正是easy_install的下一代,比easy_install好用。

     4、步驟3成功后,pip install ipython。

     5、如果步驟4不行,退一步,使用easy_install ipython安裝。

 

運行IPython   

    cmd提示符下,輸入ipython運行就可以使用除了原python外,IPython多出來的貼心的“I”了。

退出IPython

    與python一樣也是輸入exit

 

Python實用技巧:

1、關於 "_" 字符使用

在 Python shell 下 _ 總是被賦予之前最后一個表達式的值(注:@pythonwood)。這里看個例子應該就能清楚:

>>> import string
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> print _

abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

>>>2014

2014

>>> v = _

>>> v

2014

 
舉個實際的例子,比如你在調試時讀文件的時候直接進行 f.read() ,你看了看發現輸出結果很有意思,想要對它進行進一步處理,但發現讀的時候忘記賦值了。以往你只能嘆嘆氣重新開文件再讀一次,現在你只要執行 result = _,把 _ 附到另外一個變量就可以了。

2、python -m

相信很多人應該用過這個東西,Python 很多標准庫都提供這樣的調用方式來實現一些簡單的命令行功能。Python 3 現在自帶 pip。比如我們想使用 Python 3 的 pip 來安裝別的庫,我們可以這樣:

py -3 -m pip install bottle

跟你預料的一樣,這樣就可以了。當然你可以用個 .bat 文件來把這些包裹起來並放在 Path 上,一個簡單的例子,把下面的內容寫到一個叫 pip3.bat 的文件里:

@echo off
py -3 -m pip %*

並放到 Path 上,就可以方便調用了。其中 %* 負責傳遞所有的命令行參數。

實際上 python -m 可以用的東西還真的挺多,這里給出一個不完全的列表:

######################################################
# 最強功能
######################################################
# 局域網共享,宿舍中任意一台筆記本都可以瞬間變身web資源共享服務器
# 命令ipconfig可以看到局域網ip地址,一般是192,172這些開頭的。
# 使用本機80端口,可任意設置。只共享當前運行目錄。
# 
python -m SimpleHTTPServer 80 
# 
# 本機任意瀏覽器輸入 http://localhost 或 http://127.0.0.1 可以訪問。
# (80端口瀏覽器默認的,不需輸入)甚至在地址欄直接輸入localhost即可。
# 局域網,(宿舍)任意電腦輸入上面所說192或172等開頭的IP地址即可訪問。
######################################################


# 縮進輸出 JSON
echo {"hey" : "kid"} | python -m json.tool
# 簡單的執行時間測量
python -m timeit [ix*ix for ix in range(100)]
# 簡單的 Profiling
python -m cProfile myscript.py
# 比較兩個文件夾的區別
python -m filecmp path/to/a path/to/b
# base64 轉換
echo foo bar | python -m base64
# 調用默認瀏覽器打開一個新標簽頁
python -m webbrowser -t http://google.com
# 生成程序文檔
python -m pydoc myscript.py
# 類似 nose 的自動搜索 unittest
python -m unittest discover
# 調用 pdb 執行代碼
python -m pdb myscript.py

 

IPython實用技巧:

1、Tab自動補全,一種是簡單的關鍵字補全,另外一種是對象的方法和屬性補全。

作為例子,我們先引入 sys 模塊,之后再輸入 sys. (注意有個點),此時按下 tab 鍵,IPython 會列出所有 sys 模塊下的方法和屬性。

IPython

接着上面的例子,我們輸入 sys?,這樣會顯示出 sys 模塊的 docstring及相關信息。很多時候這個也是很方便的功能。

 
 
 

2、IPython 還有強大之處很大部分還體現在它的 magic function 中。它是指的在 IPython 環境下執行以 % 開頭的一些命令來對 IPython 進行一些設定或者執行某些功能。在 IPython 中輸入 %lsmagic 就能列出所有的 magic functions。在這里簡單介紹下幾個比較有意思的,你也可以自己通過查看文檔來找找有哪些你特別用的到得。

  • 之前看到能用 ? 來查詢函數的文檔,對於 magic function 也是如此。比如 %run?

  • !cd .. 在命令前面加上 ! 則它會被作為命令行命令執行,這樣你就不用退出 IPython 來進行命令行操作。

  • %run foo.py 在當前環境下直接執行 foo.py,效果跟命令行下調用 ipython foo.py 相同。

  • %time foo.bar()timeit decorator 作用相同,進行簡單的 profile。

  • %hist 能顯示之前輸入過的命令的歷史,同時你可以用 In[<linenumber>] 來訪問之前的命令。比如 %exec In[10] 就能執行列表中第十行。

  • %rep 類似上面的 _ 變量,但是是以字串的形式返回

  • 最后,如果 %automagic 是打開的狀態的話,所有 magic function 不需要在前面加 % 就能正確調用。

在當前 IPython 版本中還有一個由於安全原因沒有默認引入的 %autoreload,它的作用是在可以自動重新載入你調用的函數,以及其相關模塊。接觸過 django 的同學對這個應該比較熟悉,在 IPython 中的效果就是,當你在調試一個一直在反復改動的函數時,你可以開啟這個功能保證每次調用都會重新讀取最新的版本,讓你在源碼中的改動馬上生效。在 IPython 中執行

import ipy_autoreload
%%autoreload 2

這樣 IPython 會對所有的模塊都進行 autoreload。你可以通過執行 %autoreload? 來查詢它的文檔來進行進一步設定。如果你希望 IPython 每次啟動自動載入次功能,那么可以通過配置 ipythonrc (在 Windows 下可以在 C:\Users\<username>\_ipython\ipythonrc.ini 找到) 來進行相關設置。

3、還有一個很神奇的功能。如果你的程序是由命令行開始執行的,即在命令行下輸入 python foo.py(大部分 Python 程序都是),那么你還可以利用 IPython 在你的程序任意地方進行斷點調試!

在你程序中任意地方,加入如下語句:

from IPython.Shell import IPShellEmbed
IPShellEmbed([])()

注意:最近 IPython 發布了 0.11 版本,各方面變化都非常大,API 也經過了重新設計。如果你使用的是 0.11 那么上面兩行對應的是這樣的:

from IPython import embed
embed()

再和平常一樣運行你的程序,你會發現在程序運行到插入語句的地方時,會轉到 IPython 環境下。你可以試試運行些指令,就會發現此刻 IPython 的環境就是在程序的那個位置。你可以逐個瀏覽當前狀態下的各個變量,調用各種函數,輸出你感興趣的值來幫助調試。之后你可以照常退出 IPython,然后程序會繼續運行下去,自然地你在當時 IPython 下執行的語句也會對程序接下來的運行造成影響。

這個方法我實在這里看 到的。想象一下,這樣做就像讓高速運轉的程序暫停下來,你再對運行中的程序進行檢查和修改,之后再讓他繼續運行下去。這里舉一個例子,比如編寫網頁 bot ,你在每取回一個頁面后你都得看看它的內容,再嘗試如何處理他獲得下一個頁面的地址。運用這個技巧,你可以在取回頁面后讓程序中斷,再那里實驗各種處理方 法,在找到正確的處理方式后寫回到你的代碼中,再進行下一步。這種工作流程只有像 Python 這種動態語言才可以做到。

 

4、一個實際的例子


這里以一個簡單的例子來講解一下是怎樣的一個情況。我們要寫一個可以將簡單的數據表達式,類似 1 + (2 - 3) * 456 解析成樹的 Pratt Parser。首先我們需要一個 lexer 把每個 token 解析出來,那么最開始的代碼就是:

# simple math expression parser

def lexer(s):
    '''token generator, yields a list of tokens'''
    yield s

if __name__ == '__main__':
    for token in lexer("1 + (2 - 3) * 456"):
        print token

 

明顯這個沒有任何意義,但現在程序已經有足夠的東西能夠跑起來。我們把這個程序存為 expr.py,開啟一個命令行窗口,運行 ipython 然后像這樣執行它:

$ ipython
IPython 0.13.1 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
...

In [1]: run expr.py
1 + (2 - 3) * 456

 

在 IPython 里面用 run 跑的好處有很多,首先是你在程序執行完畢后整個程序的狀態,比如最后全局變量的值,你寫的函數這些你都是可以隨便執行的!同樣的你可以在 IPython 里面保存一些用來測試的常量,每次用 run 跑的話新的程序會被重新載入,你可以這樣方便的測試每個函數,有一個非常動態的環境來調試你的程序:

In [2]: print token # 注意這里 token 就是 __main__ 里面的那個 token 的值
1 + (2 - 3) * 456

In [3]: print list(lexer('1+2+3')) # 可以運行你寫的函數
['1+2+3']

 

然后按照之前的想法,我們嘗試把這個 lexer 寫出來。在這個過程中,IPython 可以用來查看函數的文檔,測試如何調用某些函數,看看返回值是什么樣子等等,還是跟上面的說的一樣,我們有一個動態的環境可以真真正正的執行程序,你可以 在把代碼寫到你珍貴的主程序之前就有機會運行它,這樣你可以更確認你的代碼能正常工作:

In [4]: s = "foo" # 忘記判斷字符串是數字的函數的名字了,用一個字符串試試看

In [5]: s.is      # 開頭大概是 is,這里按下 tab 鍵 IPython 會幫我們補全
s.isalnum  s.isalpha  s.isdigit  s.islower  s.isspace  s.istitle

In [6]: s.isdigit?  # 結果是 isdigit,在表達式后加上問號並回車查看文檔
Type:       builtin_function_or_method
String Form:<built-in method isdigit of str object at 0x1264f08>
Docstring:
S.isdigit() -> bool

Return True if all characters in S are digits
and there is at least one character in S, False otherwise.

In [8]: s.isdigit()      # 調用試試看
Out[8]: False

In [9]: 'f' in 'foo'     # 試試字符串能不能用 in 來判斷
Out[9]: True

 

確認了各個步驟以后,我們把 lexer 的代碼填起來。我們為了節省縱向的空間我們把很多東西寫在一行里面:

# simple math expression parser (broken lexer)

def lexer(s):
    '''token generator'''
    ix = 0 
    while ix < len(s):
        if s[ix].isspace(): ix += 1
        if s[ix] in "+-*/()":
            yield s[ix]; ix += 1
        if s[ix].isdigit():
            jx = ix + 1 
            while jx < len(s) and s[jx].isdigit(): jx += 1
            yield s[ix:jx]; ix = jx
        else:
            raise Exception("invalid char at %d: '%s'" % (ix, s[ix]))
    yield ""

if __name__ == '__main__':
    print list(lexer("1 + (2 - 3) * 456"))

 

看起來不錯,我們還是在 IPython 里執行試試,結果發現程序拋出了一個異常:

In [6]: run expr.py
------------------------------------------------------------------
Exception                       Traceback (most recent call last)
py/expr.py in <module>()
     18 
     19 if __name__ == '__main__':
---> 20     print list(lexer("1 + (2 - 3) * 456"))

py/expr.py in lexer(s)
     13             yield s[ix:jx]; ix = jx
     14         else:
---> 15             raise Exception("invalid character at ...))
     16     yield ""
     17

Exception: invalid character at 3: ' '

 

嗯?好像程序里已經處理了空格的情況。怎么會這樣?不知道你碰到異常的時候一般都怎么辦。你可能會選擇到處添加 print,用 IDE 斷點調試。其實這種情況用 pdb 是很明智的選擇,在 IPython 里我們可以非常輕松的使用它。

In [13]: pdb   # 開啟 pdb ,這樣在異常的時候我們會自動的 break 到異常處
Automatic pdb calling has been turned ON

In [14]: run expr.py
-----------------------------------------------------------------
Exception: invalid character at 3: ' '
> py/expr.py(15)lexer()
     14         else:
---> 15             raise Exception("invalid char at ...))
     16     yield ""

ipdb> print ix  # 這里我們可以執行任何 Python 的代碼
3
ipdb> whatis ix # 也可以用 pdb 提供的命令,輸入 help 可以查看所有命令
<type 'int'>

 

通過方便的調試和仔細檢查代碼,我們發現是沒有正確的使用 elif 造成了問題!(我知道這個過程不是太符合情理...)。把代碼里的后面的幾個 if 都換成 elif 以后我們發現結果基本上是對的了。我們可以馬上再跑幾個類似的例子,確認不同的輸入是否都有比較好的結果:

In [18]: run expr.py   # 這次差不多對了,我們可以試試幾個別的例子
['1', '+', '(', '2', '-', '3', ')', '*', '456', '']

In [19]: print list(lexer("1*123*87-2*5"))
['1', '*', '123', '*', '87', '-', '2', '*', '5', '']

# 跟在 shell 里面一樣,你可以用上下來選取之前的記錄,然后簡單的修改再重新執行。
# 記得每次 run 后你的函數都是最新版本,你可以很簡單的用重復的數據來測試你的函數
# IPython 甚至還實現了 Ctrl+R!自己試試看吧
In [19]: print list(lexer("1 + two"))     
Exception: invalid character at 2: 't'...

 

在一段痛苦的調試之后,我們最終把程序寫出來了。很遺憾程序超出了我預計的長度,就不貼在這里了。后面部分的開發過程跟前面基本還是一樣,總結起來就是:

  1. 保持你的程序是一個可以運行並且有意義的狀態,盡可能頻繁的運行。
  2. 在 IPython 里查看文檔,嘗試小的程序片段,測試些你不確定的做法,確定之后再把東西添加到你的代碼里。
  3. 用不同的參數在 IPython 里測試你正在編寫的函數/class。
  4. 當遇到問題的時候,先簡單的用 pdb 在異常處 break,十有八九都能有些頭緒。

額外的注意事項

這里舉的例子是你所有的開發都是在單個 .py 文件里的。現實生活中你很有可能會橫跨幾個文件一起修改。請務必注意,在 IPython 里你每次 run 的時候只有被 run 的那個文件里的東西會是最后修改的版本,其 import 的東西如果在期間被修改是不會反應出來的。

這個的原理就跟你在 Python shell 里在修改前修改后重復 import 某個模塊不會有作用是一樣的,Python 神奇的 import 機制不會去追蹤其他模塊的修改。你可以手動用 reload 函數來重新載入,你也可以使用 IPython 的 autoreload 功能來讓你忽略這個問題。個人來說我沒怎么用過這個功能,IPython 沒有默認開啟它可能也是有些顧慮,請自己評估看看。

另外你應該已經注意到,run 的效果基本上就是把你的代碼拷貝進 IPython 里執行一遍。對於沒有 __main__ 的文件,你也可以 run,這樣里面定義的函數和 class 就會反映出你的更改。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM