marimo-notebook

marimo-notebook

热门

以正确格式在 Python 文件中编写 marimo 笔记本。

157Star
14Fork
更新于 2026/6/15
SKILL.md
readonly只读
name
marimo-notebook
description

以正确格式在 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