marimo 迁移指南:从 Jupyter、Streamlit、Jupytext、Papermill 平滑切换
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
marimo 是一个"单一工具"替代方案,目标是用一个工具取代jupyter、streamlit、jupytext、ipywidgets、papermill等一整套生态。本文以 docs/guides/coming_from 系列文档为主体,系统性对比 marimo 与 Jupyter、Streamlit、Jupytext、Papermill 的核心差异,给出可复制的迁移示例、等价 API 对照表与执行模型适配建议,帮助读者在迁移后直接上手 marimo 的响应式执行、纯 Python 文件格式、CLI 参数注入与程序化执行能力。
总览:marimo 在工具生态中的定位
marimo 的核心主张是:notebook-first 的编程环境,同时天然具备脚本与应用能力。它取代的不只是某一个工具,而是从交互式开发、应用搭建、文本化存储到流水线执行的整条链路:
| 被替代工具 | marimo 中的对应能力 |
|---|---|
| Jupyter | 响应式 notebook 环境,单元之间通过变量依赖构成数据流图 |
| Streamlit | 同一份 notebook 直接以marimo run运行成交互式 Web 应用 |
| Jupytext | notebook 原生以纯 Python(.py)存储,无需配对同步 |
| Papermill | 内置mo.cli_args/mo.query_params参数注入与程序化执行 API |
从仓库结构看,marimo/_runtime 承载了上述能力的运行时核心:cli_args与query_params的实现位于 marimo/_runtime/runtime.py,参数对象的类型定义在 marimo/_runtime/params.py 附近,CLI 的run、edit、convert、export等子命令定义于 marimo/_cli/cli.py。后续各节将围绕这些实现展开说明。
一、从 Jupyter 迁移:理解 marimo 的执行模型
1.1 核心差异:REPL 与响应式数据流图
Jupyter 本质是一个REPL:代码块逐段执行,Jupyter 并不理解不同代码块之间的依赖关系。这种模型容易累积"隐藏状态"——你可能不小心乱序执行单元,或运行/删除了某个单元却忘记重跑依赖它的单元,导致 notebook 难以复现。
marimo 则不同:它在运行时把 notebook 的所有单元"编译"成一张以变量声明与引用为边的有向图(见 docs/guides/reactivity.md)。单元之间由变量依赖自动关联,从而消除隐藏状态;也正是这张图,让 notebook 可以被复用作应用与脚本。
默认行为:运行某个单元时,所有读取其变量的其他单元会自动重跑,以保证代码与输出始终同步。刚迁移时这可能需要一段时间适应,仓库提供了两种缓解手段:
- 配置运行时:参考 docs/guides/configuration/runtime_configuration.md,可关闭"启动时自动运行""执行单元后自动运行"等选项。即使关闭自动运行,marimo 仍会跟踪跨单元依赖,把受影响单元标记为 stale,你只需点击一次按钮即可重跑所有过期单元。
- 用
mo.stop手动控制执行:mo.stop(condition)在条件满足时中止当前单元继续执行,常用于"必须点击按钮才执行昂贵计算"的场景:
# 如果 condition 为 True,mo.stop() 返回后单元停止执行 mo.stop(condition) # 若 condition 为 True,这行不会执行 expensive_function_call()结合mo.ui.run_button()的典型用法(这也是 docs/guides/coming_from/jupyter.md 中给出的示例):
# 单元 A run_button = mo.ui.run_button() run_button # 单元 B mo.stop(not run_button.value, mo.md("点击 👆 运行此单元")) mo.md("你点击了按钮!🎉")针对"昂贵 notebook"的更多适配技巧,见 docs/guides/expensive_notebooks.md。
1.2 变量重定义约束与 dataframe 迁移技巧
由于 marimo 需要确定单元的排序,同一个变量不能在多个单元中被重复定义。仓库建议的三种适配方式:
- 尽可能把代码封装进函数,减少全局变量;
- 用下划线前缀(
_my_temporary)声明单元局部变量; - 在定义变量的那个单元内完成对该变量的修改。
对于 dataframe,Jupyter 用户习惯在多个单元里反复改写同一个df,这在 marimo 中行不通。正确做法是把操作合并进单个单元:
df = pd.DataFrame({"my_column": [1, 2]}) df["another_column"] = [3, 4]若确实需要跨单元变换,可以给 dataframe 起别名:
df = pd.DataFrame({"my_column": [1, 2]}) augmented_df = df augmented_df["another_column"] = [3, 4]1.3 文件格式:Python 而非 JSON
marimo 把 notebook 存成 Python 文件而非 JSON,由此获得的能力包括:用 git 版本化、作为脚本执行(见 docs/guides/scripts.md)、在其他 Python 文件中导入具名单元。代价是输出(如图表)不保存在文件里。
- 保存输出快照:启用编辑器中的 "Auto-download as HTML/IPYNB" 设置(见 docs/guides/configuration/index.md),marimo 会定期把 notebook 快照为 HTML 或 ipynb,存放在 notebook 目录下的
__marimo__文件夹;也可用marimo export命令手动导出。 - 在 GitHub 上预览输出:可以把导出的 ipynb 提交进版本控制,GitHub 会直接渲染输出。
- 存储细节提醒:不同指南对快照目录的表述略有差异(jupyter.md 中为
__marimo__文件夹,papermill.md 中为.marimo/目录),迁移时请以你安装版本编辑器的实际行为为准。
1.4 转换命令:双向转换
Jupyter → marimo(命令行):
marimo convert your_notebook.ipynb -o your_notebook.pyPython 脚本 → marimo:
marimo convert your_script.py -o your_notebook.py- py:percent 格式:脚本使用
# %%单元标记时,marimo 会转换为多单元 notebook(需安装 jupytext); - 普通 Python 脚本:没有单元标记时转换为单单元 notebook。
用 uv 进行 py:percent 转换:
uvx --with=jupytext marimo convert your_script.py -o your_notebook.pymarimo → Jupyter:
marimo export ipynb notebook.py -o notebook.ipynb注意:部分 marimo 库函数(尤其是 UI 元素)在 Jupyter notebook 中无法工作。
1.5 Magic 命令替代方案
marimo notebook 就是纯 Python,因此不支持 IPython magic 命令,也不支持!前缀的 shell 命令。替代方式如下:
import subprocess # 等价于运行 "ls -l" subprocess.run(["ls", "-l"])常见 magic 命令的等价替代:
| Magic 命令 | marimo/Python 替代 |
|---|---|
| %cd | os.chdir(),另见mo.notebook_dir() |
| %clear | 右键单元或切换单元操作菜单 |
| %debug | Python 内置调试器breakpoint() |
| %env | os.environ |
| %load | 使用 Python import |
| %load_ext | 无对应 |
| %autoreload | marimo 的模块自动重载(见 docs/guides/editor_features/module_autoreloading.md) |
| %matplotlib | marimo 自动显示绘图 |
| %pwd | os.getcwd() |
| %pip | marimo 内置包管理(见 docs/guides/editor_features/package_management.md) |
| %who_ls | dir()、globals()、mo.refs()、mo.defs() |
| %system | subprocess.run() |
| %%time | time.perf_counter()或 timeit 模块 |
| %%timeit | timeit 模块 |
| %%writefile | with open("file.txt", "w") as f: f.write(...) |
| %%capture | mo.capture_stdout()、mo.capture_stderr() |
| %%html | mo.Html()或mo.md() |
| %%latex | mo.md(r'$$...$$') |
包安装则直接使用 marimo 的包管理侧边栏面板,它会安装到当前环境。
二、从 Streamlit 迁移:notebook-first 的数据应用
2.1 关键差异
Streamlit 是应用框架,marimo 首先是响应式 notebook 环境,两者的定位差异带来一系列不同:
- Notebook 即应用:Streamlit 开发数据应用时常先原型于 Jupyter、再重构迁移;marimo 中每个 notebook 本身就是应用,用
marimo run即可运行,无需迁移步骤。 - 执行性能:marimo 采用响应式执行模型,交互或代码变化时只重跑维持 notebook 最新状态所需的最小单元集合;Streamlit 每次交互都重跑整个脚本,容易引发性能问题。
- 文件格式:两者都是纯 Python 文件,但 marimo 的文件结构支持更细粒度的响应式;且 marimo 文件可以直接作为 Python 脚本执行、可以被其他程序 import 复用(例如通过
marimo.Cell.run复用单元,见 docs/api/cell.md)。 - UI 元素:两者都提供滑块、文本框、表格等元素。Streamlit 创建元素即自动输出;marimo 将"创建"与"显示"分离,可以自由组合布局、构造高阶元素,甚至把同一个元素输出两次。
- 自定义组件:marimo 支持 anywidget 规范,可直接复用为 Jupyter 生态开发的 widget;Streamlit 使用自己的自定义组件体系。
- 内置编辑器:marimo 自带专为数据工作设计的内置编辑器(见 docs/guides/editor_features/index.md),Streamlit 依赖外部编辑器。
- 数据工作流:marimo 的 notebook 环境适合迭代式探索开发,内置原生 SQL 支持(见 docs/guides/working_with_data/sql.md);Streamlit 专用于构建独立数据应用。
2.2 常见 Streamlit 功能的 marimo 等价写法
1. 显示文本
# Streamlit import streamlit as st st.markdown("# Greetings\nHello world") # marimo import marimo as mo mo.md("# Greetings\nHello world")2. 显示数据
# Streamlit st.dataframe(df) # marimo:单元最后一个表达式自动显示 df3. 输入控件
# Streamlit age = st.slider("How old are you?", 0, 130, 25) # marimo:创建与显示分离,值通过 .value 访问,可与文本任意组合 age = mo.ui.slider(label="How old are you?", start=0, stop=130, value=25) mo.md(f"One more question: {age}")4. 按钮
# Streamlit if st.button("Click me"): st.write("Button clicked!") # marimo:按钮对象 + 另一单元读取 .value button = mo.ui.run_button("Click me") # 在另一个单元中 if button.value: mo.output.replace(mo.md("Button clicked!")) # 或者 mo.md("Button clicked!") if button.value else None5. 基本布局
# Streamlit col1, col2 = st.columns(2) with col1: st.write("Column 1") with col2: st.write("Column 2") # marimo mo.hstack([ mo.md("Column 1"), mo.md("Column 2") ])6. 高级布局(折叠面板)
# Streamlit with st.expander("Expand me"): st.write("Hello from the expander!") # marimo:可无限嵌套,组合更灵活 mo.accordion({"Expand me": "Hello from the expander!"})7. 绘图
# Streamlit import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.plot([1, 2, 3, 4]) st.pyplot(fig) # marimo:最后一个表达式自动显示 import matplotlib.pyplot as plt plt.plot([1, 2, 3, 4]) plt.gca()8. 缓存
# Streamlit @st.cache_data def expensive_computation(args): ... # marimo @mo.cache def expensive_computation(args): ...marimo 提供mo.cache、mo.lru_cache(缓存函数返回值)以及mo.persistent_cache(把变量持久化到磁盘),详见 docs/api/caching.md。
9. 会话状态
Streamlit 用st.session_state持久化数据;marimo 中直接用普通 Python 变量即可——对于未被重新执行的单元,notebook 会为其维护一致的状态。
10. 作为应用运行
# Streamlit streamlit run your_app.py # marimo marimo run your_notebook.py2.3 迁移后需要记住的四条原则
- 单元在依赖变化时自动重跑,但只有受影响单元会重跑,效率远高于朴素实现的 Streamlit 程序;
- marimo 的 UI 元素通常赋值给变量,通过
.value属性读取值; mo.md()非常灵活,可以用 f-string 同时组合文本与 UI 元素;- notebook-first 的定位让 marimo 适用于各种数据工作:探索性数据分析、数据工程、机器学习实验与训练、库文档与示例等。
三、从 Jupytext 迁移:原生文本化存储
3.1 与 Jupytext 的本质区别
Jupytext 依赖配对与同步:ipynb与文本表示之间需要额外配置、可能产生同步问题。marimo notebook默认就以.py存储,不存在同步问题。另外 Jupytext 服务于 IPython/Jupyter notebook,marimo notebook 并不基于 IPython/Jupyter。
marimo 还有 markdown 文件格式,可通过命令行marimo tutorial markdown-format学习;markdown 形式下没有特殊语法,在 GitHub 等平台渲染效果良好。
3.2 逐项对比
| 场景 | Jupytext | marimo |
|---|---|---|
| Notebook 格式 | 用注释或特殊标记定义单元类型 | 默认纯 Python(.py)文件,用标准 Python 语法(装饰器、函数)定义单元;markdown 形式(.md)无特殊语法,GitHub 渲染良好 |
从.ipynb转换 | jupytext --to py notebook.ipynb | marimo convert notebook.ipynb > notebook.py |
| py:percent 转换 | — | marimo convert percent_notebook.py -o marimo_notebook.py(需 jupytext);uv 下为uvx --with=jupytext marimo convert percent_notebook.py -o marimo_notebook.py |
导出到.ipynb | jupytext --to notebook.ipynb notebook.py | marimo export ipynb notebook.py > notebook.ipynb |
| 编辑 | 需要在.ipynb与.py之间同步 | 直接在 marimo 编辑器marimo edit notebook.py编辑,读写同一文件 |
| 执行 | 用 Jupyter 交互编辑,或用 Papermill 命令行执行 | 交互运行marimo notebook.py;脚本运行python notebook.py;应用运行marimo run notebook.py,并内置 CLI 参数支持(见 docs/api/cli_args.md) |
| 版本控制 | ipynb 默认是 JSON,git diff 难以阅读;需用 Jupytext 配对以获得小 diff | 已是.py格式,天然 git 友好,小改动保证产生小 diff |
| Markdown 与代码单元 | 需要特殊标记或格式区分单元类型 | 用mo.md("...")写 Markdown,用mo.md(f"...")插值 Python 值,无魔法语法 |
| 部署 | 需迁移到 Voila、Streamlit 等其他库 | 直接用marimo run部署为交互式 Web 应用 |
四、从 Papermill 迁移:参数化与程序化执行
4.1 参数化 notebook:CLI 参数与 URL 查询参数
Papermill 通过定义 "parameters" 单元并在运行时注入值来实现参数化。marimo 提供两种内置方式:
方式一:命令行参数mo.cli_args()
import marimo as mo # 读取 CLI 参数 args = mo.cli_args() param1 = args.get("param1", "default_value")作为脚本运行:
python notebook.py -- --param1 value1作为应用运行:
marimo run notebook.py -- --param1 value1从源码看,mo.cli_args()返回只读的CLIArgs字典(定义于 marimo/_runtime/params.py 附近,运行时实现见 marimo/_runtime/runtime.py),不能在 notebook 内修改。edit、run等 CLI 子命令都接受nargs=-1的透传参数(见 marimo/_cli/cli.py),--之后的内容即注入 notebook 的 argv。
方式二:URL 查询参数mo.query_params()
import marimo as mo # 读取查询参数 params = mo.query_params() param1 = params.get("param1", "default_value")marimo run notebook.py然后访问:
http://your-app-url/?param1=value1与CLIArgs不同,QueryParams是可变的:修改它会被持久化到前端 URL,且引用该对象的其他单元会自动重跑。源码 marimo/_runtime/runtime.py 的 docstring 给出了典型用法——让文本框与 URL 参数双向同步:
# 独立单元 query_params = mo.query_params() # 另一单元 search = mo.ui.text( value=query_params["search"] or "", on_change=lambda value: query_params.set("search", value), ) search4.2 程序化执行 notebook
marimo notebook 是纯 Python 文件,天然支持程序化执行:
1. 运行具名单元(marimo.Cell.run)
from my_notebook import my_cell # last_expression 是该单元的视觉输出 # definitions 是该单元定义的变量字典 last_expression, definitions = my_cell.run()该 API 也支持为单元输入传参,完整示例见 docs/api/cell.md 中marimo.Cell.run的说明。
2. 程序化运行整个应用并覆盖定义(app.run)
import marimo from my_notebook import app # 覆盖单元定义后运行 # 这会完全替换定义这些变量的单元的返回定义 outputs, defs = app.run(defs={"batch_size": 64, "learning_rate": 0.001, "model_type": "transformer"})重要限制(务必理解,避免踩坑):
- 传入
defs后,定义这些变量的单元逻辑完全被覆盖、不再执行; - 必须提供某单元原本会产生的全部定义,而非个别参数;
- 这与 CLI 参数不同——CLI 参数是在单元执行内部被解析的。
例如某个单元定义了:
@app.cell def config(): batch_size = 32 learning_rate = 0.01 return batch_size, learning_rate覆盖时必须两个变量一起提供:
# 正确:覆盖该单元的全部定义 outputs, defs = app.run(defs={"batch_size": 64, "learning_rate": 0.001}) # 错误:会留下 learning_rate 未定义 # outputs, defs = app.run(defs={"batch_size": 64})3. 子进程方式
import subprocess subprocess.run(["python", "notebook.py", "--", "--param1", "value1"])4.3 存储与分享产物
| 需求 | 命令/方式 |
|---|---|
| 导出为 HTML | marimo export html notebook.py -o notebook.html -- -arg1 foo --arg2 bar |
| 部署为 Web 应用 | marimo run notebook.py |
| 编辑期自动导出 | 在编辑器应用设置中开启 auto-export HTML,每次修改后自动生成 HTML 快照到 notebook 所在位置的.marimo/目录 |
4.4 集成进工作流
- 作为 Python 脚本:marimo notebook 就是 Python 文件,可直接在大多数工作流系统中执行(仓库 examples 目录提供与主流工具的集成示例);
- 程序化执行:把 notebook 作为 Python 模块 import,或通过 subprocess 执行,可在工作流中串联多个 notebook。
五、迁移速查:四类用户的下一步
| 你的来源 | 最需要先掌握的能力 | 参考文档 |
|---|---|---|
| Jupyter | 响应式执行模型、变量不可重定义、magic 替代 | docs/guides/reactivity.md、docs/guides/coming_from/jupyter.md |
| Streamlit | 创建与显示分离的 UI 写法、最小化重跑 | docs/guides/coming_from/streamlit.md、docs/api/inputs/index.md |
| Jupytext | .py/.md原生存储、marimo convert/marimo export | docs/guides/coming_from/jupytext.md |
| Papermill | mo.cli_args/mo.query_params、Cell.run/app.run | docs/api/cli_args.md、docs/api/cell.md、docs/guides/coming_from/papermill.md |
所有迁移示例都可在仓库 examples 目录中找到对应的可运行范例;转换与执行命令均由 marimo/_cli/cli.py 中的convert、export、edit、run子命令提供。建议迁移时先在本地用marimo edit打开一个转换后的 notebook,感受响应式执行与"notebook 即应用"的工作流差异。
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考