試實(shí)戰(zhàn))
很多人在用 Jupyter Notebook 和 JupyterLab 時(shí)其實(shí)只把它當(dāng)成了一個(gè)帶執(zhí)行的草稿紙寫一段代碼按下 ShiftEnter看到結(jié)果完事。這個(gè)用法不能說(shuō)錯(cuò)但遠(yuǎn)遠(yuǎn)低估了 Jupyter 家族能解決的問(wèn)題。作為一個(gè)日常拿它寫分析、做演示、甚至搭內(nèi)部小工具的人我今天想把這些年踩過(guò)的坑和沉淀下來(lái)的技巧一次性梳理出來(lái)從安裝環(huán)境、路徑權(quán)限到長(zhǎng)文檔導(dǎo)航、魔法命令和調(diào)試器再到導(dǎo)出和分享的邊界問(wèn)題都盡量講透。如果你是剛?cè)腴T的小白這篇文章能幫你少走一半冤枉路如果你已經(jīng)用了很久但總覺(jué)得卡手里面不少細(xì)節(jié)大概率是你沒(méi)注意過(guò)的。核心關(guān)鍵詞只有一個(gè)——Jupyter Notebook/Lab 使用技巧我把重心放在“實(shí)際按下鍵盤之后會(huì)發(fā)生什么”上盡量不談空泛的概念。1. 別急著裝完就跑安裝、啟動(dòng)與路徑選擇里的坑1.1 用對(duì)安裝方式才不糟蹋你的 Python 環(huán)境第一次按網(wǎng)上的教程裝 Jupyter 時(shí)最常見(jiàn)的操作就是打開終端輸入pip install jupyter然后等它慢慢裝。如果用的是系統(tǒng)自帶的 Python尤其是 macOS/Linux 上這一步很容易埋雷系統(tǒng)環(huán)境里已經(jīng)有一堆依賴pip裝的時(shí)候又不敢亂動(dòng)權(quán)限最后要么裝上去了啟動(dòng)報(bào)錯(cuò)要么把系統(tǒng)的 Python 搞亂。我的建議是優(yōu)先用 Anaconda 或者 Miniconda 建一個(gè)獨(dú)立環(huán)境再裝。比如conda create -n jupyter_env python3.11 conda activate jupyter_env pip install jupyterlab notebook這樣做的核心原因不是潔癖而是 Jupyter 的內(nèi)核機(jī)制。Jupyter 本身只是一個(gè) Web 前端真正跑代碼的是一個(gè)“內(nèi)核”要是一個(gè)筆記本文件記錄的內(nèi)核和你當(dāng)前激活的環(huán)境對(duì)不上啟動(dòng)時(shí)最大的表現(xiàn)就是報(bào)錯(cuò)、無(wú)響應(yīng)、或者找不到包。獨(dú)立環(huán)境能讓你隨時(shí)重建、隨時(shí)扔掉不會(huì)影響你干其他活。當(dāng)然如果你對(duì) conda 無(wú)感用python -m venv也一樣python -m venv jupyter_venv source jupyter_venv/bin/activate # Windows 下是 Scripts\activate pip install notebook jupyterlab裝完之后直接在終端敲jupyter lab或jupyter notebook瀏覽器會(huì)自動(dòng)彈出。要是沒(méi)彈出終端里會(huì)打印一個(gè)帶 token 的 URL手動(dòng)復(fù)制到瀏覽器即可這就涉及下面要說(shuō)到的登錄入口問(wèn)題。1.2 啟動(dòng)失敗和 PermissionError 究竟卡在哪熱詞里有個(gè)很典型的報(bào)錯(cuò)permissionerror:[errno 13] permission denied。我在各個(gè)群里見(jiàn)過(guò)不下三四十次大部分場(chǎng)景都是在 Linux 服務(wù)器或者 macOS 上敲了jupyter notebook結(jié)果提示這個(gè)錯(cuò)誤一臉懵。這個(gè)錯(cuò)誤的本意是“當(dāng)前用戶沒(méi)有權(quán)限訪問(wèn)某個(gè)文件或目錄”。在 Jupyter 語(yǔ)境下最常涉及兩處一處是配置文件目錄通常叫.jupyter在用戶主目錄下。Jupyter 啟動(dòng)時(shí)會(huì)往里面讀寫history.sqlite、runtime等文件。如果這個(gè)目錄的屬主不是你或者之前用sudo啟動(dòng)過(guò)導(dǎo)致文件屬主變成了root后面再用普通用戶啟動(dòng)就會(huì)撞上 PermissionError。另一處是運(yùn)行時(shí)創(chuàng)建的runtime目錄默認(rèn)在~/.local/share/jupyter/runtime或者/tmp/jupyter-runtime附近。某些服務(wù)器會(huì)把/tmp權(quán)限鎖得很緊或者掛載的/home分區(qū)有noexec/ 限制寫入的選項(xiàng)也會(huì)導(dǎo)致類似報(bào)錯(cuò)。排查思路比直接給命令更重要。先找到 Jupyter 認(rèn)為自己的工作目錄在哪運(yùn)行jupyter --config-dir jupyter --runtime-dir然后對(duì)照著看ls -ld ~/.jupyter ls -ld $(jupyter --runtime-dir)如果是屬主問(wèn)題直接sudo chown -R $USER:$USER ~/.jupyter如果是/tmp被限制了就顯式指定一個(gè)可寫的運(yùn)行目錄export JUPYTER_RUNTIME_DIR$HOME/.jupyter/runtime jupyter notebook同樣配置目錄也可以就近遷移export JUPYTER_CONFIG_DIR$HOME/.config/jupyter這套方法在服務(wù)器上尤其好用畢竟不是所有機(jī)器都讓你隨便sudo。1.3 默認(rèn)保存路徑和下載文件最終去哪了另一個(gè)高頻熱搜是“jupyter notebook 默認(rèn)保存路徑”和“使用 jupyter 下載的文件默認(rèn)安置在哪”。先搞清楚一個(gè)概念在瀏覽器里點(diǎn)Download下載的.ipynb文件走的是瀏覽器自身的下載邏輯存到哪里由瀏覽器的設(shè)置決定跟 Jupyter 無(wú)關(guān)。所以有人發(fā)現(xiàn)默認(rèn)下載到了~/Downloads那不是 Jupyter 管的。但是如果你在 notebook 里用 Python 代碼寫文件比如with open(out.txt, w, encodingutf-8) as f: f.write(hello)這個(gè)文件會(huì)出現(xiàn)在“當(dāng)前工作目錄”下也就是 notebook 文件所在的目錄。因?yàn)?Jupyter 啟動(dòng)后會(huì)把 notebook 所在目錄作為當(dāng)前目錄而open()的相對(duì)路徑就是相對(duì)于這個(gè)目錄的。那默認(rèn)保存路徑怎么改最干凈的做法是在啟動(dòng)前就指定jupyter notebook --notebook-dir/path/to/your/projects可以把--notebook-dir寫進(jìn)啟動(dòng)目錄的快捷方式或別名里alias jnjupyter notebook --notebook-dir$HOME/projects更一勞永逸的是修改配置文件jupyter notebook --generate-config打開生成的~/.jupyter/jupyter_notebook_config.py找到這一段# c.ServerApp.root_dir 改成你的目標(biāo)目錄c.ServerApp.root_dir /home/me/projects注意老版本里變量名可能是c.NotebookApp.notebook_dir新版本已經(jīng)遷移到ServerApp。版本差異是這里最搞人的點(diǎn)遇到修改后不生效先查一下你現(xiàn)在裝的版本和對(duì)應(yīng)的配置項(xiàng)名。2. 在 Notebook 和 Lab 之間怎么選不是升級(jí)換代那么簡(jiǎn)單2.1 Notebook 的老派優(yōu)雅與掣肘Jupyter Notebook也就是經(jīng)典版的界面是“一個(gè)頁(yè)面里上下排列多個(gè)單元格”雖然簡(jiǎn)單但用了很多年后你會(huì)很明顯地感受到它的限制整個(gè)頁(yè)面只有一個(gè)視圖沒(méi)法同時(shí)看兩個(gè) notebook每個(gè)文件單獨(dú)占一個(gè)標(biāo)簽頁(yè)一旦開得多標(biāo)簽頁(yè)密密麻麻拖拽單元格換位置在單個(gè)文檔里還可以跨文檔基本叫天天不應(yīng)。Notebook 的好處是它老插件生態(tài)成熟。jupyter_contrib_nbextensions里一大堆經(jīng)典擴(kuò)展比如代碼折疊、目錄、高亮選中的變量、自動(dòng)格式化等等都只在 Notebook 界面里比較順手。如果你的主力機(jī)器還在跑老項(xiàng)目那么新版 JupyterLab 往往無(wú)法直接復(fù)用這些 nbextensions。2.2 Lab 補(bǔ)齊了什么切換成本又有多高JupyterLab 從設(shè)計(jì)之初就奔著“集成開發(fā)環(huán)境”去文件列表在左邊主區(qū)域可以開多個(gè)窗口支持拖拽布局同一個(gè)瀏覽器窗口里同時(shí)排兩個(gè)筆記本毫無(wú)壓力。它還統(tǒng)一了很多之前要單獨(dú)裝的東西多語(yǔ)言支持、命令面板CtrlShiftC 呼出以及內(nèi)置的表格查看器、CSV 預(yù)覽等。切換成本不是我一開始想得那么低。比如原先在 Notebook 里靠 nbextensions 開啟的“目錄”功能在 Lab 里需要單獨(dú)裝jupyterlab/toc新版本甚至已經(jīng)內(nèi)置為右側(cè)的 Outline 按鈕。還有一些自定義的 nbextensions 在 Lab 里完全不認(rèn)只能等作者出 Lab 插件版本有的等了兩三年都沒(méi)消息。所以我的個(gè)人建議是如果你主要做數(shù)據(jù)分析不太依賴?yán)喜寮苯由?Lab如果維護(hù)的是三年前的 notebook 文件而且經(jīng)常要用老擴(kuò)展那就老老實(shí)實(shí)繼續(xù)用 Notebook或者兩個(gè)都裝用哪種界面取決于當(dāng)時(shí)的任務(wù)。2.3 多標(biāo)簽、文件樹、單元格拖拽實(shí)測(cè)在 Lab 里拖拽單元格比想象中順手。你先在左側(cè)文件樹點(diǎn)擊文件名打開兩個(gè) notebook然后在任意一個(gè) notebook 里選中若干單元格Shift點(diǎn)擊可以多選直接拖到另一個(gè) notebook 的目標(biāo)位置。這個(gè)操作在 Notebook 里是做不到的除非你 CtrlX、CtrlV 跨頁(yè)面復(fù)制再手動(dòng)修一下代碼里的狀態(tài)。文件樹的支持也算一個(gè)決定性差異。在 Lab 里可以直接用右鍵菜單對(duì)文件做重命名、移動(dòng)、新建文件夾甚至打開一個(gè)臨時(shí)終端日常文件操作就省得跳出瀏覽器了??旖萱I體系兩個(gè)界面基本一致。熟悉的核心組合ShiftEnter運(yùn)行單元格并選中/跳到下一個(gè)CtrlEnter只運(yùn)行當(dāng)前單元格A在上方插入B在下方插入D D連續(xù)按兩次 D 刪除單元格M進(jìn)入 Markdown 狀態(tài)Y切回 Code 狀態(tài)我建議所有用戶把這幾個(gè)刻進(jìn)肌肉記憶比點(diǎn)鼠標(biāo)效率高出一個(gè)量級(jí)。3. 讓目錄樹滾起來(lái)長(zhǎng)文檔導(dǎo)航方案實(shí)測(cè)3.1 裝插件前先搞清你用的是哪個(gè)環(huán)境“為 jupyter notebook 添加目錄”是永久熱搜。很多人遇到的尷尬是明明在網(wǎng)上搜到pip install jupyter_contrib_nbextensions裝完也執(zhí)行了 enable但打開 Notebook 還是看不到目錄。最常見(jiàn)原因是你裝了插件 A但 Jupyter 跑的是另一個(gè)環(huán)境的內(nèi)核。確認(rèn)環(huán)境一致性最直接的方法which jupyter jupyter --version pip list | grep nbextensions確保這三者指向同一個(gè) Python 環(huán)境。尤其是當(dāng)你用 conda 激活了環(huán)境但終端里的jupyter還是指向另一個(gè)路徑時(shí)插件安裝到哪里去了根本說(shuō)不清。這時(shí)候要么老老實(shí)實(shí)把jupyter重裝到當(dāng)前環(huán)境要么用python -m jupyter notebook來(lái)強(qiáng)制走當(dāng)前解釋器。3.2 兩種主流目錄插件配置與對(duì)比在 Notebook 界面主流方案是jupyter_contrib_nbextensions提供的Table of Contents (2)也叫toc2。安裝過(guò)程pip install jupyter_contrib_nbextensions jupyter contrib nbextension install --user jupyter nbextension enable toc2/main裝好后重啟 Notebook在“Nbextensions”配置頁(yè)面里找到Table of Contents (2)勾選上。它會(huì)視情況在工具欄上多一個(gè)目錄按鈕同時(shí)給 Markdown 標(biāo)題生成錨點(diǎn)。在 Lab 界面新版默認(rèn)自帶 Outline入口在右側(cè)側(cè)邊欄的書簽圖標(biāo)下方。如果你用的版本比較老可以手動(dòng)裝pip install jupyterlab jupyter labextension install jupyterlab/toc對(duì)比一下兩者體驗(yàn)功能Notebook toc2Lab Outline實(shí)時(shí)定位點(diǎn)擊目錄跳轉(zhuǎn)點(diǎn)擊目錄跳轉(zhuǎn)標(biāo)題層級(jí)折疊支持支持跟隨滾動(dòng)高亮部分版本支持支持較好與代碼塊協(xié)作僅 Markdown 標(biāo)題支持 Markdown 和代碼單元格大綱拖拽調(diào)整窗口否是我自己的實(shí)測(cè)感受是如果你只是需要一個(gè)簡(jiǎn)單的導(dǎo)航Lab 自帶 Outline 反而更干凈但老牌 toc2 可以和“折疊代碼”等 nbextensions 協(xié)同習(xí)慣后其實(shí)非常好用。3.3 目錄/折疊/大綱的一體化用法光有目錄還不夠?qū)嶋H閱讀體驗(yàn)要配合代碼折疊。在 Notebook 里Codefolding是 nbextensions 里非常受歡迎的一個(gè)擴(kuò)展點(diǎn)擊代碼單元格左側(cè)的小箭頭即可對(duì)函數(shù)體、類體或if塊進(jìn)行折疊。這個(gè)功能長(zhǎng) notebook 里特別有用尤其當(dāng)你只關(guān)心某個(gè)函數(shù)的位置時(shí)折疊掉 80% 的代碼配合目錄整個(gè)文檔的可視性會(huì)大幅提升。另外一個(gè)小技巧Markdown 標(biāo)題順序要及時(shí)更新。目錄本身是讀取 notebook 的元數(shù)據(jù)來(lái)生成的如果你在單元格里刪改了很多標(biāo)題位置最好先保存文件再刷新一下目錄避免跳轉(zhuǎn)目標(biāo)錯(cuò)亂。在 toc2 里還有一個(gè)小齒輪可以設(shè)置“標(biāo)題編號(hào)”、“超鏈接跳轉(zhuǎn)”等選項(xiàng)。我一般會(huì)開啟“標(biāo)題編號(hào)”這樣段落之間有層級(jí)感導(dǎo)出成 PDF 或者 HTML 時(shí)也更專業(yè)。在 Lab 里Outline 支持搜索過(guò)濾直接鍵入標(biāo)題關(guān)鍵字可以快速定位到長(zhǎng)文中某個(gè)章節(jié)。對(duì)于動(dòng)不動(dòng)幾百個(gè)單元格的報(bào)告型 notebook這個(gè)功能我?guī)缀趺刻於紩?huì)用到。4. 魔法命令與交互模式真正提升日常效率的十來(lái)個(gè)玩法4.1 把時(shí)間測(cè)出來(lái)%time 和 %%time 的差別很多人不知道 Jupyter 里自帶“魔法命令”以百分號(hào)開頭的指令。最開始值得記住的是%time和%%time。區(qū)別很直接%time只測(cè)量這一行的代碼耗時(shí)%%time測(cè)量整個(gè)單元格的耗時(shí)%time sum(range(10_000_000))%%time total 0 for i in range(10_000_000): total i使用場(chǎng)景是當(dāng)你在兩個(gè)寫法之間猶豫時(shí)別靠猜跑一遍%%time比什么都有說(shuō)服力。如果你想知道更精細(xì)的差異還有%%timeit它會(huì)自動(dòng)把代碼執(zhí)行很多次然后取平均適合對(duì)微性能敏感的項(xiàng)目。4.2 自動(dòng)重載模塊、一鍵運(yùn)行任意代碼串在 notebook 里開發(fā)自己的 Python 模塊時(shí)最痛苦的一件事是改了.py文件再重新導(dǎo)入模塊不會(huì)自動(dòng)更新。解決方案不是importlib.reload一遍遍手工來(lái)而是用內(nèi)置的自動(dòng)重載%load_ext autoreload %autoreload 2開了%autoreload 2之后只要.py文件的代碼發(fā)生變化運(yùn)行任意單元格前 Jupyter 會(huì)先自動(dòng)重新加載這些模塊。這幾乎是本地開發(fā)一個(gè)包時(shí)的標(biāo)配省掉的“重啟內(nèi)核”時(shí)間不計(jì)其數(shù)。注意%autoreload 1只對(duì)%aimport指定的模塊生效一般不如 2 來(lái)得省心。還有一個(gè)很有用的命令是%run它可以運(yùn)行一個(gè) Python 腳本并把腳本執(zhí)行后的命名空間留在當(dāng)前內(nèi)核里%run myscript.py如果你有一個(gè)生成的.py文件想看看里面的變量結(jié)果又不想復(fù)制粘貼這個(gè)命令非常合適。相反%load可以把一個(gè)文件內(nèi)容直接加載到當(dāng)前單元格%load myscript.py這適合你想拆解別人的腳本加載進(jìn)來(lái)后手動(dòng)調(diào)整再執(zhí)行。4.3 魔改輸出顯示%config 與裝飾器的結(jié)合Jupyter 的顯示系統(tǒng)遠(yuǎn)比 print 豐富。我最常用的是IPython.displayfrom IPython.display import display, HTML, Markdown, Image, Video display(HTML(tabletrtdA/tdtdB/td/tr/table)) display(Markdown(### 這是一個(gè)三級(jí)標(biāo)題)) display(Image(filenameplot.png))對(duì)于 DataFrameJupyter 會(huì)自動(dòng)渲染成可滾動(dòng)的表格但如果你希望控制 pandas 顯示的行數(shù)和列數(shù)可以這樣%config ZMQInteractiveShell.ast_node_interactivity all這是一個(gè)被低估的配置在普通 Python 里一行表達(dá)式只有最后一個(gè)會(huì)被輸出而在 Jupyter 里加上這個(gè)配置后單元格內(nèi)所有能求值的表達(dá)式都會(huì)顯示出來(lái)不需要一個(gè)個(gè)寫display。類似地可以限制 DataFrame 顯示import pandas as pd pd.set_option(display.max_rows, 100) pd.set_option(display.max_columns, 50)還可以通過(guò)interact創(chuàng)建交互控件這是 IPython widgets 的入口。簡(jiǎn)單示例from ipywidgets import interact interact(x(-10, 10, 1)) def f(x): return x ** 2運(yùn)行后會(huì)生成一個(gè)滑動(dòng)條調(diào)節(jié)參數(shù)立即重新執(zhí)行函數(shù)。這在做參數(shù)探索時(shí)很有用比如調(diào)整繪圖窗口大小、過(guò)濾閾值等親測(cè)能省大量“改參數(shù)重跑”的無(wú)聊循環(huán)。5. 排錯(cuò)和調(diào)代碼那些讓人抓狂的異常與診斷技巧5.1 內(nèi)核死了別慌先查這四類問(wèn)題在 Jupyter 里你可能會(huì)遇到Kernel Restarting或者“The kernel appears to have died”這類提示。內(nèi)核崩了最直接的影響是之前定義的變量全部丟失只能重跑一遍。按照經(jīng)驗(yàn)八成原因逃不開四種無(wú)限遞歸或死循環(huán)占滿了 CPU 和內(nèi)存。一次性分配超大數(shù)組比如numpy.arange(10**12)瞬間把內(nèi)存打滿。C 擴(kuò)展段錯(cuò)誤例如某個(gè)庫(kù)底層用了 ctypes 且指針寫壞。多線程/多進(jìn)程管理不當(dāng)比如在 fork 之后調(diào)用了某些非線程安全的庫(kù)。應(yīng)對(duì)手段是分級(jí)排查。先看任務(wù)管理器或top確認(rèn)是不是內(nèi)存爆了。如果只是死循環(huán)可以用菜單里的Interrupt Kernel快捷鍵是I I中斷執(zhí)行。如果已經(jīng)完全卡死那就只能重啟內(nèi)核但注意重啟前把還熱乎的結(jié)果手動(dòng)保存因?yàn)檩敵鼍彌_區(qū)不一定來(lái)得及寫盤。5.2 調(diào)試器的三種打開姿勢(shì)pdb、%debug、ipdb代碼報(bào)錯(cuò)后最常見(jiàn)的動(dòng)作是看 traceback然后一頭扎進(jìn)代碼里加 print。但 Jupyter 其實(shí)給了三套調(diào)試姿勢(shì)效率完全不同。第一種直接在異常之后執(zhí)行%debugdef buggy(): x 1 return x / 0 buggy()報(bào)錯(cuò)后在下一個(gè)單元格輸入%debug會(huì)自動(dòng)進(jìn)入事后調(diào)試模式停在出錯(cuò)的位置。這個(gè)時(shí)候你能做的是p x查看局部變量、u/d切換上下幀、n單步、c繼續(xù)、q退出。第二種是主動(dòng)在函數(shù)里加斷點(diǎn)使用IPython.core.debugger的set_tracefrom IPython.core.debugger import set_trace def func(): a 1 set_trace() # 運(yùn)行到這里會(huì)進(jìn)入交互調(diào)試界面 b 2 return a b第三種是ipdb它是把 IPython 的能力和pdb結(jié)合起來(lái)的工具pip install ipdb然后在代碼里import ipdb; ipdb.set_trace()在 Notebook 中的體驗(yàn)比純pdb好支持 Tab 補(bǔ)全和彩色輸出。說(shuō)實(shí)話一旦習(xí)慣了用%debug事后復(fù)盤就再也不想從頭加 print 再去猜了。5.3 日志、打印、變量探查三板斧調(diào)試不僅靠斷點(diǎn)還靠對(duì)變量的快速探查。三個(gè)林林總總的函數(shù)建議背下來(lái)type(obj)看類型vars(obj)看對(duì)象屬性字典dir(obj)看對(duì)象可用的屬性/方法列表尤其是dir(pd.DataFrame)這樣的用法比翻文檔還快。配合 helphelp(df.groupby)或者df.groupby?這是 Jupyter 特有的“object? 顯示文檔、object?? 顯示源碼”的便捷查詢。關(guān)于日志我長(zhǎng)期吐槽在 notebook 里只靠print做記錄效率太低。更推薦專門開一個(gè)單元配置 logging輸出到文件import logging logging.basicConfig( filename./my_jupyter.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, ) logger logging.getLogger(__name__) logger.info(這一步跑到了參數(shù): %s, param)這樣對(duì)環(huán)境進(jìn)行異常分析時(shí)至少不會(huì)因?yàn)槟硞€(gè)輸出單元格被誤刪而痛失線索。5.4 PermissionError 完整排查鏈路既然前面提到了 PermissionError這里專門放一條完整的排查鏈路方便直接照做先在當(dāng)前環(huán)境里確認(rèn)是不是只有 Jupyter 報(bào)錯(cuò)還是所有用戶目錄都寫不進(jìn)去。執(zhí)行touch ~/.jupyter/write_test rm ~/.jupyter/write_test如果這一步也報(bào)錯(cuò)那就是主目錄權(quán)限本身的問(wèn)題檢查~的屬主和權(quán)限。如果寫用戶目錄沒(méi)問(wèn)題再查 runtime 目錄jupyter --runtime-dir ls -ld $(jupyter --runtime-dir)runtime目錄下的文件要求只能是當(dāng)前用戶可寫如果發(fā)現(xiàn)一些 root 屬主的僵尸文件清空后重新啟動(dòng)。如果是在服務(wù)器上被 SELinux 或 AppArmor 限制那需要看具體的審計(jì)日志dmesg | tail -n 20里常有線索。優(yōu)先考慮把 Jupyter 的數(shù)據(jù)目錄放到用戶自己的設(shè)定路徑下繞開限制目錄。最后一個(gè)兜底方案新建一個(gè)專用目錄并從零配置mkdir -p ~/jupyter_data cd ~/jupyter_data jupyter notebook --notebook-dir$PWD如果這樣還報(bào)錯(cuò)那就說(shuō)明問(wèn)題不在 Jupyter 本身需要去查終端里實(shí)際運(yùn)行命令的用戶身份是不是你想的那個(gè)人。有時(shí)候你用某云服務(wù)器的默認(rèn)賬號(hào)登錄但 Web 界面綁定的端口啟動(dòng)腳本是另一個(gè)服務(wù)賬號(hào)自然對(duì)某些路徑?jīng)]有寫權(quán)限。6. 分享與復(fù)用導(dǎo)出、部署、插件生態(tài)里值得留意的細(xì)節(jié)6.1 導(dǎo)出為 md/PDF/HTML 時(shí)的邊界問(wèn)題在 LaTeX 和 pandoc 都安裝好的環(huán)境下Jupyter 支持直接把 notebook 導(dǎo)出成 PDF、HTML、Markdown 等格式。命令行對(duì)應(yīng)是jupyter nbconvert --to markdown my_nb.ipynb jupyter nbconvert --to html my_nb.ipynb jupyter nbconvert --to pdf my_nb.ipynb但導(dǎo)出看著簡(jiǎn)單坑不少。默認(rèn)情況下nbconvert 會(huì)保留所有的代碼輸出包括那些非常長(zhǎng)的 DataFrame 表格、動(dòng)態(tài)圖片、甚至報(bào)錯(cuò)信息。分享給別人的時(shí)候這些輸出往往是噪音。解決方案是先清理輸出再導(dǎo)出jupyter nbconvert --clear-output --to markdown my_nb.ipynb如果你想把 notebook 當(dāng)作一份報(bào)告發(fā)給團(tuán)隊(duì)參考我經(jīng)常用jupyter nbconvert --to html --template full --no-input my_nb.ipynb--no-input的意思是不渲染代碼單元格只保留輸出結(jié)果和 Markdown觀感更像一份報(bào)表。6.2 別人給的文件怎么快速?gòu)?fù)現(xiàn)requirements、npm 依賴拿到別人的.ipynb最怕的就是運(yùn)行到一半報(bào)ModuleNotFoundError。要讓 notebook 可復(fù)現(xiàn)可不能只發(fā).ipynb文件至少得帶上環(huán)境說(shuō)明。我在每次分享 notebook 項(xiàng)目時(shí)都會(huì)在同一目錄下生成兩份文件pip freeze requirements.txt jupyter nbextension list extensions.txtrequirements.txt記錄 Python 包extensions.txt記錄 Jupyter 前端插件。不要嫌它們繁瑣這是能救命的“安全網(wǎng)”。如果有人明確都是 Lab 用戶還需要注意jupyter labextension list因?yàn)榍岸藬U(kuò)展不在pip freeze范圍內(nèi)。在復(fù)現(xiàn)別人 notebook 時(shí)強(qiáng)烈建議開一個(gè)全新的干凈環(huán)境而不是往現(xiàn)有的原環(huán)境里硬塞包。沖突了很難排查而且也能逼著對(duì)方把依賴說(shuō)明白。常見(jiàn)順序conda create -n reproduction python3.11 conda activate reproduction pip install -r requirements.txt jupyter notebook my_nb.ipynb6.3 用 Jupyter 寫小系統(tǒng)的擴(kuò)展思路最后一個(gè)環(huán)節(jié)聊聊怎么在 notebook 基礎(chǔ)上再往前走一步。我發(fā)現(xiàn)不少人辛辛苦苦在 notebook 里調(diào)通了數(shù)據(jù)處理流程但每當(dāng)要把它變成可復(fù)用工具時(shí)就犯難。這里有一個(gè)比較順手的做法把 notebook 里穩(wěn)定不變的邏輯抽成一個(gè).py模塊然后在 notebook 里用%autoreload調(diào)用這個(gè)模塊。這樣既能享受 notebook 的交互性和可視化又保住了模塊化工程的可維護(hù)性。如果你想直接給非技術(shù)同事一個(gè)交互界面可以試一下 Voila 我提這個(gè)不是廣告是它確實(shí)解決了從 notebook 到 Web 面板的最后一公里。它能把 notebook 里的可視化輸出、交互控件包裝成一個(gè)臨時(shí)網(wǎng)頁(yè)不需要你額外寫 Flask 或者 Django。安裝pip install voila然后啟動(dòng)voila my_notebook.ipynb它會(huì)自動(dòng)識(shí)別代碼中表征交互的 widgets 和 matplotlib 圖表生成一個(gè)可點(diǎn)擊、可滑動(dòng)的瀏覽器應(yīng)用。適合小范圍內(nèi)的數(shù)據(jù)看板、報(bào)表工具。還有一類場(chǎng)景是定時(shí)執(zhí)行 notebook。雖然可以用papermill這樣的工具批量跑但我的經(jīng)驗(yàn)是當(dāng)任務(wù)真的需要定時(shí)調(diào)度時(shí)Jupyter 并不是最好的載體把邏輯抽成普通腳本 cron 會(huì)可靠得多。Notebook 的優(yōu)勢(shì)在于“人機(jī)交互、實(shí)時(shí)反饋”真到了無(wú)人值守的生產(chǎn)環(huán)境腳本化和參數(shù)化才是正路。以上就是我這些年圍繞 Jupyter Notebook/Lab 沉淀下來(lái)的實(shí)戰(zhàn)要點(diǎn)。寫到這里最想提醒你的一點(diǎn)是工具技巧說(shuō)到底是為了少打斷心流。安裝選型一步到位、目錄和調(diào)試兩個(gè)痛點(diǎn)提前布置好后面每次打開 Jupyter 就能把注意力全放在問(wèn)題上而不是和環(huán)境較勁。不知道你有沒(méi)有遇到過(guò)更好用的小技巧如果有歡迎在評(píng)論區(qū)給我留言我也想去試試。