如何以编程方式将单元格添加到 IPython 或 Jupyter 笔记本?

时间:2021-01-23 10:37:50

标签: python jupyter-notebook ipython

这个问题之前已经被问过几次了,然而接受的答案表明提问者希望对我做一些不同的事情。

我正在尝试创建一个 IPython 小部件来创建并运行一个新单元。这样做的目的是在小部件和 IPython 单元之间提供一种相似性。

例如:我想要一个 SelectorWidget 来填充并运行带有“单元格魔法”的下一个单元格。

但我面临的问题是(如果我错了,请纠正我)IPython 不保留单元格的内部数据结构。

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

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

In [2]: 1

我只是希望能够自动运行该行。但我认为 set_next_input 不可能实现,因为它只是对 readline 的一个黑客攻击。


我可以做我想做的事:

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

 '''))

但是以这种方式驱动输入感觉非常错误,因为它依赖于网络浏览器来做一些非常简单的事情。而你依赖于一个外部事件循环。

相当于在命令行上执行此操作将启动一个后台线程/进程/协程,它可以访问 IPython 提示的 stdin 并且将接受来自前台线程的任务以写入前台线程自己的标准输入。这是可能的,但与 Jupyter 方法有相同的问题,因为它特定于 IPython 的输入机制。

2 个答案:

答案 0 :(得分:1)

看问题,我似乎也找不到没有 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")

我抄袭了 from here。您的 JavaScript 代码段对我不起作用。

其他途径是深入了解IPython.core.interactiveshell.InteractiveShell

答案 1 :(得分:0)

一种可能但不可移植的解决方案是对处理 """ The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/3.1/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include("home.urls")) ] 内核消息的 JavaScript 进行猴子补丁。

set_next_input

然后可以像这样在笔记本中调用它:

(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 ) ) 的新单元格,并执行输出。这比每次插入 JavaScript 来控制输入要好一些。

有问题的是,这只适用于 Jupyter notebook。仍在为 Jupyter Lab 寻找解决方案。从好的方面来说,我认为在普通终端中解决这个问题可能相当简单。