簡體   English   中英

如何以編程方式將單元格添加到 IPython 或 Jupyter 筆記本?

[英]How can I programatically add cells to and IPython or Jupyter notebook?

這個問題之前已經被問過幾次,但是接受的答案表明提問者想要做一些與我不同的事情。

我正在嘗試創建一個創建和運行新單元格的 IPython 小部件。 這樣做的目的是在小部件和 IPython 單元之間提供相似性。

例如:我想要一個 SelectorWidget,它用“cell magic”填充並運行下一個單元格。

但我面臨的問題是(如果我錯了,請糾正我)IPython 不保留單元的內部數據結構。

在我看來,修改新單元格的唯一方法是:

In [1]: get_ipython().set_next_input('1')

In [2]: 1

我只想能夠自動運行該行。 但我認為set_next_input不可能做到這一點,因為它只是readline之上的一個hack。


我可以做我想做的事:

 display(Javacript('''
 
 var code = IPython.notebook.insert_cell_at_bottom('code'))
 code.execute()

 '''))

但是以這種方式驅動輸入感覺非常錯誤,因為它依賴於網絡瀏覽器來做一些非常簡單的事情。 而且您依賴於外部事件循環。

在命令行上執行此操作的等價物是啟動一個后台線程/進程/ stdin ,它可以訪問 IPython 提示符的標准輸入,並接受來自前台線程的任務以寫入前台線程自己的標准輸入。 可以這樣做,但與 Jupyter 方法有相同的問題,因為它特定於 IPython 的輸入機制。

看問題,我似乎也找不到沒有 JavaScript 的完全同步方法。

我發現run_cellrun_cell_magic可以避免 JavaScript 示例,但無法生成具有輸出的單元。

我最接近的是:

from IPython import get_ipython

def make_cell_run_code(code):
    ipython = get_ipython()
    ipython.set_next_input(code)
    ipython.run_cell(code)

code = "print('test')"
make_cell_run_code(code)

這避免了 JavaScript 但仍然有單元不同步。

使用 JavaScript,我們可以使用負索引來確保執行相同的單元格。

from IPython.display import Javascript, display

def create_and_excecute_code_cell(code=''):
    display(Javascript("""
        var code = IPython.notebook.insert_cell_at_bottom('code');
        code.set_text("{0}");
        Jupyter.notebook.execute_cells([-1]);
    """.format(code)))

create_and_excecute_code_cell("%time x=0")

從這里抄來的。 您的 JavaScript 片段對我不起作用。

其他途徑是深入IPython.core.interactiveshell.InteractiveShell

一種可能但不可移植的解決方案是對處理set_next_input kernel 消息的 JavaScript 進行猴子補丁。

(function (){
    IPython.CodeCell.prototype._handle_set_next_input = function (payload) {
        var data = {
            cell: this,
            text: payload.text,
            replace: payload.replace,
            clear_output: payload.clear_output,
        execute: payload.execute
        };
        this.events.trigger('set_next_input.Notebook', data);
    };

    var that = IPython.notebook;
    // Out with the old, in with the new
    that.events.unbind("set_next_input")
    that.events.on('set_next_input.Notebook', function (event, data) {
            if (data.replace) {
                data.cell.set_text(data.text);
                if (data.clear_output !== false) {
                  // default (undefined) is true to preserve prior behavior
                  data.cell.clear_output();
                }
            } else {
                var index = that.find_cell_index(data.cell);
                var new_cell = that.insert_cell_below('code',index);
                new_cell.set_text(data.text);
            }
        
        if (data.execute && data.execute === true) {
        new_cell.execute();
        } else {
        that.dirty = true;
        }
    });
})()

然后可以像這樣在筆記本中調用它:

with open('run_next_cell_patch.js') as f:
    # Monkey patch Jupyter with
    # set_next_input(run=True) functionality
    display(Javascript(f.read()))


from IPython import get_ipython
ip = get_ipython()
ip.payload_manager.write_payload(
        dict(
            source='set_next_input',
            text='print(1)',
            replace=False,
            execute=True
        )

)

這將在下面創建一個帶有print(1)的新單元格,並執行 output。 這比每次插入 JavaScript 來控制輸入要好一點。

有問題的是,這只適用於 Jupyter notebook 仍在為 Jupyter Lab 尋找解決方案。 從好的方面來說,我認為這在普通終端中解決起來相當簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM