Apache Hamilton 介绍与核心概念详解

基于实测介绍 Apache Hamilton 的函数即节点编程模型、类型驱动依赖、Driver 执行架构、功能特性与插件生态,并给出与 Airflow、Dagster、Prefect、纯 pandas 脚本的选型对比。

Apache HamiltonApacheApache Hamilton数据管道Python2026/10/14

Apache Hamilton 介绍与核心概念详解

目标:讲清 Apache Hamilton 是什么、以「函数即节点」为核心的 DAG 数据流编程模型如何运作、有哪些功能特性与边界,并给出与同类工具的选型对比。全文结论均在 Ubuntu 22.04(x86_64)+ Python 3.10.12 + apache-hamilton 1.90.0 环境下实测验证,命令回显均为真实运行结果。

一、环境信息

本文所有结论与命令回显均在以下环境实测取得,读者复现时保持相近版本即可获得一致结果。先列清单,再给出两条最关键的环境验证命令及真实回显。

  • 操作系统:Ubuntu 22.04(x86_64)
  • Python:3.10.12(系统 pip 22.0.2,虚拟环境内升级至 pip 26.2.1)
  • Apache Hamilton:apache-hamilton 1.90.0(PyPI 最新版,Apache-2.0 许可,Apache 软件基金会孵化项目)
  • 核心依赖:numpy 2.2.6、pandas 2.3.3、typing_extensions 4.16.0、typing_inspect 0.9.0
  • 虚拟环境路径:/opt/venv-hamilton(隔离安装,不污染系统 Python)

验证 Python 与已安装的 Apache Hamilton 版本(两条命令的真实回显如下):

python3 -V

输出:

Python 3.10.12
/opt/venv-hamilton/bin/python -c "import hamilton; print(hamilton.__version__)"

输出:

(1, 90, 0)

💡 说明:hamilton.__version__ 返回的是元组 (1, 90, 0) 而非字符串,这是当前版本的实际行为。若需要字符串形式的版本号,用标准库读元数据更稳妥:

/opt/venv-hamilton/bin/python -c "from importlib.metadata import version; print(version('apache-hamilton'))"

输出:

1.90.0

二、软件概述

Apache Hamilton(Apache 孵化中)是一个轻量级 Python 库,用于以「有向无环图(DAG,Directed Acyclic Graph)」的方式组织数据变换逻辑。它不启动独立进程、不占用端口,而是作为库嵌入任意 Python 程序:开发者只写普通 Python 函数,框架自动把函数组装成 DAG 并驱动执行。本节用 pip show 的真实元数据来佐证项目定位与出处。

/opt/venv-hamilton/bin/pip show apache-hamilton

输出:

Name: apache-hamilton
Version: 1.90.0
Summary: Apache Hamilton (incubating) is a lightweight Python library for directed acyclic graphs (DAGs) of transformations. Your DAG is **portable**; it runs anywhere Python runs, whether it's a script, notebook, Airflow pipeline, FastAPI server, etc. Your DAG is **expressive**; Apache Hamilton has extensive features to define and modify the execution of a DAG (e.g., data validation, experiment tracking, remote execution).
Home-page: https://www.tryhamilton.dev/
Author:
Author-email: Stefan Krawczyk <stefank@cs.stanford.edu>, Elijah ben Izzy <elijah@dagworks.io>
License-Expression: Apache-2.0
Location: /opt/venv-hamilton/lib/python3.10/site-packages
Requires: numpy, pandas, typing_extensions, typing_inspect
Required-by:

从实测元数据可以读出几个关键事实:

  • 定位:官方 Summary 自述为「轻量级 Python 库,用于数据变换的有向无环图」,并强调 DAG 的可移植性——同一份函数模块可以跑在脚本、Notebook、Airflow 管道、FastAPI 服务等任意 Python 环境里。
  • 出处:由 Stefan Krawczyk 与 Elijah ben Izzy 创建(Author-email 所示),项目已进入 Apache 软件基金会孵化流程,许可证为 Apache-2.0。
  • 包名沿革:PyPI 上曾用包名 sf-hamilton(实测 pip index versions sf-hamilton 显示其历史版本自 1.1.1 延续至 1.90.0),现用包名 apache-hamilton(实测其名下可用版本为 1.90.0 与 1.89.0)。两种包名导入的 Python 包名都是 hamilton。
  • 依赖极简:Requires 仅有 numpy、pandas、typing_extensions、typing_inspect 四项,属于低侵入性依赖,嵌入现有项目负担小。
  • 运行形态:无独立进程、无守护服务,随宿主 Python 程序启动与退出。

一句话概括:Apache Hamilton 把「数据怎么流」写进函数签名,把「函数怎么算」留在函数体里,框架负责把两者组装成可执行、可校验、可可视化的 DAG。

三、核心架构

Apache Hamilton 的架构可以用三句话概括:函数即节点、类型注解即依赖声明、Driver 负责组装与执行。本节逐一拆解这三个核心机制,并配以实测代码与真实节点清单佐证。

3.1 函数即节点

每一个被 Hamilton 扫描的 Python 函数都会成为 DAG 中的一个节点:函数名就是节点名,函数参数就是要依赖的上游节点,函数返回值就是该节点的输出。下面是官方 quickstart 的函数模块(实测文件 /root/my_functions.py,共 6 个函数):

import pandas as pd


def avg_3wk_spend(spend: pd.Series) -> pd.Series:
    """Rolling 3 week average of spend."""
    return spend.rolling(3).mean()


def acquisition_cost(avg_3wk_spend: pd.Series, signups: pd.Series) -> pd.Series:
    """The cost per signup in relation to a rolling average of spend."""
    return avg_3wk_spend / signups


def spend_mean(spend: pd.Series) -> float:
    """Shows function creating a scalar. In this case it computes the mean of the entire column."""
    return spend.mean()


def spend_zero_mean(spend: pd.Series, spend_mean: float) -> pd.Series:
    """Shows function that takes a scalar. In this case to zero mean spend."""
    return spend - spend_mean


def spend_std_dev(spend: pd.Series) -> float:
    """Function that computes the standard deviation of the spend column."""
    return spend.std()


def spend_zero_mean_unit_variance(
    spend_zero_mean: pd.Series, spend_std_dev: float
) -> pd.Series:
    """Function showing one way to make spend have zero mean and unit variance."""
    return spend_zero_mean / spend_std_dev

逐条解读这个模块的约定:

  • avg_3wk_spend(spend: pd.Series):函数名即节点名,参数 spend 声明它依赖名为 spend 的上游节点(运行时由外部输入提供),返回 pd.Series 表示输出是序列型数据。
  • acquisition_cost(avg_3wk_spend, signups):两个参数同时声明了对 avg_3wk_spend 与 signups 两个上游节点的依赖,Hamilton 据此画出边。
  • spend_mean 返回 float:标量节点与序列节点可以共存于同一个 DAG,序列函数可以直接依赖标量函数的输出(如 spend_zero_mean 依赖 spend_mean)。
  • docstring 是节点文档,会出现在可视化与文档生成场景中。

把该模块交给 Driver 构建后,用 list_available_variables() 打印出的节点清单实测如下(6 个函数节点加上 2 个外部输入节点,共 8 个):

=== DAG nodes: dr.list_available_variables() ===
  acquisition_cost  (type: <class 'pandas.core.series.Series'>)
  avg_3wk_spend  (type: <class 'pandas.core.series.Series'>)
  spend_mean  (type: <class 'float'>)
  spend_std_dev  (type: <class 'float'>)
  spend_zero_mean  (type: <class 'pandas.core.series.Series'>)
  spend_zero_mean_unit_variance  (type: <class 'pandas.core.series.Series'>)
  signups  (type: <class 'pandas.core.series.Series'>)
  spend  (type: <class 'pandas.core.series.Series'>)

核对清单:acquisition_cost、avg_3wk_spend、spend_mean、spend_std_dev、spend_zero_mean、spend_zero_mean_unit_variance 正是 6 个函数名;signups、spend 是运行时从外部注入的输入节点。函数模块与节点清单一一对应,验证了「函数即节点」。

3.2 类型驱动的依赖声明

Hamilton 用函数签名的类型注解做两件事:声明依赖的数据形态、决定运行时如何校验与装配。实测中观察到三类典型形态:

注解形态实例语义
pd.Seriesspend: pd.Series依赖一个序列节点(通常是某一列数据)
float / 标量spend_mean: float依赖一个标量节点(如全列均值)
pd.DataFrame配合 @extract_columns 使用先产出数据框,再由装饰器拆列成节点

对上表的解读:序列与标量在同一个 DAG 里自然衔接——spend_zero_mean(spend, spend_mean) 的参数一个是序列一个是标量,Hamilton 会先把 spend_mean 算出来,再把标量广播进逐元素运算。外部输入通过 execute() 的 inputs 字典注入,字典的键必须与函数参数名一致(实测中注入 signups 与 spend 两个键,节点清单中即出现同名输入节点)。

这一机制的直接收益是:依赖关系由签名声明、由框架校验。若请求的输出所依赖的输入缺失,Driver 会在执行前直接报错并列出缺失项(实测报错样例见《Apache Hamilton 可视化与插件体系详解》第五节的排障实录,该文给出了完整报错回显与修复写法)。

3.3 Driver 执行模型与 Builder API

Driver 是 Hamilton 的执行入口。当前官方文档统一使用 builder API:通过 driver.Builder() 链式配置后 build() 出 Driver 实例,再调用 execute()。实测入口脚本 /root/my_script.py 的核心片段如下:

import pandas as pd

from hamilton import registry

registry.disable_autoload()

from hamilton import base, driver

import my_functions

if __name__ == "__main__":
    index = pd.date_range("2022-01-01", periods=6, freq="w")
    initial_columns = {
        "signups": pd.Series([1, 10, 50, 100, 200, 400], index=index),
        "spend": pd.Series([10, 10, 20, 40, 40, 50], index=index),
    }
    dr = (
        driver.Builder()
        .with_config({})  # we don't have any configuration or invariant data for this example.
        .with_modules(my_functions)  # we need to tell hamilton where to load function definitions from
        .with_adapters(base.PandasDataFrameResult())  # we want a pandas dataframe as output
        .build()
    )
    output_columns = [
        "spend",
        "signups",
        "avg_3wk_spend",
        "acquisition_cost",
    ]
    df = dr.execute(output_columns, inputs=initial_columns)
    print(df)

链式调用逐段解读:

  • with_config({}):向 DAG 传入配置(本例为空配置)。函数模块内可用 @config.when() 等装饰器按配置切换节点实现,实现同一模块多形态复用。
  • with_modules(my_functions):告诉 Hamilton 到哪些 Python 模块里收集函数定义,可传入多个模块。
  • with_adapters(base.PandasDataFrameResult()):结果装配器(result adapter),决定 execute() 的返回形态——这里要求把结果装配成 pandas DataFrame。
  • execute(output_columns, inputs=initial_columns):第一个参数是希望得到的输出节点名列表,inputs 是外部输入数据。Driver 只围绕所请求输出的依赖子图进行计算与装配。

实测运行 python my_script.py 的完整回显如下(一次跑通):

/root/my_script.py:20: FutureWarning: 'w' is deprecated and will be removed in a future version, please use 'W' instead.
  index = pd.date_range("2022-01-01", periods=6, freq="w")
=== DAG nodes: dr.list_available_variables() ===
  acquisition_cost  (type: <class 'pandas.core.series.Series'>)
  avg_3wk_spend  (type: <class 'pandas.core.series.Series'>)
  spend_mean  (type: <class 'float'>)
  spend_std_dev  (type: <class 'float'>)
  spend_zero_mean  (type: <class 'pandas.core.series.Series'>)
  spend_zero_mean_unit_variance  (type: <class 'pandas.core.series.Series'>)
  signups  (type: <class 'pandas.core.series.Series'>)
  spend  (type: <class 'pandas.core.series.Series'>)
=== execute result ===
            spend  signups  avg_3wk_spend  acquisition_cost
2022-01-02     10        1            NaN               NaN
2022-01-09     10       10            NaN               NaN
2022-01-16     20       50      13.333333          0.266667
2022-01-23     40      100      23.333333          0.233333
2022-01-30     40      200      33.333333          0.166667
2022-02-06     50      400      43.333333          0.108333

结果验证:avg_3wk_spend 前两行为 NaN(滚动窗口不足 3 期),第 3 行 (10+10+20)/3 = 13.333333;acquisition_cost 第 3 行 13.333333/50 = 0.266667,与人工计算完全一致,证明 Driver 按声明的依赖正确编排了执行。首行的 FutureWarning 是 pandas 2.3.3 对小写 freq="w" 的弃用提示,写新代码应使用大写 "W"。

💡 说明:旧式 driver.Driver(config, module) 直接构造的方式已不在当前官方文档出现,官方统一使用 builder API。新代码一律按上述 Builder 链式写法。

四、功能特性

本节把实测验证过的核心能力逐项列出,每项都附真实回显,便于读者对照评估是否满足自身需求。

4.1 声明式数据流定义

如第三章所示,用 6 个普通函数即定义出 8 节点 DAG,无需手写边与拓扑。这种「写函数即写管道」的风格让数据流的单元测试天然成立——每个函数可独立构造输入做测试。

4.2 灵活的结果装配(Result Adapter)

with_adapters() 决定 execute() 的返回形态。实测两种形态:

  • 挂载 base.PandasDataFrameResult() 时返回 pandas DataFrame(见 3.3 节结果表);
  • 不挂载 adapter 时返回 dict(键为节点名、值为节点输出)。实测装饰器脚本不挂 adapter 的运行结果片段:
=== execute result ===
  spend_mean_abs: np.float64(25.0)
  spend_mean_signed: np.float64(25.0)
  date: 0   2022-01-01
1   2022-01-02
2   2022-01-03
3   2022-01-04
Name: date, dtype: datetime64[ns]
  value: 0    1.0
1    2.0
2    3.0
3    4.0
Name: value, dtype: float64
  value_pct: 0     25.0
1     50.0
2     75.0
3    100.0
Name: value, dtype: float64

解读:需要表格形态就挂 PandasDataFrameResult;需要逐节点取用或对接自定义存储就省略 adapter 拿 dict。

4.3 装饰器体系(节点级增强)

Hamilton 提供一组装饰器在函数级别增强 DAG:实测跑通 @parameterize(一个函数展开成多个节点)、@extract_columns(把 DataFrame 的列拆成独立节点)、@check_output(对节点输出做数据质量校验)。实测装饰器脚本的节点清单片段:

  raw_df  (type: <class 'pandas.core.frame.DataFrame'>)
  date  (type: <class 'pandas.core.series.Series'>)
  value  (type: <class 'pandas.core.series.Series'>)
  spend_mean_abs  (type: <class 'float'>)
  spend_mean_signed  (type: <class 'float'>)
  value_pct_data_type_validator  (type: <class 'hamilton.data_quality.base.ValidationResult'>)
  value_pct_range_validator  (type: <class 'hamilton.data_quality.base.ValidationResult'>)
  value_pct  (type: <class 'pandas.core.series.Series'>)
  value_pct_raw  (type: <class 'pandas.core.series.Series'>)

解读:spend_mean_abs 与 spend_mean_signed 由同一个函数经 @parameterize 展开;date、value 由 @extract_columns 从 raw_df 拆出;value_pct_data_type_validator 与 value_pct_range_validator 是 @check_output 自动追加的校验节点。三个装饰器的完整实测代码、输出与踩坑排障见《Apache Hamilton 装饰器与数据校验详解》,该篇对每个装饰器都有「说明、实测代码、实测输出、要点」的完整闭环。

4.4 数据质量校验内建

@check_output 自动生成 ValidationResult 类型的校验节点(见上节回显),校验不通过时默认产生告警而不中断流程。校验器(如 dtype 校验、取值范围校验)以声明式参数给出,无需手写断言代码。

4.5 DAG 可视化与节点清单

挂载 [visualization] 扩展后可将 DAG 导出为 PNG 图片,实测生成两张图并存盘成功:

ls -la /root/*.png

输出:

-rw-r--r-- 1 root root 26052 Oct 14 12:45 my_dag.png
-rw-r--r-- 1 root root 56053 Oct 14 12:45 my_dag_all_functions.png

人工目检结果:my_dag.png 为执行视图(虚线 input 节点 spend/signups,蓝色 function 节点 avg_3wk_spend,橙色 output 节点 acquisition_cost,附带 Legend 图例);my_dag_all_functions.png 为全部 8 个函数节点的全景图。此外 list_available_variables() 可在不依赖图形工具的情况下把节点清单与类型打印成文字(见 3.1 节回显),适合日志与代码评审场景。可视化全流程实测见《Apache Hamilton 可视化与插件体系详解》。

4.6 插件生态(extras)

PyPI 元数据实测列出 23 个 extras,按用途分组如下:

分组extras用途
执行后端扩展dask、dask-array、dask-dataframe、dask-diagnostics、dask-distributed、ray、pyspark、vaex接入分布式/并行 DataFrame 或数组后端
数据校验pandera、pydantic接入第三方数据校验体系
可观测与跟踪datadog、openlineage、experiments、ui、sdk监控、血缘、实验跟踪与 UI/SDK 集成
通知与展示slack、tqdm、rich消息通知、进度条与终端美化
工具链cli、lsp、mcp、diskcache、visualization命令行、语言服务、MCP、磁盘缓存、可视化

对上表的解读:extras 按「用到才装」的原则按需引入,核心包安装后不附带这些重依赖。实测 apache-hamilton[visualization] 仅追加安装 graphviz 0.21 与 networkx 3.4.2 两个 Python 包;各 extras 的安装实测与插件加载机制详见《Apache Hamilton 可视化与插件体系详解》。

4.7 可移植嵌入

如第二节 pip show 的 Summary 原文所述,同一份函数模块可嵌入脚本、Notebook、Airflow 管道、FastAPI 服务等宿主。本批全部示例均以 python my_script.py 这类普通脚本方式运行(工作目录 /root,直接执行即出结果),验证了「随宿主 Python 程序运行、无独立进程」的库级定位:函数模块本身不感知宿主类型,换宿主只需改 Driver 入口。

五、适用场景与限制

本节从两个方向给结论:先列出实测能力对应适配的场景,再逐条交代评估确认的边界与限制,供选型时对照。

5.1 适用场景

结合实测能力,Apache Hamilton 适合以下数据工作场景:

场景说明实测对应能力
指标计算管道如获客成本、滚动均值等业务指标的批计算quickstart 的 avg_3wk_spend、acquisition_cost 链路
特征工程为机器学习批量生产特征列,训练/推理复用同一函数模块函数即节点 + config 切换 + DataFrame 装配
ETL 变换层在提取与加载之间组织可测试的变换逻辑装饰器拆列、校验、结果装配
嵌入应用的数据流Web 服务、ETL 作业内部嵌入有依赖关系的数据计算库级形态,随宿主进程运行
数据质量前置把关对关键节点输出做 dtype 与取值范围校验@check_output 自动校验节点

对上表的解读:共同特征是「变换逻辑复杂、依赖关系需要显式管理、希望可测试可复用」;凡是这类 Python 数据变换需求,Hamilton 都能以很低的结构成本接管。

5.2 限制与边界

实测与评估确认的限制如下,选型时应逐条对照:

  • 不做调度:Hamilton 没有定时触发、重试、告警等调度能力,定时批处理需与 Airflow 等调度平台组合(Hamilton 负责任务内的数据流,平台负责任务间的编排)。
  • 默认单进程执行:DAG 默认在本进程内按拓扑顺序执行;分布式执行需要引入 dask、ray、pyspark 等 extras 并按相应后端适配。
  • Python 版本门槛:PyPI 元数据实测要求 Requires-Python: <4, >=3.10.1,classifiers 覆盖 3.10 至 3.14;低于 3.10.1 的环境无法安装(实测环境为 3.10.12,通过)。
  • 新包名下可用版本少:apache-hamilton 名下实测仅 1.90.0 与 1.89.0;1.89.0 实测存在包名元数据不一致问题(sdist 元数据仍是 sf-hamilton,pip 拒绝安装),如需 1.89.0 应走旧包名 sf-hamilton。版本选型细节见《Ubuntu 22.04 安装 Apache Hamilton 指南》。
  • 官方文档示例偶有陈旧:实测发现官方 quickstart 页面展示的 acquisition_cost 示例输出与现行代码的真实运行结果不一致(文档示例为旧版代码的产物),且官方示例使用小写 freq="w" 触发 pandas FutureWarning。以本地实测结果为准即可。
  • DataFrame 类功能有依赖兜底:1.90.0 实测存在 pandas_extensions 依赖 packaging 但包声明缺失的上游问题,需要 pip install packaging 兜底,详见《Apache Hamilton 装饰器与数据校验详解》的排障实录。

六、同类选型对比

选择数据流工具时,市面常见选项不止一种,需要先分清「库级框架」与「调度平台」两类形态再对号入座。

💡 技术拓展:Python 数据管道工具按形态可分为「库级嵌入式框架」与「调度编排平台」两类,常见选项横向对比如下——

工具形态定位核心抽象擅长不擅长
Apache Hamilton库级嵌入式数据流框架函数即节点、类型即依赖数据变换逻辑的结构化、可测试、可校验、可嵌入任意 Python 程序任务调度、定时触发、告警运维
Apache Airflow调度编排平台DAG 文件 + 调度器 + Web UI定时批任务编排、重试、依赖管理、运维监控单任务内部的数据变换抽象
Dagster资产编排平台软件定义资产(Software-defined Assets)以数据资产为中心的物化、血缘与治理轻量嵌入已有 Python 程序
Prefect工作流编排库/平台flow/task 装饰器动态工作流、失败重试、云托管编排数据节点级的变换抽象与校验
纯 pandas 脚本无框架过程式代码顺序语句简单一次性分析,零学习成本依赖管理、复用、测试、可视化

本文所介绍的 Apache Hamilton 属于第一类:它是「库」,不是「平台」——不与 Airflow/Dagster/Prefect 争夺调度编排的定位,而是把自己产出的 DAG 嵌入这些平台的单个任务里执行,两者是互补关系。选型建议:需要定时/重试/运维选 Airflow 等平台;需要把「任务内部的数据变换」结构化并复用,选 Hamilton;两者常组合使用。

七、参考链接