Jupyter(ipython)のマジックコマンドを自作する

Jupyterには便利なマジックコマンド(%や%%を付けて呼び出すアレです)がたくさんありますが、あれを自作する方法を紹介します。

ドキュメントは IPythonのドキュメントのこちらを参照します。
参考: Defining custom magics — IPython 8.14.0 documentation

簡単な方法は、register_line_magic, register_cell_magic, register_line_cell_magic の3種のデコレーターをマジックコマンドとして使いたい関数につけることです。

register_line_magicはその行の文字列を格納する引数を1個だけ、register_cell_magicとregister_line_cell_magicは、マジックコマンドと同じ行の文字列を格納する引数と、セル内の文字列を格納する引数の2個をもちます。

ざっと、受け取った文字列をprintするだけのコマンドを作ってみましょう。3種類それぞれのサンプルです。

from IPython.core.magic import register_line_magic
from IPython.core.magic import register_cell_magic
from IPython.core.magic import register_line_cell_magic


@register_line_magic
def line_magic(line):
    print(line)


@register_cell_magic
def cell_magic(line, cell):
    print(f"line: {line}")
    print(f"cell:\n{cell}")


@register_line_cell_magic
def line_cell_magic(line, cell=None):
    print(f"line: {line}")
    if cell:
        print(f"cell:\n{cell}")

順番に使ってみます。

%line_magic ラインマジックテスト
print()
# 以下出力
# ラインマジックテスト
%%cell_magic セルマジックと同じ行のテキスト
セルマジック内のテキスト
その2行目

# 以下出力。
"""
line: セルマジックと同じ行のテキスト
cell:
セルマジック内のテキスト
その2行目
"""
%line_cell_magic ラインマジックとして動作させた場合

# 以下出力
# line: ラインマジックとして動作させた場合
%%line_cell_magic セルマジックとして動作させた場合。
セルの中身

# 以下出力
"""
line: セルマジックとして動作させた場合。
cell:
セルの中身
"""

めっちゃ簡単ですね。

最初のマジックコマンドを定義したコードをPythonファイルとして保存して、import可能なディレクトリに置いておくと、インポートして使うこともできる様になります。例えば、 my_magic.py というファイル名で保存しておけば次の様に使えます。

import my_magic


%line_magic 読み込んだモジュールのマジックコマンドが使える
# 読み込んだモジュールのマジックコマンドが使える

my_magic.line_magic("普通の関数としても呼び出せる")
# 普通の関数としても呼び出せる

さて、通常マジックコマンドをライブラリ等から読み込んで使う場合、この様にimport するのではなく、%load_ext して使うことが多いと思います。これは、先ほどあげたドキュメントのページでベストプラクティスとされているのがその方式だからです。@register_* のデコレーターで直接登録する上記の方法は推奨されてないんですね。

その代わりにどうするかというと、 load_ipython_extension というメソッドを持つpythonファイルを作り、このメソッドの中で定義した関数たちを register_magic_function でマジックコマンドへ登録していきます。

引数は順に、登録する関数本体、コマンドの種類(省略したら’line’)、マジックコマンドとして呼び出す時の名前(省略したら元の関数名)です。

例えば、 my_ext.py というファイルを作りその中を次の様にします。

def load_ipython_extension(ipython):
    ipython.register_magic_function(
        line,
        magic_kind='line',
        magic_name='line_magic'
    )

    ipython.register_magic_function(
        cell,
        magic_kind='cell',
        magic_name='cell_magic'
    )

    ipython.register_magic_function(
        line_cell,
        magic_kind='line_cell',
        magic_name='line_cell_magic'
    )


def line(line):
    print(line)


def cell(line, cell):
    print(f"line: {line}")
    print(f"cell:\n{cell}")


def line_cell(line, cell=None):
    print(f"line: {line}")
    if cell:
        print(f"cell:\n{cell}")

各メソッドそれぞれにはデコレーターはつきません。

この様なファイルを用意すると、load_ext で読み込んだ時に、load_ipython_extension が実行されて、その中でマジックコマンドの登録が行われます。結果、次の様に使えます。

%load_ext my_ext


%line_magic ロードしたマジックコマンドが使えた
# ロードしたマジックコマンドが使えた

先ほどのimportした場合との挙動の違いとしては、これは明示的にマジックコマンドの読み込みだけを行っているので、各メソッドはインポートはされておらず、個々のメソッドの、line, cell, line_cell は名前空間に登録されてないということです。(マジックコマンドとして登録された、line_magic, cell_magic, line_cell_magic の名前でなら通常のメソッドと同じ様に使うことも可能です)

以上が簡単なマジックコマンドの作り方になります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です