jupyter notebook :一個交互式計算和開發環境


一、 IPython基礎

代碼自動補全:Tab鍵

可補全內容包括:變量名、函數名、成員變量函數、目錄文件

內省(Itrospection)

  • 在變量名之前或之后加上問號(?),這樣可以顯示這個對象的相關信息。
    如果這個對象是個函數或實例方法,那么它的docstring也會被顯示出來。
    • 使用??還將顯示該函數的源代碼
  • ?還能搜索IPython命名空間。一些字符再配以通配符(*)即可顯示出所有與其相匹配的名稱。
    如,我們可以列出NumPy頂級命名空間中含有load的所有函數:
    np.load?

%run 命令

在IPython環境中,所有文件都可以通過%run命令當做Python程序來運行。
腳本的行為和在標准命令行環境(通過python xxx.py啟動的)中執行時一樣,此后,在文件中所定義的全部變量就可以在當前IPython shell中訪問了。

  • Ctrl-C 終止當前正在執行的代碼(引發一個KeyboardInterrupt)
    IPython有一些特殊的命令。有的為常見任務提供便利,有的則使你能夠輕松控制IPython系統的行為。

魔術命令(以%為前綴的命令)

  • 魔術命令可以看做運行於IPython系統中的命令行程序,它們大都還有一些參數選項。在命令后面加問號(?)可以查看。
  • 魔術命令默認是可以不帶百分號使用的,只要沒有定義與其同名的變量即可。這個功能可以通過%automagic命令打開或者關閉。
  • 通過%quickref%magic命令可以查看所有的命令。

常用的魔術命令如下:

  • %quickref thon快速參考
  • %magic 顯示magic command詳細文檔
  • %debug 從最新的異常跟蹤的底部進入交互式調試器
  • %hist 打印命令輸入歷史
  • %pdb 在發生異常后自動進入調試器
  • %paste 執行剪貼板中的Python代碼
  • %cpaste 打開一個特殊的提示符以便手工粘貼待執行的代碼
  • %reset 刪除interactive空間中的全部變量/名稱
  • %run 執行一個python腳本
  • %page 分頁顯示一個對象
  • %time 報告statement執行的時間
  • %timeit 多次執行statement以計算平均執行時間,用於執行時間非常小的代碼。
  • %who、%who_is、%whos 顯示Interactive命名空間的中定義的變量,信息級別/冗余度可變
  • %xdel 刪除變量,並嘗試清楚其在IPython中的對象上的一切引用

matplotlib集成與pylab模式

導致IPython廣泛應用於科學計算領域的重要原因在於它能夠跟matplotlib這樣的庫及其他GUI工具的默契配合。
如果在標准 python shell 中創建一個matplotlib繪圖窗口,就會發現GUI時間循環會接管Python回話的控制權,知道該窗口關閉。這顯然無法實現交互式的數據分析和可視化,因此IPython對各個GUI框架進行了專門的處理以使其能夠與shell配合得天衣無縫。
集成matplotlib方法:

%pylab
這將會使IPython完成以下工作:

import numpy as np
import matplotlib as plt
from matplotlib import pylab, mlab, pyplot
from IPython.display import display
from IPython.core.pylabtools import figsize, getfigs

from pylab import *
from numpy import *

使用示例:

%pylab  inline
plot(randn(1000).cumsum())

二、 使用歷史命令

IPython維護着一個位於硬盤上的一個小型數據庫,包含執行過的每一天命令。這樣的目的在於:

  • 方便的搜索、自動完成之前執行過的命令
  • 在回話間持久化歷史命令
  • 將輸入輸出歷史記錄到日志文件

搜索並重用命令歷史

上箭頭鍵:搜索出命令歷史中第一個與你輸入的字符相匹配的命令。多次按將會在歷史中不斷搜索。
下箭頭鍵:子命令歷史中向前搜索。
Ctrl-R:部分增量搜素,循環在命令歷史中搜素與輸入相符的行。

輸入和輸出變量

IPython會將輸入和輸出的引用保存在一些特殊變量中。
最近的輸入和輸出分別保存在_(一個下划線)和__(兩個下划線)兩個變量中。
輸入被保存在_iX變量中,其中X是輸入的行號。
輸出被保存在_X變量中,其中X是輸出的行號
幾個與輸入輸出有關的魔術命令:

  • %hist 打印輸入歷史
  • %reset 清空interactive命名空間,可選擇是否清空輸入和輸出緩存
  • %xdel 從IPython中移除特定對象的一切引用

記錄輸入和輸出

執行%logstart能夠開始記錄控制台回話,包括輸入和輸出。與之配合的命令有:%logoff%logon%logstate%logstop

三、 與操作系統交互

IPython與操作系統shell結合的非常緊密,可以在IPython中執行shell命令。
與系統相關的命令:

  • !cmd在系統shell中執行cmd
  • output=!cmd args 執行cmd,並將結果放在output中
  • %bookmark 書簽名 使用IPython的目錄書簽系統
  • %cd directory(文件路徑) 更改工作目錄
  • %pwd 返回系統當前工作目錄
  • %env 以字典形式返回系統環境變量

shell命令

在IPython中以感嘆號(!)開頭的命令表示其后的所有內容將會在系統shell中執行。
使用!時,還允許使用當前環境中定義的Python值,只需在變量名前加上美元($)符號即可:

foo = 'E:\學習*'
!dir $foo
 驅動器 E 中的卷是 LENOVO
 卷的序列號是 8635-9A21

 E:\ 的目錄

2017/08/13  19:54    <DIR>          學習
               0 個文件              0 字節
               1 個目錄 124,378,099,712 可用字節

目錄書簽系統

IPython的目錄書簽系統能夠保存常用目錄的別名以便實現快速跳轉。書簽能夠持久化保存。
如:

  • %bookmark pys 'C:/User/xxx/PyWorkSpace'
  • 定義好書簽之后,就可以在執行魔術命令%cd時使用這些書簽了: cd pys
  • 列出所有書簽: %bookmark -l
  • 書簽名與目錄沖突:%bookmark -b pys #強制使用書簽目錄

四、 軟件開發工具

交互式調試器

代碼調試的最佳時機就是錯誤發生的時候:

  • 在發生異常之后馬上輸入 %debug 命令將會調用調試器,並直接跳轉到引發異常的那個幀。
    • pdb中,可以查看各個幀中的一切對象和數據。默認是從最低級開始的。輸入u(p)d(own)可以在棧的各級別之間切換。
  • 使用 %pdb on命令可以開啟自動調試功能, 即當程序異常時直接開始進行調試。%pdb off命令可關閉自動調試。如果不加參數,只輸入%pdb命令則在兩種模式之間切換。
  • 如果你想設置斷點或對函數進行單步調試。 可以使用帶有 -d 選項的%run命令,這將會在執行腳本文件中的代碼之前打開調試器。然后,如果你想套設置斷點,那么使用 b(reak) x 命令,其中x為行號。前進一行使用 n(ext) 命令,進入函數體使用 s(tep into) 然后使用 c(ontinue) 命令即可直行至斷點處。要查看變量可以使用 !x ,其中x為變量名。

雖然大部分IDE都擁有優秀的GUI調試器,但是在IPython中調試程序卻往往會帶來更高的生產率。
pdb主要命令如下:

  • h(elp) 顯示命令列表
  • help command 顯示命令的說明文檔
  • c(continue) 回復程序的執行
  • q(uit) 退出調試器
  • b(reak) number 在指定行設置斷點
  • s(tep) 單步進入(step into)函數
  • n(next) 執行當前行(step over),並進入下一行
  • u(p)/d(own) 在函數調用棧中向上或向下移動
  • a(rgs) 顯示當前函數的參數
  • debug statement 在新的調試器中調用語句statement
  • l(ist) statement 顯示當前行,以及當前棧級別上的上下文參考代碼
  • w(here) 打印當前位置的完整棧跟蹤

調試器的其他使用場景

除了上面提到的,還有另外的調試方法。可以使用set_trace這個特別的函數。下面兩個函數很有用:

def set_trace():
    from IPython.core.debugger import Pdb
    Pdb(color_sheme='Linux').set_trace(sys._getframe().f_back)

def debug(f, *args, **kwargs):
    from IPython.core.debugger import Pdb
    pdb = Pdb(color_scheme='Linux')
    return pdb.runcall(f, *args, **kwargs)

第一個函數set_trace很簡單,你可以將它放在代碼中任何希望停下來查看一番的地方:

def calling_things():
    set_trace()  #自定義的調試函數
    works_fine()
    throws_an_exception()

第二個函數(debug),使你能夠直接在任意函數上使用調試器。
假設我們有如下函數:

def f(x, y, z=1):
    tmp = x + y
    return tmp / z

現在想對其進行單步調試,按如下方式即可:debug(f, 1, 2, z=3)4.3

debug(f, 1, 2, z=3)
C:\Users\xiner\Anaconda3\lib\site-packages\ipykernel_launcher.py:7: DeprecationWarning: The `color_scheme` argument is deprecated since version 5.1
  import sys


> <ipython-input-18-b1f62d757512>(2)f()
      1 def f(x, y, z=1):
----> 2     tmp = x + y
      3     return tmp / z

ipdb> 4.3
4.3
ipdb> %time
*** SyntaxError: invalid syntax
ipdb> exit()

測試代碼執行時間:%time%timeit

對於大規模運行時間長的程序,你可能希望測試一下各個部分的執行時間。了解某個復雜計算過程到底是哪些函數占用時間最多。
使用內置的time.clock和time.time函數手工測試代碼執行時間的方式如下:

import time 
start = time.time()
iterations = 10000
for i in range(iterations):
    # some code
    elapsed_per = (time.time() - start) / iterations

由於這是一個非常常用的功能,所以IPython專門提供了兩個魔術函數%time%timeit以便自動完成該過程。

  • %time 一次執行一條語句,然后報告總體執行時間。
  • %timeit會自動多次執行以產生一個非常准確的平均執行時間。

舉例來說,有一個擁有60萬字符換的數組,你希望選出其中以foo開頭的字符串。現有兩種實現方法:

strs = ['foo', 'foobar', 'baz', 'qux', 'python', 'Guido Van Rossum'] * 100000
method1 = [x for x in strs if x.startswith('foo')]
method2 = [x for x in strs if x[:3] == 'foo']

看上去它們差不多是吧?那么我們通過%time來驗證一下:

%time method1 = [x for x in strs if x.startswith('foo')]
Wall time: 121 ms
%time method2 = [x for x in strs if x[:3] == 'foo']
Wall time: 87 ms
%timeit method1 = [x for x in strs if x.startswith('foo')]
106 ms ± 963 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit method2 = [x for x in strs if x[:3] == 'foo']
80 ms ± 1.33 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

基本性能分析:%prun%run -p

代碼性能分析與代碼執行時間分析的區別在於,它關注的是耗費時間的位置。
python使用cProfile模塊進行性能分析,它會記錄各函數所耗費的時間。
使用方法如下:
python -m cProfile cprof_example.py

執行之后會發現輸出結果是按函數名排序的,這我們很難發現哪里才是最花時間的地方,一次通常會再加一個 -s 指定排序規則:
python -m cProfile -s cumulative cprof_example.py
除此自外,cProfile還能以編程的方式分析任意代碼塊的性能。IPython為此提供了一個方便的接口。即 %prun 和帶 -p 命令的%run
%prun 格式跟cProfle相同,但它分析的是部分語句而不是整個文件。

%prun -l 7 -s cumulative run_experiment()
利用 %run -p 也能達到上面的系統命令的效果去無需退出IPython:
%run -p -s cumulative cprof_example.py

逐行分析函數性能

有時從%prun得到的信息要么不足以說明函數的執行時間,要么復雜到難以理解。這是我們可以使用一個叫做line_profiler的小型庫(可以通過PyPI或其他包管理工具獲取)。其中有一個新的魔術函數%lprun,它可以對一個或多個函數進行逐行性能分析。

四、 IPython HTML Notebook

IPython Notebook目前已成為一種非常棒的交互式計算工具,同時還是科研和教學的一種理想媒介。它有一種基於JSON的文檔格式.ipynb,一種流行的演示手段就是使用IPython Notebook再將.ipynb文件發布到網上以供所有人查閱。

IPython Notebook是一個運行於命令行上的輕量級服務器進程。

由於我們是在一個Web瀏覽器上使用Notebook的,因此該服務器進程可以運行於任何其他地方。甚至可以連接到那些運行在雲服務上的Notebook

五、 使用IPython進行高效開發的提示

重新加載模塊依賴項

在Python中,當你輸入import some_lib時,其中的代碼就會執行,且其中的變量、函數和引入項都會被保存在一個新建的some_lib模塊命名空間中。下次再輸入import some_lib時,就會得到這個模塊命名空間的一個引用。
這種"一次加載"方式對於IPython的交互式開發就會帶來問題。

如果一個文件import了另一個模塊some_lib,在執行了這個文件之后,需要對some_lib進行修改,然后再重新執行文件。這時仍然會使用老版本的some_lib。因為原來的some_lib在交互式環境中已經被加載過了。

為了解決這個問題,有兩個方法:

  • 使用reload函數
import some_lib 
reload(some_lib)

顯然,當依賴變得更強的時候,就需要在很多地方插入很多的reload。

  • 下面的方法用於解決模塊的”深度“(遞歸)重加載。
    • 使用dreload函數:它會嘗試重新加載some_lib以及其所有的依賴項。
      遺憾的是,以上方法不是萬靈丹,如果真的失效了,重啟IPython試試看。

代碼設計提示

保留有意義的對象和數據
如果你在IPython中執行了一下代碼:

from my_funcs import g

def f(x, y):
    return g(x + y)

def main():
    x = 6
    y = 7.5
    result = x + y

if __name__ == '__main__':
    main()

那么我們將訪問不到任何結果以及main函數中定義的對象。
更好的方法是直接在該模塊的全局命名空間中執行main中的代碼(當然如果你希望該模塊是可引用的,可以將這些代碼放到 if __name__ == '__main__'中)。這樣當你 %run 這段代碼時就可以訪問到main中定義的所有變量了。
對這個簡單的例子而言可能意義不大,但是對於以后將會遇到的針對大數據集的復雜數據分析問題而言就很重要了。
扁平結構要比嵌套結構好。
這個思想來自”Zen of Python“,它對交互式的代碼開發模式同樣有效。編寫函數和類時應盡量注意低耦合和模塊化,這樣能使它們更易於測試、調試和交互式使用。不要怕大文件。
在IPython開發時,處理10個小的文件會比處理一個相對較大的文件來的更頭疼。更少的文件意味着需要重新加載的模塊更少,編輯時需要在各個文件之間跳轉的次數也越少。
當然,不建議將此原則極端化。對於一個大型代碼庫而言,要找到一種合乎邏輯的模塊/包架構要花點功夫,這對團隊工作非常重要。每個模塊都應該高內聚,並能足夠直觀地找到對應各種功能的函數和類。

六、 IPython高級用法

6.1 讓你的類對IPython更加友好

許多對象(如字典、列表、數組等)在IPython下都能以漂亮的格式輸出。但是對於自定義的類,就需要自己生成所需的字符串輸出。做法是重載 __repr__ 函數即可:

class Message:
    def __init__(self, msg):
        self.msg = msg
    def __str__(self):
        return 'Message: %s' % self.msg

類似的重載 __str__ 能夠在print函數時輸出相應內容


免責聲明!

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



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