Jupyter:编写一个自定义魔术来修改它所在的单元格的内容
Jupyter: Write a custom magic that modifies the contents of the cell it's in
在 Jupyter notebook 中,有一些内置的魔法可以改变 notebook 单元格的内容。例如,%load
魔法将当前单元格的内容替换为文件系统上文件的内容。
我如何编写一个自定义魔术命令来做类似的事情?
到目前为止我已经打印了一些东西到标准输出
def tutorial_asset(line):
print('hello world')
def load_ipython_extension(ipython):
ipython.register_magic_function(tutorial_asset, 'line')
我可以用 %load_ext tutorial_asset
加载它。但是从那里我迷路了。
[编辑]:
我找到了进入交互式 shell 实例的方法:
@magics_class
class MyMagics(Magics):
@line_magic
def tutorial_asset(self, parameters):
self.shell
self.shell
对象似乎可以完全访问笔记本中的单元格集,但我能找到修改单元格的唯一方法是 self.shell.set_next_input('print("hello world")')
。这还不够,因为在 Jupyter notebook 中,该输入单元格被跳过,并且它不会覆盖输入单元格,而是在它之后创建一个新的输入单元格。
这很好,但如果我第二次 运行 笔记本,它会创建另一个加载了相同文件的输入单元格,这很烦人。我可以让它只加载一次吗,比如说,通过检查内容是否已经在下一个单元格中?
编辑:进一步挖掘后,我发现当前的笔记本版本无法同时做到这两项。
嗯,这有点棘手...看IPython代码,好像要替换单元格需要用set_next_input
,run_cell
如果你真的想要 运行 一些代码。但是,我无法让两者同时工作 - 看起来 set_next_input
总是赢。
深入研究代码,Web 前端支持 optional clearing of the output on set_next_input
. However, the kernel doesn't yet support setting this flag(因此默认操作始终清除输出)。要做得更好,需要给 ipykernel 打个补丁。
因此,我最好的是以下代码,使用 jupyter notebook 4.2.1 版:
from __future__ import print_function
from IPython.core.magic import Magics, magics_class, line_magic
@magics_class
class MyMagics(Magics):
@line_magic
def lmagic(self, line):
"Replace current line with new output"
raw_code = 'print("Hello world!")'
# Comment out this line if you actually want to run the code.
self.shell.set_next_input('# %lmagic\n{}'.format(raw_code), replace=True)
# Uncomment this line if you want to run the code instead.
# self.shell.run_cell(raw_code, store_history=False)
ip = get_ipython()
ip.register_magics(MyMagics)
这为您提供了一个神奇的命令 lmagic
,它将替换当前单元格或 运行 raw_code
,具体取决于您注释掉的代码位。
在 Jupyter notebook 中,有一些内置的魔法可以改变 notebook 单元格的内容。例如,%load
魔法将当前单元格的内容替换为文件系统上文件的内容。
我如何编写一个自定义魔术命令来做类似的事情?
到目前为止我已经打印了一些东西到标准输出
def tutorial_asset(line):
print('hello world')
def load_ipython_extension(ipython):
ipython.register_magic_function(tutorial_asset, 'line')
我可以用 %load_ext tutorial_asset
加载它。但是从那里我迷路了。
[编辑]:
我找到了进入交互式 shell 实例的方法:
@magics_class
class MyMagics(Magics):
@line_magic
def tutorial_asset(self, parameters):
self.shell
self.shell
对象似乎可以完全访问笔记本中的单元格集,但我能找到修改单元格的唯一方法是 self.shell.set_next_input('print("hello world")')
。这还不够,因为在 Jupyter notebook 中,该输入单元格被跳过,并且它不会覆盖输入单元格,而是在它之后创建一个新的输入单元格。
这很好,但如果我第二次 运行 笔记本,它会创建另一个加载了相同文件的输入单元格,这很烦人。我可以让它只加载一次吗,比如说,通过检查内容是否已经在下一个单元格中?
编辑:进一步挖掘后,我发现当前的笔记本版本无法同时做到这两项。
嗯,这有点棘手...看IPython代码,好像要替换单元格需要用set_next_input
,run_cell
如果你真的想要 运行 一些代码。但是,我无法让两者同时工作 - 看起来 set_next_input
总是赢。
深入研究代码,Web 前端支持 optional clearing of the output on set_next_input
. However, the kernel doesn't yet support setting this flag(因此默认操作始终清除输出)。要做得更好,需要给 ipykernel 打个补丁。
因此,我最好的是以下代码,使用 jupyter notebook 4.2.1 版:
from __future__ import print_function
from IPython.core.magic import Magics, magics_class, line_magic
@magics_class
class MyMagics(Magics):
@line_magic
def lmagic(self, line):
"Replace current line with new output"
raw_code = 'print("Hello world!")'
# Comment out this line if you actually want to run the code.
self.shell.set_next_input('# %lmagic\n{}'.format(raw_code), replace=True)
# Uncomment this line if you want to run the code instead.
# self.shell.run_cell(raw_code, store_history=False)
ip = get_ipython()
ip.register_magics(MyMagics)
这为您提供了一个神奇的命令 lmagic
,它将替换当前单元格或 运行 raw_code
,具体取决于您注释掉的代码位。