由于出于效率考虑,Numpy 使用 C 语言实现,我想了解 Numpy 到底是如何从 Python 调用 C 函数,比如
np.array
,Numpy 源代码的哪一部分负责调用?
我尝试跟踪源代码,
multiarraymodule.c
目录中numpy/_core/src/multiarray
的C实现,我特别关注:PyMethodDef array_module_methods[ ]
以获取模块在导入到python时提供的功能的概述,并且我还关注:
PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_multiarray_umath",
NULL,
-1,
array_module_methods,
NULL,
NULL,
NULL,
NULL
}
这是初始化期间的模块名称。我还观察到:
_multiarray_umath
被导入: numpy/_core/multiarray.py
,python 模块定义的函数如:
@array_function_from_c_func_and_dispatcher(_multiarray_umath.empty_like)
def empty_like(
prototype, dtype=None, order=None, subok=None, shape=None, *,device=None)
随后是大量文档和示例,然后返回:
return (prototype,)
我认为这是必须调用 C 函数实现的地方,比如
empty_like()
函数?
Numpy 究竟如何从 Python 调用 C 函数(如
),Numpy 源代码的哪一部分负责调用?np.array
当您编写
np.array()
时,它们会从您的代码中调用。如果您尝试反汇编 np.array
调用:
import np
import dis
dis.dis("np.array()")
你得到了
0 0 RESUME 0
1 2 LOAD_NAME 0 (np)
4 LOAD_METHOD 1 (array)
26 PRECALL 0
30 CALL 0
40 RETURN_VALUE
只是一个不错的函数调用,仅此而已。现在,如果我们尝试反汇编函数本身,如果它是Python,我们将得到它的字节码,如上所述;但我们不:
dis.dis(np.array)
# => TypeError: don't know how to disassemble builtin_function_or_method objects
因此,Python 对待
np.array
就像对待 math.sin
一样:它是 Python 加载的库中某处的一些二进制代码。具体来说,在我的系统上它位于
site-packages/numpy/core/_multiarray_umath.cpython-311-darwin.so
这个文件是包
numpy.core._multiarray_umath
,它定义了函数array
:
from numpy.core._multiarray_umath import array
Python 如何知道将函数
array
与相应的 C 代码关联起来? _multiarray_umath
模块是使用PyModule_Create
函数定义的,使用定义here
作为具有某些方法,即array_module_methods
,其中:
{"array",
(PyCFunction)array_array,
METH_FASTCALL | METH_KEYWORDS, NULL},
定义于here。导入模块后,会在
PYTHONPATH
中的适当位置找到并动态链接具有适当名称的共享库,并且程序可以访问其功能。 Tl;dr:模块 numpy.core._multiarray_umath
是通过 array
中名为 array_array
的共享库绑定到 C 函数 numpy/core/_multiarray_umath...
的 PYTHONPATH
属性定义的。
所有这些都在使用 C 或 C++ 扩展 Python 和 构建 C 和 C++ 扩展中详细描述。
我们可以验证这确实是我们所了解和喜爱的
numpy.array
:
import numpy
import numpy.core._multiarray_umath
numpy.core._multiarray_umath.array == numpy.array
# => True
现在,它最终是如何被分配给
numpy.array
的是一系列复杂的导入、getattrs、全局赋值等等,我现在不想尝试追踪,但它与它是如何调用的,也与特定的 C 函数没有任何关系。