关于 marimo-notebook
面向编写和维护 marimo Notebook 的开发者,提供单元格反应性、运行模式、界面和测试方面的实践指导。还涵盖依赖声明与检查命令。
主要功能
- 01单元格依赖与反应性指导
- 02交互与脚本模式处理
- 03PEP 723 依赖声明
- 04marimo check 与测试指导
适用场景
- 01创建可交互的 marimo Notebook
- 02让 Notebook 支持脚本运行
- 03检查 Notebook 常见错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ marimo-team/ skills --skill marimo-notebook安装后在 Claude Code 中输入/marimo-notebook确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/ANYWIDGET.mdCOLUMNS.mdCONFIGURATION.mdDEPLOYMENT.mdEXPENSIVE.mdEXPORTS.mdPYTEST.mdREACTIVITY.mdSQL.mdSTATE.mdTOP-LEVEL-IMPORTS.mdUI.mdWATCHING.md详细介绍
marimo Notebook 注意事项
marimo 使用 Python 创建 Notebook,而 Jupyter 使用 JSON。以下是一个 Notebook 示例:
# /// script
# dependencies = [
# "marimo",
# "numpy==2.4.3",
# ]
# requires-python = ">=3.14"
# ///
import marimo
__generated_with = "0.20.4"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
import numpy as np
return mo, np
@app.cell
def _():
print("hello world")
return
@app.cell
def _(np, slider):
np.array([1,2,3]) + slider.value
return
@app.cell
def _(mo):
slider = mo.ui.slider(1, 10, 1, label="number to add")
slider
return (slider,)
@app.cell
def _():
return
if __name__ == "__main__":
app.run()
请注意,Notebook 的结构是由表示单元格内容的函数构成的。每个单元格都通过 @app.cell 装饰器定义,函数的输入和输出就是单元格的输入和输出。marimo 通常会自动处理单元格之间的依赖关系。
运行 Marimo Notebook
# Run as script (non-interactive, for testing)
uv run <notebook.py>
# Run interactively in browser
uv run marimo run <notebook.py>
# Edit interactively
uv run marimo edit <notebook.py>
检测脚本模式
使用 mo.app_meta().mode == "script" 来区分命令行与交互模式:
@app.cell
def _(mo):
is_script_mode = mo.app_meta().mode == "script"
return (is_script_mode,)
核心原则:保持简单
始终显示所有 UI 元素。 仅在脚本模式下更改数据源。
- 始终创建并显示滑块、按钮和小组件
- 在脚本模式下,只需使用合成数据或默认数据,而不是等待用户输入
- 不要用
if not is_script_mode条件包裹所有内容 - 不要将 try/except 用于常规控制流
推荐模式
# Always show the widget
@app.cell
def _(ScatterWidget, mo):
scatter_widget = mo.ui.anywidget(ScatterWidget())
scatter_widget
return (scatter_widget,)
# Only change data source based on mode
@app.cell
def _(is_script_mode, make_moons, scatter_widget, np, torch):
if is_script_mode:
# Use synthetic data for testing
X, y = make_moons(n_samples=200, noise=0.2)
X_data = torch.tensor(X, dtype=torch.float32)
y_data = torch.tensor(y)
data_error = None
else:
# Use widget data in interactive mode
X, y = scatter_widget.widget.data_as_X_y
# ... process data ...
return X_data, y_data, data_error
# Always show sliders - use their .value in both modes
@app.cell
def _(mo):
lr_slider = mo.ui.slider(start=0.001, stop=0.1, value=0.01)
lr_slider
return (lr_slider,)
# Auto-run in script mode, wait for button in interactive
@app.cell
def _(is_script_mode, train_button, lr_slider, run_training, X_data, y_data):
if is_script_mode:
# Auto-run with slider defaults
results = run_training(X_data, y_data, lr=lr_slider.value)
else:
# Wait for button click
if train_button.value:
results = run_training(X_data, y_data, lr=lr_slider.value)
return (results,)
状态与响应性
对于 99% 的使用场景,单元格之间的变量就能定义 Notebook 的响应性。无需特殊的状态管理。不要跨单元格修改对象(例如 my_list.append());应改为创建新对象。除非需要双向 UI 同步或累积回调状态,否则不要使用 mo.state()。详情请参阅 STATE.md。
不要用 if 语句保护单元格
marimo 的响应性意味着,只有在依赖项就绪时才会运行单元格。不要添加不必要的保护条件:
# BAD - the if statement prevents the chart from showing
@app.cell
def _(plt, training_results):
if training_results: # WRONG - don't do this
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
# GOOD - let marimo handle the dependency
@app.cell
def _(plt, training_results):
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
无论如何,只有在 training_results 有值后,该单元格才会运行。
不要将 try/except 用于控制流
除非要处理特定且预期会发生的异常,否则不要用 try/except 块包裹代码。让错误自然显现。
# BAD - hiding errors behind try/except
@app.cell
def _(scatter_widget, np, torch):
try:
X, y = scatter_widget.widget.data_as_X_y
X = np.array(X, dtype=np.float32)
# ...
except Exception as e:
return None, None, f"Error: {e}"
# GOOD - let it fail if something is wrong
@app.cell
def _(scatter_widget, np, torch):
X, y = scatter_widget.widget.data_as_X_y
X = np.array(X, dtype=np.float32)
# ...
仅在以下情况下使用 try/except:
- 处理特定且已知的异常类型
- 该异常在正常运行中是预期会发生的(例如,找不到文件)
- 有明确且有意义的恢复操作
单元格输出渲染
marimo 只会渲染单元格的最后一个表达式。缩进的表达式或条件表达式不会被渲染:
# BAD - indented expression won't render
@app.cell
def _(mo, condition):
if condition:
mo.md("This won't show!") # WRONG - indented
return
# GOOD - final expression renders
@app.cell
def _(mo, condition):
result = mo.md("Shown!") if condition else mo.md("Also shown!")
result # This renders because it's the final expression
return
PEP 723 依赖项
通过 marimo edit --sandbox 创建的 Notebook 会自动在文件顶部添加这些依赖项,但创建 Notebook 时也最好确认这些依赖项已存在:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "marimo",
# "torch>=2.0.0",
# ]
# ///
marimo check
编写 Notebook 时,检查其能否正常运行非常重要。因此,marimo 提供了 check 命令,可作为代码检查工具来查找常见错误。
uvx marimo check <notebook.py>
将 Notebook 交还给用户之前,请确保已完成这些检查。
重要提示:你容易过度使用带下划线前缀的变量。最多只应对一两个变量这样处理。对于 marimo,不妨创建新变量,而不是给整个单元格中的变量都加前缀。
API 文档
如果用户明确要求你使用某个 marimo 函数,可以在本地查看文档:
uv --with marimo run python -c "import marimo as mo; help(mo.ui.form)"
测试
默认情况下,marimo 会发现并执行 Notebook 中的测试。
如果存在可选的 pytest 依赖项,marimo 会对完全由测试代码组成的单元格运行 pytest,也就是说,运行名称以 test_ 开头的函数。
如果用户要求你添加测试,请确保添加 pytest 依赖项,并确保有一个单元格只包含测试代码。
有关使用 pytest 进行测试的更多信息,请参阅 PYTEST.md。
添加测试后,可以在命令行中对 Notebook 运行 pytest。
pytest <notebook.py>
附加资源
- 关于在 width=columns 中运行的 marimo 笔记本,请参阅 SQL.md
- 关于在 marimo 中使用 SQL,请参阅 SQL.md
- 关于 marimo 中的 UI 元素,请参阅 UI.md
- 关于将函数/类公开为顶层导入,请参阅 TOP-LEVEL-IMPORTS.md
- 关于导出笔记本(PDF、HTML、markdown 等),请参阅 EXPORTS.md
- 关于状态管理和响应性,请参阅 STATE.md
- 关于部署 marimo 笔记本,请参阅 DEPLOYMENT.md
- 关于使用 anywidget 创建自定义交互式小组件,请参阅 ANYWIDGET.md
- 关于外部编辑和
--watch模式,请参阅 WATCHING.md - 关于开销较大的笔记本(缓存、惰性求值、mo.stop),请参阅 EXPENSIVE.md
- 关于配置(pyproject.toml、marimo.toml),请参阅 CONFIGURATION.md
- 关于响应性模型(DAG、变量作用域、变更),请参阅 REACTIVITY.md