Jupyter Notebook (IPython) でセルの出力をリッチにする


みなさん、SymPyは使ってますか?Jupyter NotebookでSymPyを使用すると、出力された式が美しく成形されて表示されます。

こんな感じですね。こんな風にリッチな出力ができるオブジェクトはどのように作るかご存じでしょうか?

IPythonのドキュメントに書いてありました。_repr_*_という形式の名前のメソッドを実装することで実現できます。この*に当てはまるものは、

  • svg
  • png
  • jpeg
  • html
  • javascript
  • markdown
  • latex

があります。SymPyはおそらくLaTeXで実装されているのだと思いますが、今回は分かりやすいSVGを使ってみましょう。

Face() + Eye(35, 40) + Eye(65, 40) + Mouth(50, 75)

実装は以下の通りです。

class Component():
    def __init__(self, items = None):
        self.items = items or [self]
    def __add__(self, target):
        return Component(self.items + target.items)
    def _repr_svg_(self):
        return ''.join([
            '<svg width="100" height="100">',
            *map(str, self.items),
            '</svg>'
        ])

class Face(Component):
    def __str__(self):
        return '<circle cx="50" cy="50" r="45" fill="none" stroke="black" stroke-width="1" />'

class Eye(Component):
    def __init__(self, x, y):
        super().__init__()
        self.x, self.y = x, y
    def __str__(self):
        return f'<circle cx="{self.x}" cy="{self.y}" r="5" fill="black" />'

class Mouth(Component):
    def __init__(self, x, y):
        super().__init__()
        self.x, self.y = x, y
    def __str__(self):
        return f'<path d="M{self.x} {self.y} l15 -5 h-30 Z" fill="none" stroke="black" stroke-width="1" />'