Marimo响应式笔记本入门
我是一个经常使用Jupyter Lab
写代码的Python
开发者,在单元格里逐段执行代码、实时绘制图表、随手记录Markdown注释,Jupyter
这种「所见即所得」的流畅感让我几乎忘记了传统IDE的存在。而最近发现了另一个Python
笔记本工具marimo,号称The future of Python notebooks
,开源、响应式、对git
友好、内置SQL、便于脚本执行、能够作为应用程序共享等等特点。
在其github
官网(marimo-team)上介绍的一些特点如下:
- 🚀 功能齐全: 替代
jupyter
、streamlit
、jupytext
、ipywidgets
、papermill
等更多工具 - ⚡️ 响应式: 运行一个单元格,marimo 会响应式地运行所有依赖单元格 或 将它们标记为陈旧
- 🖐️ 交互性: 绑定滑块、表格、图表等 UI 元素 到 Python——无需回调
- 🔬 可复现: 无隐藏状态,确定性执行
- 🏃♂️ 可执行: 作为 Python 脚本执行,通过命令行调整参数
- 🛜 可分享: 部署为交互式 Web 应用 或 幻灯片,通过 WASM 在浏览器中运行
- 🛢️ 为数据设计: 使用 SQL 查询数据框和数据库,过滤和搜索 数据框
- 🐍 支持 Git: 笔记本以
.py
文件格式存储 - ⌨️ 现代编辑器: GitHub Copilot、AI 助手、vim 快捷键、变量浏览器,和 更多功能
这一下勾起了我的兴趣,有些特点是传统jupyter
所不具有的,在尝试过后感觉挺有创新的,于是结合官方文档和一些例子总结了一下文档作为入门。
安装
正常使用pip
或uv
、poetry
等包管理工具创建虚拟环境后安装即可
pip install marimo
如果遇到报错:
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/, http://mirrors.aliyun.com/pypi/simple/, http://pypi.mirrors.ustc.edu.cn/simple/
Collecting marimo
ERROR: HTTP error 403 while getting http://mirrors.ustc.edu.cn/pypi/packages/b3/0b/27a9e0c56f1e1f88da9df5080b07167c4f30f728bc7f3656cdd4fc4abc4c/marimo-0.11.25-py3-none-any.whl (from http://mirrors.ustc.edu.cn/pypi/simple/marimo/) (requires-python:>=3.9)
可以尝试切换镜像源、清除代理等方式,如果还不能解决就尝试去PyPI下载whl
文件离线安装。
安装完成后就可以使用marimo
命令行来创建、编辑或运行代码了。
Example usage:
* marimo edit: create or edit notebooks
* marimo edit notebook.py: create or edit a notebook called notebook.py
* marimo run notebook.py: run a notebook as a read-only app
* marimo tutorial --help: list tutorials
Commands:
config Various commands for the marimo config.
convert Convert a Jupyter notebook or Markdown file to a...
edit Create or edit notebooks.
env Print out environment information for debugging...
export Export a notebook to various formats.
new Create a new notebook.
recover Recover a marimo notebook from JSON.
run Run a notebook as an app in read-only mode.
shell-completion Install shell completions for marimo.
tutorial Open a tutorial.
特性速览
部分内容无法作出很详细的总结,可以同步参考官方示例和文档获取详细解释
py文件格式
jupyter
的脚本格式为ipynb
,需要运行服务器后打开进行编辑、执行,使用记事本等文本编辑器打开会发现实际是json
格式,记录了一些单元格的脚本和元数据。
而marimo
笔记本是纯Python脚本(.py
),可以在IDE中正常编辑执行,这也是为什么说marimo
笔记本是git
友好的。
查看marimo
笔记本的代码可以知道marimo
是通过创建一个marimo.App
应用实例,使用app.cell
单元格装饰器来创建单元格,最后调用run
方法执行脚本。以下给出简洁的代码参考:
import marimo
__generated_with = "0.11.25"
app = marimo.App(width="full")
@app.cell
def import_statement():
import marimo as mo
from datetime import datetime
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
DAG拓扑顺序代码执行
jupyter
在单元格中编写python脚本进行执行,默认的执行顺序为单元格的前后顺序,执行后生成的变量会在全局空间中可用。
marimo
同样也是在单元格中编写脚本执行,但执行脚本的顺序不是依据单元格前后顺序,而是根据各单元格中引用的变量、对象,形成依赖关系,本质是一种有向无环图,节点就是创建的每个单元格(Cell),边就是依赖关系,Marimo
会分析每个单元格的引用(refs
)和定义(defs
),那么当单元格的变量发生变化时就可以同步执行后续所有依赖它的单元格,这就是响应式,听起来和react
框架有点相像。
这个特点感觉就非常适合执行一些特定流程的任务,官网中也提到数据流编程,比较适合一些数据流处理之类的固定脚本。
要保证一个Marimo Notebook
能够正常运行还需要一系列的约束:
- 全局变量名称必须唯一。如果在多个单元格中定义同一名称的变量将会无法确定执行顺序,所以像
+=
、-=
这类会重新赋值的运算符就不能够使用了。解决方法除了合并操作变量的单元格之外,可以将变量设置为下划线前缀的私有变量,这类私有变量只在定义的单元格中有效,在其他单元格中不能够使用。 - 删除单元格会同步删除变量。这和
Jupyter
也是不同的,删除单元格会删除其定义的全局变量,然后运行引用它们的所有单元格。这可以防止从编辑器中删除单元格但未从程序内存中删除状态时出现严重错误。 - 不允许单元格之间出现循环引用。防止出现死循环
Marimo
不跟踪属性和变量变更。简单来讲,在对一个单元格修改并执行后,Marimo
会对变量是否进行了显式的修改(可以认为是重新赋值)决定触发响应式执行,调用对象的属性或执行函数等其他方式可以认为是对变量的隐式修改,并不会触发响应式执行。对于属性,是因为还无法可靠的跟踪所有属性的变更。而变量的变更,是因为没办法在不运行对象的情况下知道是否会改变对象,所以也无法触发响应式执行。
上面提到marimo
具有不跟踪属性和变量变更的特点,结合这种DAG式的执行顺序,我存在一个疑问。在有向无环图中,无依赖关系(并行)的节点的运行顺序是怎样的?会按单元格的前后顺序决定吗?借助面板中对单元格执行顺序按照有向无环图绘制出来了,如下给出一个例子:
上面单元格2定义了一个类A
,然后在单元格3和4分别修改了类的name
属性,最后在单元格5中输出属性值,我本质想测试的是marimo
对于像单元格3、4这种并行的单元格是如何确定执行顺序的,并行?还是按单元格顺序?我的测试并没有得到答案。首先单元格5其实并不能跟踪到类A
的属性变化,所以上面图中单元格5并没有依赖3、4单元格的执行。意味着在修改属性前单元格5就已经执行了,应该输出wwh
。但是此时如果把单元格的输出换成process_name(A.name)
取消注释,输出结果就变成了wwh-1
,即便在单元格3改变属性前添加time.sleep(2)
等待几秒,结果仍然是wwh-1
,这很反直觉。我并不知道在这种情况下,最后代码的执行顺序是否能够按照预算的那样执行,所以这块还是需要特别注意。
多种单元格类型
jupyter
的单元格类型最主要支持代码和markdown类型,而marimo
的除了代码单元格,还有markdown
和SQL
单元格,实际其实也是代码类单元格,只是分别使用了mo.md
、mo.sql
方法进行包装。
对Markdown的支持很友好,能够渲染LaTeX,支持python字符串模板渲染,能够将绘图图表和UI组件渲染为HTML。
能够通过单元格定义SQL数据库连接,并在单元格中直接编写查询语句,查询结果输出为变量,在前端的UI交互中也很方便。
响应式UI交互
marimo
另一特点是支持定义交互式组件,并且与组件的交互会响应式的反映到使用的单元格中,这在jupyter
中也是有相关的库能够做到。而marimo
中集合的一些UI组件用起来会非常方便,我也尝试整理了marimo
的ui
模块中已有的各种UI组件。
UI布局方法
有诸如hstack
、vstack
等方法对UI组件进行布局。
多种绘图支持
marimo
支持多种常用的绘图库,包括Matplotlib、Plotly、Seaborn 和 Altair。
其他案例
官方给出了很多教程,可以在官网中进行查看,在本地使用marimo tutorial [...]
运行案例。
tutorial
- intro
- dataflow
- ui
- markdown
- plots
- sql
- layout
- fileformat
- markdown-format
- for-jupyter-users
总结
marimo
独特的DAG式执行方式在某些场景下非常贴合,这和jupyter
非常不同,但我觉得这并不意味着孰优孰劣,marimo
虽然能够按照依赖关系确定执行顺序并响应式的执行,但并不能完全的跟踪所有的变化,为此需要按照一些建议遵从特定的编码习惯,否则很容易为此得到错误的结果,这相当于只做成了一半。此外,以下列举了一些使用感受和对比jupyter
的优缺点。
缺点:
- 无法在多个脚本中便捷的切换。对于非
marimo
笔记本格式的python脚本无法直接在页面中打开,侧边栏中只显示进行编码过后的字符串内容,打开笔记本后的响应速度似乎有些慢,给我一种并不那么流畅的感觉 - 不支持魔术方法,貌似并没有提供对终端命令的支持
- 无法保存上次执行结果。纯python脚本导致无法在笔记本中保存上次的执行结果,每次打开需要重新运行才能看到结果
- 单元格状态没有变化时,无法重新全部执行
优点:
- 对当前变量的状态展示、单元格的依赖视图操作较好
- 直接支持使用
uv
、pip
等包管理器进行包的安装、移除,对包的依赖管理非常方便 - DAG拓扑顺序执行非常贴合一些具有依赖关系的任务脚本,这是与
jupyter
相比最独特的优势
基于上述的探索,感觉转而使用marimo
的主要成本不在于熟悉其编码难度,主要是习惯其独特的执行顺序和唯一变量机制等特性。所以感觉大多数情况下jupyter
就已经非常适用了,特别的一些场景下marimo
可以完胜jupyter
,但marimo
还"年轻"啊,还有很多问题和困难点需要解决,再过一段时间生态完善后也许会成为非常好用的生产力工具。
评论区