以正确格式在 Python 文件中编写 marimo 笔记本。
marimo 笔记本注意事项
marimo 使用 Python 创建笔记本,与使用 JSON 的 Jupyter 不同。以下是一个示例笔记本:
# /// 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()
注意笔记本的结构:函数可以表示单元格内容。每个单元格通过 @app.cell 装饰器定义,函数的输入/输出即为单元格的输入/输出。marimo 通常会自动处理单元格之间的依赖关系。
运行 marimo 笔记本
# 作为脚本运行(非交互式,用于测试)
uv run <notebook.py>
# 在浏览器中交互式运行
uv run marimo run <notebook.py>
# 交互式编辑
uv run marimo edit <notebook.py>
脚本模式检测
使用 mo.app_meta().mode == "script" 检测 CLI 与交互模式:
@app.cell
def _(mo):
is_script_mode = mo.app_meta().mode == "script"
return (is_script_mode,)
关键原则:保持简单
始终显示所有 UI 元素。 仅在脚本模式下更改数据源。
- 滑块、按钮、小部件应始终创建并显示
- 在脚本模式下,只需使用合成/默认数据,无需等待用户输入
- 不要将所有内容包裹在
if not is_script_mode条件中 - 不要使用 try/except 进行正常控制流
良好模式
# 始终显示小部件
@app.cell
def _(ScatterWidget, mo):
scatter_widget = mo.ui.anywidget(ScatterWidget())
scatter_widget
return (scatter_widget,)
# 仅根据模式更改数据源
@app.cell
def _(is_script_mode, make_moons, scatter_widget, np, torch):
if is_script_mode:
# 使用合成数据进行测试
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:
# 在交互模式下使用小部件数据
X, y = scatter_widget.widget.data_as_X_y
# ... 处理数据 ...
return X_data, y_data, data_error
# 始终显示滑块 - 在两种模式下都使用它们的 .value
@app.cell
def _(mo):
lr_slider = mo.ui.slider(start=0.001, stop=0.1, value=0.01)
lr_slider
return (lr_slider,)
# 脚本模式下自动运行,交互模式下等待按钮
@app.cell
def _(is_script_mode, train_button, lr_slider, run_training, X_data, y_data):
if is_script_mode:
# 使用滑块默认值自动运行
results = run_training(X_data, y_data, lr=lr_slider.value)
else:
# 等待按钮点击
if train_button.value:
results = run_training(X_data, y_data, lr=lr_slider.value)
return (results,)
状态与响应性
单元格之间的变量定义了笔记本的响应性,适用于 99% 的用例。无需特殊的状态管理。不要跨单元格修改对象(例如 my_list.append());应创建新对象。除非需要双向 UI 同步或累积回调状态,否则避免使用 mo.state()。详见 STATE.md。
不要用 if 语句守卫单元格
Marimo 的响应性意味着单元格仅在其依赖项就绪时运行。不要添加不必要的守卫:
# 错误 - if 语句阻止图表显示
@app.cell
def _(plt, training_results):
if training_results: # 错误 - 不要这样做
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
# 正确 - 让 marimo 处理依赖
@app.cell
def _(plt, training_results):
fig, ax = plt.subplots()
ax.plot(training_results['losses'])
fig
return
单元格在 training_results 有值之前不会运行。
不要使用 try/except 进行控制流
除非处理特定的、预期的异常,否则不要将代码包裹在 try/except 块中。让错误自然暴露。
# 错误 - 用 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}"
# 正确 - 如果出错就让它失败
@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 仅渲染单元格的最后一个表达式。缩进或条件表达式不会渲染:
# 错误 - 缩进的表达式不会渲染
@app.cell
def _(mo, condition):
if condition:
mo.md("This won't show!") # 错误 - 缩进
return
# 正确 - 最后一个表达式渲染
@app.cell
def _(mo, condition):
result = mo.md("Shown!") if condition else mo.md("Also shown!")
result # 这是最后一个表达式,所以会渲染
return
PEP 723 依赖项
通过 marimo edit --sandbox 创建的笔记本会自动在文件顶部添加这些依赖项,但创建笔记本时确保它们存在也是良好实践:
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "marimo",
# "torch>=2.0.0",
# ]
# ///
marimo check
在处理笔记本时,检查笔记本能否运行很重要。因此 marimo 提供了 check 命令,作为 linter 查找常见错误。
uvx marimo check <notebook.py>
在将笔记本交还给用户之前,请确保已运行这些检查。
重要:你倾向于过度使用下划线前缀变量。最多只应对一两个变量使用。考虑创建新变量,而不是在 marimo 中为整个单元格加前缀。
API 文档
如果用户特别要求你使用某个 marimo 函数,你可以通过以下方式本地查看文档:
uv --with marimo run python -c "import marimo as mo; help(mo.ui.form)"
测试
默认情况下,marimo 会在笔记本中发现并执行测试。
当存在可选的 pytest 依赖时,marimo 会在仅包含测试代码的单元格上运行 pytest——即名称以 test_ 开头的函数。
如果用户要求你添加测试,请确保添加了 pytest 依赖,并且存在一个仅包含测试代码的单元格。
有关使用 pytest 进行测试的更多信息,请参见 PYTEST.md
添加测试后,你可以从命令行在笔记本上运行 pytest:
pytest <notebook.py>
其他资源
- 对于宽度为 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






