Apache Arrow PyArrow 架构深度解析:从 .py 到 .pxd 的四层源码结构与 PyArrow C++ 支撑层
2026/9/14 17:20:28 网站建设 项目流程

Apache Arrow PyArrow 架构深度解析:从 .py 到 .pxd 的四层源码结构与 PyArrow C++ 支撑层

【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow

本文以 Apache Arrow 仓库中 Python 文档的 "Getting Involved" 与 "PyArrow Architecture" 章节为核心,讲清楚 PyArrow 的参与途径与整体分层架构:PyArrow 主体上是对 Arrow C++(libarrow)能力的封装层,通过*.pylib.pyx/*.pxi_*.pyxincludes/*.pxd四层加上独立的 PyArrow C++ 层,把 C++ 的列式能力转译为更 Pythonic 的 API。读完本文,你能掌握 PyArrow 各层文件在 python/pyarrow/ 目录中的分工与调用关系,知道在贡献、排错或阅读源码时应该从哪一层入手,并能顺藤摸瓜定位到 Arrow 格式规范与底层实现。

如何参与 Apache Arrow

Arrow 文档指出,当前 Arrow 的主要受众是数据系统的开发者,大多数人会通过间接使用它来处理内部数据、并与其它支持 Arrow 的系统互操作的系统来接触 Arrow。即使你并不打算向 Arrow 本身或其集成项目贡献代码,官方同样欢迎你参与社区:

  • 加入邮件列表:向dev-subscribe@arrow.apache.org发送邮件即可订阅,你可以在列表中分享自己对项目的想法和用例,也可以浏览历史邮件存档。
  • 关注 GitHub 上的动态:跟踪 Apache Arrow 在 GitHub 上的 issue 与讨论,是了解项目进展、寻找可贡献问题的最直接方式。
  • 学习格式规范:Arrow 的跨语言列式内存格式由一套格式规范定义,仓库根目录的 format/ 目录即存放这些规范文件,例如 Message.fbs(IPC 消息格式)、Schema.fbs(模式序列化)与 File.fbs(IPC 文件格式),它们以 FlatBuffers Schema 形式描述,是理解 Arrow 跨语言互操作约定的第一手资料。

PyArrow 的定位:C++ 能力的 Pythonic 封装

文档对 PyArrow 的定位非常明确:PyArrow 大部分是对 Arrow C++ 实现所提供功能的封装。库的设计目标是把 C++ 中可用的能力,通过更 Pythonic、更不易上手出错的方式暴露出来。因此:

  • 有些能力可以直接映射——Python 类与 C++ 类近乎一一对应;
  • 更多情况下,C++ 的类与方法只是"地基",PyArrow 在其上组装出更易用的实体。

这一点在源码中得到直接印证。python/pyarrow/lib.pyx 开头就声明了与 C++ 侧的绑定关系:

from cython.operator cimport dereference as deref from pyarrow.includes.libarrow cimport * from pyarrow.includes.libarrow_python cimport * from pyarrow.includes.common cimport PyObject_to_object cimport pyarrow.includes.libarrow_python as libarrow_python cimport cpython as cp

随后通过arrow_init_numpy()初始化 NumPy C API(当前版本要求 NumPy 2.0+,见 lib.pyx),再调用import_pyarrow()初始化 PyArrow C++ API。整个模块随后通过一系列include指令拼装各功能域,这就是文档所说的"lib.pyx 的实现大部分依赖于被包含的*.pxi文件":

# Assorted compatibility helpers include "compat.pxi" # Exception types and Status handling include "error.pxi" # Configuration information include "config.pxi" # pandas API shim include "pandas-shim.pxi" # Memory pools and allocation include "memory.pxi" # Device type and memory manager include "device.pxi" # DataType, Field, Schema include "types.pxi" # Array scalar values include "scalar.pxi" # Array types include "array.pxi" # Builders include "builder.pxi" # Column, Table, Record Batch include "table.pxi" # Tensors include "tensor.pxi" # DLPack include "_dlpack.pxi" # File IO include "io.pxi" # IPC / Messaging include "ipc.pxi" # Micro-benchmark routines include "benchmark.pxi" # Public API include "public-api.pxi"

(见 lib.pyx。)每个*.pxi文件负责一个功能域——类型系统、标量、数组、构建器、表、张量、DLPack 互操作、文件 IO、IPC 等——pyarrow.lib对外是"内部模块",公共类则由其它模块(如pyarrow包本身)从它导入后再暴露。python/pyarrow/init.py 正是这样做的:

from pyarrow.lib import (BuildInfo, CppBuildInfo, RuntimeInfo, set_timezone_db_path, MonthDayNano, VersionInfo, build_info, cpp_build_info, cpp_version, cpp_version_info, runtime_info, cpu_count, set_cpu_count, enable_signal_handlers, io_thread_count, is_opentelemetry_enabled, set_io_thread_count)

第一层:*.py文件——面向用户的声明层

文档说明:pyarrow 包中的*.py文件通常是暴露给用户的实体声明处;在某些情况下,这些文件会直接从内部实现导入实体并原样暴露,不做修改。

在 python/pyarrow/ 目录中可以看到大量这类文件:csv.pyfeather.pyipc.pyfs.pyjson.pyorc.pydataset.pyflight.pycompute.py等。它们承担了参数校验、默认值处理、文档字符串(docstring)以及高层 API 组织等纯 Python 逻辑,是用户import pyarrow.csv这类语句的落点。当某个能力不需要 Python 侧额外加工时,.py文件只是从 Cython 编译出的模块直接 re-export,保持 API 表面简洁。

第二层:lib.pyx*.pxi——核心能力的暴露层

lib.pyx是把 libarrow 核心能力暴露给 Python 的主文件,编译后成为pyarrow.lib模块。文档特别提醒:虽然以pyarrow.lib的名称暴露在 Python 中,其内容应当被视为内部实现;公共类之所以能在pyarrow等模块中使用,正是靠从pyarrow.lib导入。

*.pxi文件按功能域组织实现。以 python/pyarrow/array.pxi(数千行体量,涵盖 Array/ChunkedArray 等核心类型)为例,其中 Python 序列转数组的入口展示了这一层的典型写法——声明PyConversionOptions(来自 PyArrow C++ 层),在nogil段中调用 C++ 函数释放 GIL 执行转换,再把shared_ptr[CChunkedArray]包装回 Python 对象:

cdef _sequence_to_array(object sequence, object mask, object size, DataType type, CMemoryPool* pool, c_bool from_pandas): cdef: int64_t c_size PyConversionOptions options shared_ptr[CChunkedArray] chunked ... options.from_pandas = from_pandas options.ignore_timezone = os.environ.get('PYARROW_IGNORE_TIMEZONE', False) with nogil: chunked = GetResultValue( ConvertPySequence(sequence, mask, options, pool) ) if chunked.get().num_chunks() == 1: return pyarrow_wrap_array(chunked.get().chunk(0)) else: return pyarrow_wrap_chunked_array(chunked)

(见 array.pxi。)ConvertPySequencePyConversionOptions的声明来自includes/libarrow_python.pxd,这正是第四层在第二层的具体消费点。

第三层:_*.pyx文件——胶水代码层

文档指出:_*.pyx文件是**胶水代码(glue code)**所在,它们把 C++ 能力拼装成 Python 类与方法,可以理解为*.py文件所暴露能力的内部实现。

目录中这类文件一一对应各 IO 与扩展子系统:_csv.pyx、_parquet.pyx、_feather.pyx、_fs.pyx、_flight.pyx、_dataset.pyx、_s3fs.pyx、_gcsfs.pyx、_hdfs.pyx、_compute.pyx、_orc.pyx、_json.pyx、_cuda.pyx、_acero.pyx 等。例如csv.py(用户可见的声明层)背后的 Reader/Writer 具体类由_csv.pyx编译实现。

仓库自带了 Cython 用户侧使用示例,可作为理解这一层的参考:python/pyarrow/tests/pyarrow_cython_example.pyx 与配套的 python/pyarrow/tests/test_cython.py,演示了外部项目如何声明 Arrow 类型、并在 Cython 代码中以nogil方式安全调用 Arrow API。

第四层:includes/*.pxd——C++ API 的 Cython 声明

文档说明:includes/*.pxd文件中是为 Cython 声明的原始 C++ 库 API,C++ 类与方法按原样声明,供其它.pyx文件用来实现 Python 类、函数与辅助工具。

python/pyarrow/includes/ 目录中的文件与 Arrow 的各个 C++ 库一一对应:

  • libarrow.pxd——核心 libarrow(类型、内存池、数组、状态/状态码等),文件约三千余行;
  • libarrow_python.pxd——PyArrow C++ 层(见下一节)的声明;
  • libarrow_fs.pxd、libarrow_dataset.pxd、libparquet.pxd、libparquet_encryption.pxd、libarrow_flight.pxd、libarrow_acero.pxd、libarrow_cuda.pxd、libgandiva.pxd、libarrow_substrait.pxd、libarrow_feather.pxd 等。

这类声明的标准形态可以在 libarrow.pxd 中看到:用cdef extern from ... namespace "arrow" nogil将 C++ 头文件中的类"原样"映射进 Cython 的世界,并用C前缀" 真实C++名"的别名约定避免与 Python 类型名冲突:

cdef extern from "arrow/util/key_value_metadata.h" namespace "arrow" nogil: cdef cppclass CKeyValueMetadata" arrow::KeyValueMetadata": CKeyValueMetadata() ...

nogil修饰意味着这些声明默认在释放 GIL 的上下文中使用,这是 PyArrow 能把重计算下推到 C++ 而不阻塞解释器的关键前提。

第五层:PyArrow C++(python/pyarrow/src/arrow/python

文档特别强调:除 Arrow C++ 库外,PyArrow 还依赖一个专门的 C++ 代码集——PyArrow C++,位于 python/pyarrow/src/arrow/python/ 目录,提供低层能力:与 NumPy/pandas 的相互转换,以及让 C++ 侧可以使用 Python 对象与回调的类。

从该目录的实际文件构成看,这些说法完全可对应:

  • numpy_convert.cc/numpy_convert.hnumpy_to_arrow.ccnumpy_to_arrow.harrow_to_pandas.ccpython_to_arrow.cc——NumPy/Python 对象与 Arrow 之间的双向转换(即array.pxi中调用的ConvertPySequence所在的一侧);
  • pyarrow.cc/pyarrow.h/pyarrow_api.h——PyArrow C++ API 的入口(import_pyarrow()注册机制);
  • extension_type.ccudf.ccinference.ccio.ccipc.ccdatetime.ccdecimal.cc等——扩展类型、自定义函数、类型推断、IO 与 IPC 的 C++ 支撑;
  • config.cc/common.cc——构建期配置与公共工具(BuildInfoCppBuildInfo等运行时信息最终经由此层暴露到 Python)。

这层代码由构建系统与 C++ 核心库一起编译,includes/libarrow_python.pxd负责把它声明给 Cython,lib.pyx*.pxi再把它用起来——三层之间的分工与文档描述严格一致。

从源码结构看一次典型的跨层调用链

把四层串起来,一次"用户调用"的路径大致是:

  1. 用户调用pyarrow包中*.py层声明的函数;
  2. 调用落到 Cython 编译的pyarrow.liblib.pyx+*.pxi)或某个_*.pyx模块;
  3. Cython 代码经由includes/*.pxd的声明,直接调用 libarrow / PyArrow C++ 的 C++ 函数,通常在nogil段中执行;
  4. PyArrow C++(如ConvertPySequence)完成类型推断与内存转换,返回CStatus/Resultshared_ptr包装的 C++ 对象;
  5. 结果经pyarrow_wrap_*一类辅助函数转回 Python 对象返回给用户。

对于想深入阅读或贡献 PyArrow 的开发者,这条调用链是定位问题的"地图":API 行为与文档字符串看*.py;Python 对象与 C++ 对象的转换边界看*.pxi/_*.pyx;C++ 符号声明看includes/*.pxd;NumPy/pandas 转换与 C++ 侧回调看 python/pyarrow/src/arrow/python/。若排查构建或版本问题,pyarrow.show_versions()init.py)会打印包类型、Arrow C++ 库版本、编译器与 git 修订号等构建信息,可直接定位到是哪一层、哪次构建的产物。

小结

  • PyArrow 是 Arrow C++ 的 Pythonic 封装:*.py声明用户 API,lib.pyx+*.pxi暴露核心库能力(内部实现),_*.pyx提供子系统胶水代码,includes/*.pxd原样声明 C++ API,src/arrow/python提供 NumPy/pandas 转换与 Python 对象回调等低层支撑。
  • 参与社区的路径:订阅dev-subscribe@arrow.apache.org邮件列表、跟踪 GitHub issue、研读 format/ 下的 FlatBuffers 格式规范。
  • 各层文件与文档描述的对应关系均可在仓库中直接验证,建议以本文给出的相对路径为起点逐层阅读。

【免费下载链接】arrowApache Arrow is the universal columnar format and multi-language toolbox for fast data interchange and in-memory analytics项目地址: https://gitcode.com/GitHub_Trending/arrow3/arrow

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询