侧边栏壁纸
  • 累计撰写 21 篇文章
  • 累计创建 14 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Marimo响应式笔记本入门

whfree
2025-03-23 / 0 评论 / 2 点赞 / 7 阅读 / 9813 字

Marimo响应式笔记本入门

我是一个经常使用Jupyter Lab写代码的Python开发者,在单元格里逐段执行代码、实时绘制图表、随手记录Markdown注释,Jupyter这种「所见即所得」的流畅感让我几乎忘记了传统IDE的存在。而最近发现了另一个Python笔记本工具marimo,号称The future of Python notebooks,开源、响应式、对git友好、内置SQL、便于脚本执行、能够作为应用程序共享等等特点。

在其github官网(marimo-team)上介绍的一些特点如下:

这一下勾起了我的兴趣,有些特点是传统jupyter所不具有的,在尝试过后感觉挺有创新的,于是结合官方文档和一些例子总结了一下文档作为入门。

安装

正常使用pipuvpoetry等包管理工具创建虚拟环境后安装即可

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式的执行顺序,我存在一个疑问。在有向无环图中,无依赖关系(并行)的节点的运行顺序是怎样的?会按单元格的前后顺序决定吗?借助面板中对单元格执行顺序按照有向无环图绘制出来了,如下给出一个例子:

image-20250323163901094

上面单元格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的除了代码单元格,还有markdownSQL单元格,实际其实也是代码类单元格,只是分别使用了mo.mdmo.sql方法进行包装。

对Markdown的支持很友好,能够渲染LaTeX,支持python字符串模板渲染,能够将绘图图表和UI组件渲染为HTML。

能够通过单元格定义SQL数据库连接,并在单元格中直接编写查询语句,查询结果输出为变量,在前端的UI交互中也很方便。

响应式UI交互

marimo另一特点是支持定义交互式组件,并且与组件的交互会响应式的反映到使用的单元格中,这在jupyter中也是有相关的库能够做到。而marimo中集合的一些UI组件用起来会非常方便,我也尝试整理了marimoui模块中已有的各种UI组件。

UI布局方法

有诸如hstackvstack等方法对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脚本导致无法在笔记本中保存上次的执行结果,每次打开需要重新运行才能看到结果
  • 单元格状态没有变化时,无法重新全部执行

优点

  • 对当前变量的状态展示、单元格的依赖视图操作较好
  • 直接支持使用uvpip等包管理器进行包的安装、移除,对包的依赖管理非常方便
  • DAG拓扑顺序执行非常贴合一些具有依赖关系的任务脚本,这是与jupyter相比最独特的优势

基于上述的探索,感觉转而使用marimo的主要成本不在于熟悉其编码难度,主要是习惯其独特的执行顺序和唯一变量机制等特性。所以感觉大多数情况下jupyter就已经非常适用了,特别的一些场景下marimo可以完胜jupyter,但marimo还"年轻"啊,还有很多问题和困难点需要解决,再过一段时间生态完善后也许会成为非常好用的生产力工具。

2

评论区