numpy.distutils.misc_util
numpy.distutils.misc_util.all_strings(lst)
如果 lst 中的所有项都是字符串对象,则返回 True。
numpy.distutils.misc_util.allpath(name)
将一个 / 分隔的路径名转换为使用 OS 路径分隔符的路径。
numpy.distutils.misc_util.appendpath(prefix, path)
numpy.distutils.misc_util.as_list(seq)
numpy.distutils.misc_util.blue_text(s)
numpy.distutils.misc_util.cyan_text(s)
numpy.distutils.misc_util.cyg2win32(path: str) → str
将路径从 Cygwin 本地转换为 Windows 本地。
使用 cygpath 实用程序(Base 安装的一部分)进行实际转换。如果失败,则回退返回原始路径。
处理默认的 /cygdrive
挂载前缀以及 /proc/cygdrive
便携前缀,自定义的 cygdrive 前缀如 /
或 /mnt
,以及绝对路径如 /usr/src/
或 /home/username
参数:
pathstr
要转换的路径
返回:
converted_pathstr
转换后的路径
笔记
cygpath 实用程序文档:cygwin.com/cygwin-ug-net/cygpath.html
它包装的 C 函数文档:cygwin.com/cygwin-api/func-cygwin-conv-path.html
numpy.distutils.misc_util.default_config_dict(name=None, parent_name=None, local_path=None)
返回一个配置字典,用于配置文件 setup_.py 中定义的 configuration() 函数的使用。
numpy.distutils.misc_util.dict_append(d, **kws)
numpy.distutils.misc_util.dot_join(*args)
numpy.distutils.misc_util.exec_mod_from_location(modname, modfile)
使用 importlib 机制从文件 modfile 导入模块 modname。根据 spec.loader,模块可能未在 sys.modules 中注册。
numpy.distutils.misc_util.filter_sources(sources)
返回包含 C、C++、Fortran 和 Fortran 90 模块源文件名称的四个列表。
numpy.distutils.misc_util.generate_config_py(target)
生成包含在构建软件包过程中使用的 system_info 信息的 config.py 文件。
用法:
config[‘py_modules’].append((packagename, ‘config’,generate_config_py))
numpy.distutils.misc_util.get_build_architecture()
numpy.distutils.misc_util.get_cmd(cmdname, _cache={})
numpy.distutils.misc_util.get_data_files(data)
numpy.distutils.misc_util.get_dependencies(sources)
numpy.distutils.misc_util.get_ext_source_files(ext)
numpy.distutils.misc_util.get_frame(level=0)
从调用堆栈中返回给定级别的帧对象。
numpy.distutils.misc_util.get_info(pkgname, dirs=None)
为给定 C 库返回一个 info 字典。
info 字典包含了使用 C 库所需的选项。
参数:
pkgnamestr
包的名称(应与 .ini 文件的名称匹配,不带扩展名,例如,文件 foo.ini 对应的名称为 foo)。
dirssequence, optional
如果给定,应为额外目录序列,用于查找 npy-pkg-config 文件。在 NumPy 目录之前搜索这些目录。
返回:
infodict
具有构建信息的字典。
引发:
PkgNotFound
如果找不到包。
另请参阅
Configuration.add_npy_pkg_config
,Configuration.add_installed_library
get_pkg_info
示例
从 NumPy 获取 npymath 库所需的信息:
>>> npymath_info = np.distutils.misc_util.get_info('npymath')
>>> npymath_info
{'define_macros': [], 'libraries': ['npymath'], 'library_dirs':
['.../numpy/core/lib'], 'include_dirs': ['.../numpy/core/include']}
然后,此 info 字典可以作为 Configuration
实例的输入:
config.add_extension('foo', sources=['foo.c'], extra_info=npymath_info)
numpy.distutils.misc_util.get_language(sources)
从源代码确定语言值(c,f77,f90)
numpy.distutils.misc_util.get_lib_source_files(lib)
numpy.distutils.misc_util.get_mathlibs(path=None)
返回 numpyconfig.h 中的 MATHLIB 行
numpy.distutils.misc_util.get_num_build_jobs()
获取由 setup.py 的–parallel 命令行参数设置的并行构建作业的数量。如果命令没有接收设置,则检查环境变量 NPY_NUM_BUILD_JOBS。如果未设置,则返回系统上的处理器数量,最多为 8 个(以防止系统超载,如果有大量 CPU)。
返回:
outint
可以运行的并行作业数量
numpy.distutils.misc_util.get_numpy_include_dirs()
numpy.distutils.misc_util.get_pkg_info(pkgname, dirs=None)
返回给定包的库信息。
参数:
pkgnamestr
包的名称(应与.ini 文件的名称匹配,不包括扩展名,例如对于文件 foo,包名应为 foo)。
dirs序列,可选
如果给出,应该是一个额外的目录序列,在这些目录中查找 npy-pkg-config 文件。在 NumPy 目录之前搜索这些目录。
返回:
pkginfo类实例
包含构建信息的LibraryInfo实例。
引发:
PkgNotFound
如果找不到包。
参见
Configuration.add_npy_pkg_config
,Configuration.add_installed_library
get_info
numpy.distutils.misc_util.get_script_files(scripts)
numpy.distutils.misc_util.gpaths(paths, local_path='', include_non_existing=True)
对路径应用 glob,并在需要时添加 local_path。
numpy.distutils.misc_util.green_text(s)
numpy.distutils.misc_util.has_cxx_sources(sources)
如果 sources 包含 C ++文件,则返回 True
numpy.distutils.misc_util.has_f_sources(sources)
如果 sources 包含 Fortran 文件,则返回 True
numpy.distutils.misc_util.is_local_src_dir(directory)
如果目录是本地目录,则返回 True。
numpy.distutils.misc_util.is_sequence(seq)
numpy.distutils.misc_util.is_string(s)
numpy.distutils.misc_util.mingw32()
在使用 mingw32 环境时返回 True。
numpy.distutils.misc_util.minrelpath(path)
从路径中解析 *
和.
。
numpy.distutils.misc_util.njoin(*path)
连接两个或更多的路径名组件 + - 将一个以/分隔的路径名转换为使用 OS 的路径分隔符。- 从路径中解析 *和 *。
可以通过传递 n 个参数(例如 njoin(‘a’,‘b’)),或传递 n 个名称的序列(例如 njoin([‘a’,‘b’])),或这些参数的混合来处理。
numpy.distutils.misc_util.red_text(s)
numpy.distutils.misc_util.sanitize_cxx_flags(cxxflags)
一些标志对 C 有效但对 C ++无效。将它们剪切。
numpy.distutils.misc_util.terminal_has_colors()
numpy.distutils.misc_util.yellow_text(s)
numpy.distutils.ccompiler
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.ccompiler.html
函数
CCompiler_compile(self, sources[, …]) | 编译一个或多个源文件。 |
---|---|
CCompiler_customize(self, dist[, need_cxx]) | 对编译器实例进行任何特定于平台的定制。 |
CCompiler_customize_cmd(self, cmd[, ignore]) | 使用 distutils 命令自定义编译器。 |
CCompiler_cxx_compiler(self) | 返回 C++编译器。 |
CCompiler_find_executables(self) | 此处无操作,但由 get_version 方法调用,可以被子类重写。 |
CCompiler_get_version(self[, force, ok_status]) | 返回编译器版本,如果编译器不可用,则返回 None。 |
CCompiler_object_filenames(self, …[, …]) | 返回给定源文件的对象文件的名称。 |
CCompiler_show_customization(self) | 将编译器的定制打印到标准输出。 |
CCompiler_spawn(self, cmd[, display, env]) | 在子进程中执行命令。 |
gen_lib_options(compiler, library_dirs, …) | |
new_compiler([plat, compiler, verbose, …]) | |
replace_method(klass, method_name, func) | |
simple_version_match([pat, ignore, start]) | 版本号简单匹配,用于 CCompiler 和 FCompiler。 |
numpy.distutils.ccompiler_opt
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.ccompiler_opt.html
提供 CCompilerOpt
类,用于处理 CPU/硬件优化,从解析命令参数开始,到管理 CPU 基线和可调度特性之间的关系,还生成所需的 C 头文件,最后使用适当的编译器标志编译源代码。
CCompilerOpt
不提供对 CPU 特性的运行时检测,而是仅关注编译器方面,但它创建了可以在最终运行时调度过程中后续使用的抽象 C 头文件。
函数
new_ccompiler_opt(compiler, dispatch_hpath, …) | 创建一个新的 ‘CCompilerOpt’ 实例,并生成包含启用的 CPU 基线和可调度特性的平台特定指令集的 #定义和头文件的调度头文件。 |
---|
类
CCompilerOpt(ccompiler[, cpu_baseline, …]) | 一个辅助类用于CCompiler,旨在提供额外的构建选项,以有效地控制与 CPU 特性直接相关的编译器优化。 |
---|
numpy.distutils.cpuinfo.cpu
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.cpuinfo.cpu.html
distutils.cpuinfo.cpu = <numpy.distutils.cpuinfo.LinuxCPUInfo object>
numpy.distutils.core.Extension
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.core.Extension.html
class numpy.distutils.core.Extension(name, sources, include_dirs=None, define_macros=None, undef_macros=None, library_dirs=None, libraries=None, runtime_library_dirs=None, extra_objects=None, extra_compile_args=None, extra_link_args=None, export_symbols=None, swig_opts=None, depends=None, language=None, f2py_options=None, module_dirs=None, extra_c_compile_args=None, extra_cxx_compile_args=None, extra_f77_compile_args=None, extra_f90_compile_args=None)
参数:
namestr
扩展名。
sourcesstr 列表
源文件相对于包顶级目录的位置列表。
extra_compile_argsstr 列表
传递给编译器的额外命令行参数。
extra_f77_compile_argsstr 列表
传递给 fortran77 编译器的额外命令行参数。
extra_f90_compile_argsstr 列表
传递给 fortran90 编译器的额外命令行参数。
方法
has_cxx_sources | |
---|---|
has_f2py_sources |
numpy.distutils.exec_command
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.exec_command.html
exec_command
实现了几乎等同于 commands.getstatusoutput 函数的 exec_command 函数,但在 NT、DOS 系统上返回的状态实际上是正确的(虽然返回的状态值可能会有所不同)。此外,exec_command 采用关键字参数来(重新)定义环境变量。
提供函数:
exec_command — 在指定目录中执行命令 在修改后的环境中。 find_executable — 利用环境信息找到一个命令 变量 PATH。相当于 posix 的which命令。
作者:Pearu Peterson pearu@cens.ioc.ee 创建日期:2003 年 1 月 11 日
需要:Python 2.x
成功测试:
os.name | sys.platform | comments |
---|---|---|
posix | linux2 | Debian (sid) Linux, Python 2.1.3+, 2.2.3+, 2.3.3 PyCrust 0.9.3, Idle 1.0.2 |
posix | linux2 | Red Hat 9 Linux, Python 2.1.3, 2.2.2, 2.3.2 |
posix | sunos5 | SunOS 5.9, Python 2.2, 2.3.2 |
posix | darwin | Darwin 7.2.0, Python 2.3 |
nt | win32 | Windows Me Python 2.3(EE), Idle 1.0, PyCrust 0.7.2 Python 2.1.1 Idle 0.8 |
nt | win32 | Windows 98, Python 2.1.1. Idle 0.8 |
nt | win32 | Cygwin 98-4.10, Python 2.1.1(MSC) - echo 测试失败,即重新定义环境变量可能无效。已修复:不使用 cygwin echo!评论:cmd /c echo也不能工作,但重新定义环境变量可以工作。 |
posix | cygwin | Cygwin 98-4.10, Python 2.3.3(cygming special) |
nt | win32 | Windows XP, Python 2.3.3 |
已知的错误:
函数
exec_command(command[, execute_in, …]) | 返回执行命令的状态和输出。 |
---|---|
filepath_from_subprocess_output(output) | 将子进程使用编码的bytes转换为适合文件系统的str。 |
find_executable(exe[, path, _cache]) | 返回可执行文件的完整路径,如果不存在则返回 None。 |
forward_bytes_to_stdout(val) | 将子进程调用的字节直接转发到控制台,而不尝试解码它们。 |
get_pythonexe() | |
temp_file_name() |
numpy.distutils.log.set_verbosity
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.log.set_verbosity.html
distutils.log.set_verbosity(v, force=False)
numpy.distutils.system_info.get_info
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.system_info.get_info.html
distutils.system_info.get_info(name, notfound_action=0)
notfound_action:
0 - 什么也不做 1 - 显示警告消息 2 - 引发错误
numpy.distutils.system_info.get_standard_file
原文:
numpy.org/doc/1.26/reference/generated/numpy.distutils.system_info.get_standard_file.html
distutils.system_info.get_standard_file(fname)
返回一个名为’fname’的文件列表,来自于:1)系统范围的目录(本模块的目录位置)2)用户的 HOME 目录(os.environ[‘HOME’])3)本地目录
警告
numpy.distutils
已弃用,并将在 Python >= 3.12 中移除。有关更多详情,请参见 numpy.distutils 的状态和迁移建议
目前 SciPy 项目包含两个包:
本文档的目的是描述如何向 SciPy 中添加新工具。
SciPy 由名为 SciPy 包的 Python 包组成,通过 scipy
命名空间可以提供给 Python 用户使用。每个 SciPy 包可能包含其他 SciPy 包。因此,SciPy 目录树是具有任意深度和宽度的包树。任何 SciPy 包可能依赖于 NumPy 包,但对其他 SciPy 包的依赖应尽可能减少或为零。
一个 SciPy 包除了其源代码外,还包括以下文件和目录:
setup.py
— 构建脚本__init__.py
— 包初始化器tests/
— 单元测试目录它们的内容如下所述。
setup.py
文件为了将一个 Python 包添加到 SciPy,其构建脚本 (setup.py
) 必须符合某些要求。最重要的要求是包定义一个 configuration(parent_package='',top_path=None)
函数,该函数返回一个适合传递给 numpy.distutils.core.setup(..)
的字典。为了简化构造此字典,numpy.distutils.misc_util
提供了下面描述的 Configuration
类。
下面是一个纯 SciPy 包的最小 setup.py
文件示例:
#!/usr/bin/env python3
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('mypackage',parent_package,top_path)
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
#setup(**configuration(top_path='').todict())
setup(configuration=configuration)
configuration
函数的参数指定了父 SciPy 包的名称(parent_package
)和主 setup.py
脚本的目录位置(top_path
)。这些参数和当前包的名称应该传递给 Configuration
构造函数。
Configuration
构造函数有第四个可选参数,package_path
,当包文件位于与 setup.py
文件所在目录不同的位置时可以使用。
剩余的Configuration
参数都是初始化Configuration
实例属性时将使用的关键字参数。通常,这些关键字与setup(..)
函数所期望的关键字相同,例如,packages
, ext_modules
, data_files
, include_dirs
, libraries
, headers
, scripts
, package_dir
等。但是,不建议直接指定这些关键字参数,因为这些关键字参数的内容不会被处理或检查 SciPy 构建系统的一致性。
最后,Configuration
具有.todict()
方法,它将所有配置数据作为适合传递给setup(..)
函数的字典返回。
Configuration
实例属性除了可以通过关键字参数指定给Configuration
构造函数的属性之外,Configuration
实例(我们以config
表示)还具有以下属性,这些属性在编写设置脚本时可能会有用:
config.name
- 当前软件包的完整名称。可以提取父软件包的名称作为config.name.split('.')
。
config.local_path
- 指向当前setup.py
文件的路径。
config.top_path
- 指向主setup.py
文件的路径。
Configuration
实例方法config.todict()
— 返回适合传递给numpy.distutils.core.setup(..)
函数的配置字典。
config.paths(*paths) --- 如有必要,将
paths的项应用于``glob.glob(..)
。修复相对于config.local_path
的paths
项。
config.get_subpackage(subpackage_name,subpackage_path=None)
— 返回子软件包配置的列表。在当前目录中查找名称为subpackage_name
的子软件包,但也可以通过可选的subpackage_path
参数指定路径。如果将subpackage_name
指定为None
,则子软件包的名称将采用subpackage_path
的基本名称。任何用于子软件包名称的*
都会按通配符进行展开。
config.add_subpackage(subpackage_name,subpackage_path=None)
— 将 SciPy 子软件包配置添加到当前配置中。上面解释了参数的含义和用法,请参见config.get_subpackage()
方法。
config.add_data_files(*files)
— 将files
添加到data_files
列表的开头。如果files
项是元组,则其第一个元素定义了数据文件相对于软件包安装目录的后缀,第二个元素指定了数据文件的路径。默认情况下,数据文件被复制到软件包安装目录下。例如,
config.add_data_files('foo.dat',
('fun',['gun.dat','nun/pun.dat','/tmp/sun.dat']),
'bar/car.dat'.
'/full/path/to/can.dat',
)
将数据文件安装到以下位置
<installation path of config.name package>/
foo.dat
fun/
gun.dat
pun.dat
sun.dat
bar/
car.dat
can.dat
数据文件的路径可以是一个不带参数并返回数据文件路径的函数 —— 当在构建软件包时生成数据文件时,这是非常有用的。(XXX:解释确切调用这些函数的步骤)
config.add_data_dir(data_path)
— 递归地将data_path
目录添加到data_files
中。从data_path
开始的整个目录树将被复制到包安装目录下。如果data_path
是一个元组,则它的第一个元素定义了数据文件被复制的后缀,相对于包安装目录,第二个元素指定了数据目录的路径。默认情况下,数据目录将被复制到包安装目录下的data_path
的基本名称下。例如,
config.add_data_dir('fun') # fun/ contains foo.dat bar/car.dat
config.add_data_dir(('sun','fun'))
config.add_data_dir(('gun','/full/path/to/fun'))
将数据文件安装到以下位置
<installation path of config.name package>/
fun/
foo.dat
bar/
car.dat
sun/
foo.dat
bar/
car.dat
gun/
foo.dat
bar/
car.dat
config.add_include_dirs(*paths)
— 将paths
添加到include_dirs
列表的开头。这个列表对当前包的所有扩展模块可见。
config.add_headers(*files)
— 将files
添加到headers
列表的开头。默认情况下,头文件将安装在<prefix>/include/pythonX.X/<config.name.replace('.','/')>/
目录下。如果files
项是一个元组,则它的第一个参数指定了相对于<prefix>/include/pythonX.X/
路径的安装后缀。这是一个 Python distutils 方法;在 NumPy 和 SciPy 中应该使用config.add_data_files(*files)
。
config.add_scripts(*files)
— 将files
添加到scripts
列表的开头。脚本将安装在<prefix>/bin/
目录下。
config.add_extension(name,sources,**kw)
— 创建并将一个Extension
实例添加到ext_modules
列表中。第一个参数name
定义了扩展模块的名称,该扩展模块将安装在config.name
包下。第二个参数是一个源列表。add_extension
方法还接受将传递给Extension
构造函数的关键字参数。允许的关键字列表如下:include_dirs
,define_macros
,undef_macros
,library_dirs
,libraries
,runtime_library_dirs
,extra_objects
,extra_compile_args
,extra_link_args
,export_symbols
,swig_opts
,depends
,language
,f2py_options
,module_dirs
,extra_info
,extra_f77_compile_args
,extra_f90_compile_args
。
请注意,config.paths
方法适用于可能包含路径的所有列表。extra_info
是一个字典或字典列表,其内容将追加到关键字参数。depends
列表包含扩展模块源代码依赖的文件或目录路径。如果depends
列表中的任何路径都比扩展模块新,那么模块将被重新构建。
源列表可能包含函数(‘源生成器’),其模式为def <funcname>(ext, build_dir): return <source(s) or None>
。如果funcname
返回None
,则不会生成任何源代码。如果所有源生成器处理后Extension
实例没有源代码,那么不会构建扩展模块。这是有条件地定义扩展模块的推荐方法。源生成器函数由numpy.distutils
的build_src
子命令调用。
例如,这是一个典型的源生成器函数:
def generate_source(ext,build_dir):
import os
from distutils.dep_util import newer
target = os.path.join(build_dir,'somesource.c')
if newer(target,__file__):
# create target file
return target
第一个参数包含 Extension 实例,可以用于访问其属性,如depends
、sources
等列表,并在构建过程中对其进行修改。第二个参数给出了一个构建目录的路径,在将文件写入磁盘时必须使用该目录。
config.add_library(name, sources, **build_info)
— 向libraries
列表添加一个库。允许的关键字参数有depends
、macros
、include_dirs
、extra_compiler_args
、f2py_options
、extra_f77_compile_args
、extra_f90_compile_args
。有关参数的更多信息,请参见.add_extension()
方法。
config.have_f77c()
— 如果 Fortran 77 编译器可用,则返回 True(即:简单的 Fortran 77 代码编译成功)。
config.have_f90c()
— 如果 Fortran 90 编译器可用,则返回 True(即:简单的 Fortran 90 代码编译成功)。
config.get_version()
— 返回当前包的版本字符串,如果无法检测到版本信息,则返回None
。该方法扫描文件__version__.py
、<packagename>_version.py
、version.py
、__svn_version__.py
以查找字符串变量version
、__version__
、<packagename>_version
。
config.make_svn_version_py()
— 向data_files
列表附加一个数据函数,该函数将生成__svn_version__.py
文件到当前包目录。当 Python 退出时,将从源目录中删除该文件。
config.get_build_temp_dir()
— 返回一个临时目录的路径。这是构建临时文件的地方。
config.get_distribution()
— 返回 distutils 的Distribution
实例。
config.get_config_cmd()
— 返回numpy.distutils
配置命令实例。
config.get_info(*names)
—
.src
文件NumPy distutils 支持自动转换命名为.src 的源文件。该功能可用于维护非常相似的代码块,只需要在块之间进行简单的更改。在设置的构建阶段,如果遇到名为.src 的模板文件,则会从模板构造一个名为的新文件,并将其放置在构建目录中以供使用。支持两种模板转换形式。第一种形式用于名为.ext.src 的文件,其中 ext 是一个已识别的 Fortran 扩展名(f、f90、f95、f77、for、ftn、pyf)。第二种形式用于所有其他情况。 ### Fortran 文件
此模板转换器将根据‘<…>’中的规则复制文件中所有的函数和子程序块,名字中含有‘<…>’的。‘<…>’中以逗号分隔的单词的数量决定了块的重复次数。这些单词指示了在每个块中将重复规则‘<…>’替换为什么内容。块中的所有重复规则必须包含相同数量的逗号分隔的单词,以指示该块应该重复多少次。如果重复规则中的单词需要逗号、箭头或右箭头,则在其前面添加反斜杠‘ '。如果重复规则中的单词与‘ <index>’匹配,则会被替换为相同重复规范中的第个单词。重复规则有两种形式:命名和短形式。
命名的重复规则在一个块中多次使用相同重复集时很有用。它使用<rule1=item1, item2, item3,…, itemN>的格式指定,其中 N 是应该重复该块的次数。在每个块的重复中,整个表达式‘<…>’将首先替换为 item1,然后替换为 item2,以此类推,直到完成 N 次重复。一旦引入了命名的重复规范,相同的重复规则可以通过只引用名称(即)在当前块中使用。
短形式的重复规则的格式为<item1, item2, item3, …, itemN>。该规则指定整个表达式‘<…>’应首先替换为 item1,然后替换为 item2,以此类推,直到完成 N 次重复。
可用的预定义命名重复规则如下:
非 Fortran 文件使用单独的语法来定义应使用类似于 Fortran 特定重复的命名重复规则的模板块的变量扩展。
NumPy Distutils 预处理以自定义模板语言编写的 C 源文件(扩展名:.c.src
)以生成 C 代码。@
符号用于包装宏样式的变量,以实现可能描述(例如)一组数据类型的字符串替换机制。
模板语言块由/**begin repeat
和/**end repeat**/
行进行界定,这些界定行也可以使用连续编号的界定行(如/**begin repeat1
和/**end repeat1**/
)进行嵌套:
/**begin repeat
单独一行标志着应该重复的段的开始。
#name=item1, item2, item3, ..., itemN#
定义命名变量扩展,并放置在连续的行上。这些变量会在每个重复块中被相应的词替换。同一重复块中的所有命名变量必须定义相同数量的词。
item*N
是item, item, ..., item
重复 N 次的简写。此外,结合*N
的括号可用于对应多个应该重复的项进行分组。因此,#name=(item1, item2)*4#
等同于#name=item1, item2, item1, item2, item1, item2, item1, item2#
。
*/
标记结束变量扩展命名。下一行是将使用命名规则重复的第一行。
@name@
。
/**end repeat**/
标记前一行为要重复的块的最后一行。
@TYPE@
变量,用于字符串替换,预处理为具有多个字符串(如INT
、LONG
、UINT
、ULONG
等)的通常相同的循环。因此,@TYPE@
样式语法通过模仿具有通用类型支持的语言来减少代码重复和维护负担。
以上规则在以下模板源示例中可能更清晰:
1 /* TIMEDELTA to non-float types */
2
3 /**begin repeat 4 *
5 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
6 * LONGLONG, ULONGLONG, DATETIME,
7 * TIMEDELTA#
8 * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
9 * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
10 * npy_datetime, npy_timedelta#
11 */
12
13 /**begin repeat1 14 *
15 * #FROMTYPE = TIMEDELTA#
16 * #fromtype = npy_timedelta#
17 */
18 static void
19 @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n,
20 void *NPY_UNUSED(aip), void *NPY_UNUSED(aop))
21 {
22 const @fromtype@ *ip = input;
23 @totype@ *op = output;
24
25 while (n--) {
26 *op++ = (@totype@)*ip++;
27 }
28 }
29 /**end repeat1**/ 30
31 /**end repeat**/
通用类型 C 源文件的预处理(无论是 NumPy 本身还是使用 NumPy Distutils 的任何第三方包中的文件)由conv_template.py执行。在构建过程中由这些模块生成的特定类型 C 文件(扩展名:.c
)已准备好进行编译。这种通用类型的形式也支持 C 头文件(预处理以生成.h
文件)。
numpy.distutils.misc_util
中的有用函数get_numpy_include_dirs()
— 返回 NumPy 基础包含目录的列表。NumPy 基础包含目录包含诸如numpy/arrayobject.h
、numpy/funcobject.h
等头文件。对于已安装的 NumPy,返回的列表长度为 1,但在构建 NumPy 时,列表可能包含更多目录,例如,numpy/base/setup.py
文件生成并被numpy
头文件使用的config.h
文件路径。
append_path(prefix,path)
— 智能地将path
附加到prefix
。
gpaths(paths, local_path='')
— 对路径应用 glob 并在需要时添加local_path
前缀。
njoin(*path)
— 连接路径名组件+将/
分隔路径转换为os.sep
分隔路径并解析路径中的..
、.
。例如,njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g')
。
minrelpath(path)
— 解析path
中的点号。
rel_path(path, parent_path)
— 返回相对于parent_path
的path
。
def get_cmd(cmdname,_cache={})
— 返回numpy.distutils
命令实例。
all_strings(lst)
has_f_sources(sources)
has_cxx_sources(sources)
filter_sources(sources)
— 返回c_sources, cxx_sources, f_sources, fmodule_sources
get_dependencies(sources)
is_local_src_dir(directory)
get_ext_source_files(ext)
get_script_files(scripts)
get_lib_source_files(lib)
get_data_files(data)
dot_join(*args)
— 用点连接非零参数。
get_frame(level=0)
— 返回给定级别调用堆栈中的帧对象。
cyg2win32(path)
mingw32()
— 在使用 mingw32 环境时返回True
。
terminal_has_colors()
, red_text(s)
, green_text(s)
, yellow_text(s)
, blue_text(s)
, cyan_text(s)
get_path(mod_name,parent_path=None)
— 在给定时,返回相对于 parent_path 的模块路径。也处理__main__
和__builtin__
模块。
allpath(name)
— 在name
中将/
替换为os.sep
。
cxx_ext_match
, fortran_ext_match
, f90_ext_match
, f90_module_name_match
numpy.distutils.system_info
模块get_info(name,notfound_action=0)
combine_paths(*args,**kws)
show_all()
numpy.distutils.cpuinfo
模块cpuinfo
numpy.distutils.log
模块set_verbosity(v)
numpy.distutils.exec_command
模块get_pythonexe()
find_executable(exe, path=None)
exec_command( command, execute_in='', use_shell=None, use_tee=None, **env )
__init__.py
文件典型 SciPy __init__.py
的头部是:
"""
Package docstring, typically with a brief description and function listing.
"""
# import functions into module namespace
from .subpackage import *
...
__all__ = [s for s in dir() if not s.startswith('_')]
from numpy.testing import Tester
test = Tester().test
bench = Tester().bench
可以在 setup.py 脚本中指定 config_fc 选项。例如,使用
config.add_library(‘library’, sources=[…], config_fc={‘noopt’😦file,1)})
将编译 library
源文件时不使用优化标志。
建议只指定那些与编译器无关的 config_fc 选项。
一些旧的 Fortran 代码需要特殊的编译器选项才能正确工作。为了指定每个源文件的编译器选项,numpy.distutils
Fortran 编译器寻找以下模式:
CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>
在源代码的前 20 行中,并针对指定类型的 fcompiler 使用 f77flags
(第一个字符 C
可选)。
TODO: 这个功能也可以很容易地扩展到 Fortran 90 代码上。如果您需要这样的功能,请告诉我们。
当前 SciPy 项目包括两个包:
本文旨在描述如何向 SciPy 添加新工具。
SciPy 由 Python 包组成,称为 SciPy 包,通过scipy
名称空间提供给 Python 用户。每个 SciPy 包可能包含其他 SciPy 包。以此类推。因此,SciPy 目录树是具有任意深度和宽度的包树。任何 SciPy 包可能依赖于 NumPy 包,但对其他 SciPy 包的依赖应保持最小或零。
除了源文件之外,SciPy 包还包含以下文件和目录:
setup.py
— 构建脚本__init__.py
— 包初始化文件tests/
— 单元测试目录它们的内容如下。
setup.py
文件要将 Python 包添加到 SciPy,其构建脚本(setup.py
)必须满足一定的要求。最重要的要求是该包定义一个返回适合传递给numpy.distutils.core.setup(..)
的字典的configuration(parent_package='',top_path=None)
函数。为了简化构建此字典的过程,numpy.distutils.misc_util
提供了下面描述的Configuration
类。
以下是一个纯 SciPy 包的最小setup.py
文件示例:
#!/usr/bin/env python3
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('mypackage',parent_package,top_path)
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
#setup(**configuration(top_path='').todict())
setup(configuration=configuration)
configuration
函数的参数指定父 SciPy 包的名称(parent_package
)和主setup.py
脚本的目录位置(top_path
)。这些参数,连同当前包的名称,应传递给Configuration
构造函数。
Configuration
构造函数有一个第四个可选参数package_path
,用于当包文件位于不同于setup.py
文件目录的位置时使用。
剩余的Configuration
参数都是将用于初始化Configuration
实例属性的关键字参数。通常,这些关键字与setup(..)
函数所期望的关键字相同,例如packages
,ext_modules
,data_files
,include_dirs
,libraries
,headers
,scripts
,package_dir
等。但是,不建议直接指定这些关键字,因为这些关键字参数的内容不会被处理或检查 SciPy 构建系统的一致性。
最后,Configuration
有一个.todict()
方法,将所有配置数据作为适合传递给setup(..)
函数的字典返回。
Configuration
实例属性除了可以通过关键字参数指定的属性外,Configuration
实例(我们将其标记为config
)具有以下对于编写安装脚本很有用的属性:
config.name
- 当前包的完整名称。可以提取父包的名称为config.name.split('.')
。
config.local_path
- 当前setup.py
文件的路径位置。
config.top_path
- 主setup.py
文件的路径位置。
Configuration
实例方法config.todict()
— 返回适合传递给numpy.distutils.core.setup(..)
函数的配置字典。
config.paths(*paths) --- 如果必要,对
paths的项目应用
glob.glob(…)。修复相对于
config.local_path的
paths`项目。
config.get_subpackage(subpackage_name,subpackage_path=None)
— 返回子包配置的列表。在当前目录下查找名为subpackage_name
的子包,但也可以通过可选的subpackage_path
参数指定路径。如果subpackage_name
指定为None
,则子包名称将使用subpackage_path
的基本名称。任何用于子包名称的*
都会被扩展为通配符。
config.add_subpackage(subpackage_name,subpackage_path=None)
— 向当前配置添加 SciPy 子包配置。解释参数的含义和用法如上所述,请参阅config.get_subpackage()
方法。
config.add_data_files(*files)
— 将files
添加到data_files
列表的开头。如果files
项目是一个元组,则其第一个元素定义了将数据文件复制到包安装目录的后缀,第二个元素指定了数据文件的路径。默认情况下,数据文件将复制到包安装目录下。例如,
config.add_data_files('foo.dat',
('fun',['gun.dat','nun/pun.dat','/tmp/sun.dat']),
'bar/car.dat'.
'/full/path/to/can.dat',
)
将数据文件安装到以下位置
<installation path of config.name package>/
foo.dat
fun/
gun.dat
pun.dat
sun.dat
bar/
car.dat
can.dat
数据文件的路径可以是一个不带参数并返回路径(s)到数据文件的函数——当构建包时生成数据文件时,这是一个有用的功能(XXX:具体解释这个函数什么时候被调用)
config.add_data_dir(data_path)
— 递归地将目录data_path
添加到data_files
中。从data_path
开始的整个目录树将在包安装目录下复制。如果data_path
是一个元组,则其第一个元素定义了复制数据文件的后缀,相对于包安装目录,第二个元素指定了数据目录的路径。默认情况下,数据目录被复制到包安装目录下,以data_path
的基本名称命名。例如,
config.add_data_dir('fun') # fun/ contains foo.dat bar/car.dat
config.add_data_dir(('sun','fun'))
config.add_data_dir(('gun','/full/path/to/fun'))
将数据文件安装到以下位置
<installation path of config.name package>/
fun/
foo.dat
bar/
car.dat
sun/
foo.dat
bar/
car.dat
gun/
foo.dat
bar/
car.dat
config.add_include_dirs(*paths)
— 将paths
添加到include_dirs
列表的开头。这个列表将对当前包的所有扩展模块可见。
config.add_headers(*files)
— 将files
添加到headers
列表的开头。默认情况下,头文件将安装在<prefix>/include/pythonX.X/<config.name.replace('.','/')>/
目录下。如果files
项目是一个元组,那么它的第一个参数指定相对于<prefix>/include/pythonX.X/
路径的安装后缀。这是一个 Python distutils 方法;在 NumPy 和 SciPy 中,建议使用config.add_data_files(*files)
。
config.add_scripts(*files)
— 将files
添加到scripts
列表的开头。脚本将安装在<prefix>/bin/
目录下。
config.add_extension(name,sources,**kw)
— 创建并将一个 Extension
实例添加到 ext_modules
列表中。第一个参数 name
定义了扩展模块的名称,该模块将安装在 config.name
包下。第二个参数是一个源列表。add_extension
方法还接受传递给 Extension
构造函数的关键字参数。允许的关键字列表如下:include_dirs
、define_macros
、undef_macros
、library_dirs
、libraries
、runtime_library_dirs
、extra_objects
、extra_compile_args
、extra_link_args
、export_symbols
、swig_opts
、depends
、language
、f2py_options
、module_dirs
、extra_info
、extra_f77_compile_args
、extra_f90_compile_args
。
请注意,config.paths
方法适用于可能包含路径的所有列表。extra_info
是一个字典或字典列表,其内容将附加到关键字参数中。depends
列表包含了扩展模块的源文件依赖的文件或目录路径。如果 depends
列表中的任何路径新于扩展模块,则将重新构建该模块。
源列表可能包含具有模式 def <funcname>(ext, build_dir): return <source(s) or None>
的函数(‘源生成器’)。 如果 funcname
返回 None
,则不会生成任何源。如果 Extension
实例在处理所有源生成器后没有任何源,那么不会构建扩展模块。这是有条件地定义扩展模块的推荐方法。源生成器函数由 numpy.distutils
的 build_src
子命令调用。
例如,这是一个典型的源生成器函数示例:
def generate_source(ext,build_dir):
import os
from distutils.dep_util import newer
target = os.path.join(build_dir,'somesource.c')
if newer(target,__file__):
# create target file
return target
第一个参数包含了 Extension
实例,可以用于访问其属性,如 depends
、sources
等列表,并在构建过程中修改它们。第二个参数提供了一个构建目录的路径,在创建文件到磁盘时必须使用该路径。
config.add_library(name, sources, **build_info)
— 将库添加到 libraries
列表中。允许的关键字参数包括 depends
、macros
、include_dirs
、extra_compiler_args
、f2py_options
、extra_f77_compile_args
、extra_f90_compile_args
。有关参数的更多信息,请参阅.add_extension()
方法。
config.have_f77c()
— 如果 Fortran 77 编译器可用(即:简单的 Fortran 77 代码编译成功),则返回 True。
config.have_f90c()
— 如果 Fortran 90 编译器可用(即:简单的 Fortran 90 代码编译成功),则返回 True。
config.get_version()
— 返回当前包的版本字符串,如果无法检测到版本信息,则返回 None
。该方法扫描文件 __version__.py
、<packagename>_version.py
、version.py
、__svn_version__.py
,以查找字符串变量 version
、__version__
、<packagename>_version
。
config.make_svn_version_py()
— 向data_files
列表添加一个数据函数,该函数将生成__svn_version__.py
文件到当前包目录。Python 退出时,该文件将从源目录中删除。
config.get_build_temp_dir()
— 返回临时目录路径。这是应该构建临时文件的地方。
config.get_distribution()
— 返回 distutils Distribution
实例。
config.get_config_cmd()
— 返回 numpy.distutils
配置命令实例。
config.get_info(*names)
—
.src
文件NumPy distutils 支持自动转换命名为.src 的源文件。这个功能可用于在块之间保持非常相似的代码,只需要进行简单的更改。在设置的构建阶段期间,如果遇到名为.src 的模板文件,将从模板构造一个名为的新文件,并将其放置在构建目录中以供使用。支持两种模板转换形式。第一种形式适用于文件名为.ext.src,其中 ext 是一个已识别的 Fortran 扩展名(f,f90,f95,f77,for,ftn,pyf)。第二种形式用于所有其他情况。### Fortran 文件
此模板转换器将根据‘<…>’中的规则,复制文件中所有的函数和子例程块,并以包含‘<…>’的名称来重复。‘<…>’中用逗号分隔的单词的数量决定了块被重复的次数。这些单词指示了在每个块中‘<…>’应该用什么替换。一个块中的所有重复规则必须包含相同数量的逗号分隔的单词,表明这个块应该被重复的次数。如果重复规则中的单词需要有逗号,左箭头或右箭头,那么在其前面加上反斜杠’ '。如果重复规则中的单词匹配‘<index>’,那么它将被替换为相同重复规范中的第个单词。重复规则有两种形式:命名和简短。
当同一组重复必须多次在一个块中使用时,命名重复规则非常有用。它使用<rule1=item1, item2, item3,…, itemN>
来指定,其中 N 是块应该重复的次数。在块的每次重复中,整个表达式‘<…>’首先会被 item1 替换,然后被 item2 替换,依此类推,直到完成 N 次重复。一旦引入了命名重复规范,就可以通过只引用名称(即<规则 1>)在当前块中使用相同的重复规则。
简短重复规则看起来像<item1, item2, item3, …, itemN>。规则指定整个表达式‘<…>’应首先用 item1 替换,然后用 item2 替换,依此类推,直到 N 次重复完成。
以下是可用的预定义命名重复规则:
非 Fortran 文件使用单独的语法来定义应使用类似于 Fortran 特定重复规则的变量扩展的模板块。
NumPy Distutils 对使用自定义模板语言编写的 C 源文件(扩展名:.c.src
)进行预处理以生成 C 代码。@
符号用于包装宏样式的变量,以提供描述(例如)一组数据类型的字符串替换机制。
模板语言块由/**begin repeat
和/**end repeat**/
行界定,这些行也可以使用连续编号的界定行进行嵌套,例如/**begin repeat1
和/**end repeat1**/
:
/**begin repeat
标记了应重复的段的开始。
#name=item1, item2, item3, ..., itemN#
定义命名变量扩展,并将其放置在连续的行上。这些变量在每个重复块中与相应的单词替换。同一重复块中的所有命名变量必须定义相同数量的单词。
item*N
表示item, item, ..., item
重复 N 次的简写。此外,结合*N
的括号可以用于分组多个应该重复的项。因此,#name=(item1, item2)*4#
相当于#name=item1, item2, item1, item2, item1, item2, item1, item2#
。
*/
标记了变量扩展命名的结束。下一行是将使用命名规则重复的第一行。
@name@
。
/**end repeat**/
标记了前一行作为要重复块的最后一行。
@TYPE@
变量,用于字符串替换,该变量经预处理后成为多个完全相同的循环,其中包含INT
、LONG
、UINT
、ULONG
等多个字符串。因此,@TYPE@
样式的语法通过模仿具有通用类型支持的语言来减少代码重复和维护负担。
以下是上述规则在以下模板源示例中更为明确的示例:
1 /* TIMEDELTA to non-float types */
2
3 /**begin repeat 4 *
5 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
6 * LONGLONG, ULONGLONG, DATETIME,
7 * TIMEDELTA#
8 * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
9 * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
10 * npy_datetime, npy_timedelta#
11 */
12
13 /**begin repeat1 14 *
15 * #FROMTYPE = TIMEDELTA#
16 * #fromtype = npy_timedelta#
17 */
18 static void
19 @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n,
20 void *NPY_UNUSED(aip), void *NPY_UNUSED(aop))
21 {
22 const @fromtype@ *ip = input;
23 @totype@ *op = output;
24
25 while (n--) {
26 *op++ = (@totype@)*ip++;
27 }
28 }
29 /**end repeat1**/ 30
31 /**end repeat**/
对于通用类型的 C 源文件(无论是在 NumPy 本身还是在使用 NumPy Distutils 的任何第三方包中),都使用conv_template.py进行预处理。在构建过程中,这些模块生成的特定类型的 C 文件(扩展名:.c
)已经准备好编译了。此类通用类型也支持 C 头文件(预处理为.h
文件)。
numpy.distutils.misc_util
中的实用函数get_numpy_include_dirs()
— 返回 NumPy 基本包含目录列表。NumPy 基本包含目录包含诸如numpy/arrayobject.h
,numpy/funcobject.h
等头文件。对于已安装的 NumPy,返回的列表长度为 1,但构建 NumPy 时,该列表可能包含更多目录,例如,numpy/base/setup.py
文件生成并被numpy
头文件使用的config.h
文件路径��
append_path(prefix,path)
— 将path
智能地附加到prefix
。
gpaths(paths, local_path='')
— 对路径应用 glob,并在必要时添加local_path
前缀。
njoin(*path)
— 连接路径名组件+将/
分隔的路径转换为os.sep
分隔的路径并从路径中解析..
,.
。例如,njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g')
。
minrelpath(path)
— 解析path
中的点。
rel_path(path, parent_path)
— 返回相对于parent_path
的path
。
def get_cmd(cmdname,_cache={})
— 返回numpy.distutils
命令实例。
all_strings(lst)
has_f_sources(sources)
has_cxx_sources(sources)
filter_sources(sources)
— 返回c_sources, cxx_sources, f_sources, fmodule_sources
get_dependencies(sources)
is_local_src_dir(directory)
get_ext_source_files(ext)
get_script_files(scripts)
get_lib_source_files(lib)
get_data_files(data)
dot_join(*args)
— 用点连接非零参数。
get_frame(level=0)
— 返回调用栈中给定级别的 frame 对象。
cyg2win32(path)
mingw32()
— 在使用 mingw32 环境时返回True
。
terminal_has_colors()
, red_text(s)
, green_text(s)
, yellow_text(s)
, blue_text(s)
, cyan_text(s)
get_path(mod_name,parent_path=None)
— 在给定时返回模块相对于 parent_path 的路径。还处理__main__
和__builtin__
模块。
allpath(name)
— 在name
中将/
替换为os.sep
。
cxx_ext_match
, fortran_ext_match
, f90_ext_match
, f90_module_name_match
numpy.distutils.system_info
模块get_info(name,notfound_action=0)
combine_paths(*args,**kws)
show_all()
numpy.distutils.cpuinfo
模块cpuinfo
numpy.distutils.log
模块set_verbosity(v)
numpy.distutils.exec_command
模块get_pythonexe()
find_executable(exe, path=None)
exec_command( command, execute_in='', use_shell=None, use_tee=None, **env )
下面是用于纯 SciPy 软件包的最小setup.py
文件示例:
#!/usr/bin/env python3
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('mypackage',parent_package,top_path)
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
#setup(**configuration(top_path='').todict())
setup(configuration=configuration)
configuration
函数的参数指定父 SciPy 软件包的名称(parent_package
)和主setup.py
脚本的目录位置(top_path
)。这些参数以及当前软件包的名称应传递给Configuration
构造函数。
Configuration
构造函数有第四个可选参数package_path
,可在软件包文件位于与setup.py
文件目录不同的位置时使用。
剩余的Configuration
参数都是Configuration
实例属性的关键字参数的初始化值。通常,这些关键字与setup(..)
函数期望的关键字是相同的,例如,packages
,ext_modules
,data_files
,include_dirs
,libraries
,headers
,scripts
,package_dir
等。然而,不建议直接指定这些关键字,因为这些关键字参数的内容不会被处理或检查 SciPy 构建系统的一致性。
最后,Configuration
有一个.todict()
方法,它返回所有配置数据,以字典的形式适合传递给setup(..)
函数。
Configuration
实例的属性除了可以通过Configuration
构造函数的关键参数指定的属性之外,Configuration
实例(假设为config
)还有以下属性,可以在编写设置脚本时很有用:
config.name
- 当前软件包的完整名称。父软件包的名称可以通过config.name.split('.')
来提取。
config.local_path
- 当前setup.py
文件的路径。
config.top_path
- 主setup.py
文件的路径。
Configuration
实例的方法config.todict()
— 返回适合传递给numpy.distutils.core.setup(..)
函数的配置字典。
config.paths(*paths) --- 如果有必要,对
paths的项应用``glob.glob(..)
。修复相对于config.local_path
的paths
项。
config.get_subpackage(subpackage_name,subpackage_path=None)
— 返回子软件包配置的列表。子软件包在当前目录下寻找,名称为subpackage_name
,但路径也可以通过可选的subpackage_path
参数指定。如果subpackage_name
指定为None
,则子软件包名称将取subpackage_path
的基本名称。任何用于子软件包名称的*
都会被扩展为通配符。
config.add_subpackage(subpackage_name,subpackage_path=None)
— 将 SciPy 子软件包配置添加到当前配置。关于参数的意义和用法已在上文中解释过,参见config.get_subpackage()
方法。
config.add_data_files(*files)
— 将files
加入data_files
列表的最前面。如果files
是一个元组,则其第一个元素定义了数据文件的后缀,相对于软件包安装目录的位置,第二个元素指定了数据文件的路径。默认情况下,数据文件将被复制到软件包安装目录下。例如,
config.add_data_files('foo.dat',
('fun',['gun.dat','nun/pun.dat','/tmp/sun.dat']),
'bar/car.dat'.
'/full/path/to/can.dat',
)
将数据文件安装到以下位置
<installation path of config.name package>/
foo.dat
fun/
gun.dat
pun.dat
sun.dat
bar/
car.dat
can.dat
数据文件的路径可以是一个不带参数并返回路径的函数 —— 这在生成软件包时生成数据文件时非常有用。(XXX:解释一下什么时候确切地调用这个函数)
config.add_data_dir(data_path)
— 递归地将目录data_path
添加到data_files
中。从data_path
开始的整个目录树将被复制到包安装目录下。 如果data_path
是一个元组,那么它的第一个元素定义了将数据文件复制到的后缀的相对于包安装目录的位置,第二个元素指定了数据目录的路径。默认情况下,数据目录被复制到基础data_path
的包安装目录下。例如,
config.add_data_dir('fun') # fun/ contains foo.dat bar/car.dat
config.add_data_dir(('sun','fun'))
config.add_data_dir(('gun','/full/path/to/fun'))
将数据文件安装到以下位置
<installation path of config.name package>/
fun/
foo.dat
bar/
car.dat
sun/
foo.dat
bar/
car.dat
gun/
foo.dat
bar/
car.dat
config.add_include_dirs(*paths)
— 将paths
添加到include_dirs
列表的开头。这个列表将对当前包的所有扩展模块可见。
config.add_headers(*files)
— 将files
添加到headers
列表的开头。默认情况下,头文件将安装在<prefix>/include/pythonX.X/<config.name.replace('.','/')>/
目录下。 如果files
项是一个元组,那么它的第一个参数指定了相对于<prefix>/include/pythonX.X/
路径的安装后缀。 这是一个 Python distutils 方法;NumPy 和 SciPy 不鼓励使用它,而是使用config.add_data_files(*files)
。
config.add_scripts(*files)
— 将files
添加到scripts
列表的开头。脚本将安装在<prefix>/bin/
目录下。
config.add_extension(name,sources,**kw)
— 创建并将一个Extension
实例添加到ext_modules
列表中。第一个参数name
定义了扩展模块的名称,该模块将安装在config.name
包下。第二个参数是一个来源列表。 add_extension
方法还接受传递给Extension
构造函数的关键字参数。允许的关键字参数列表如下:include_dirs
、define_macros
、undef_macros
、library_dirs
、libraries
、runtime_library_dirs
、extra_objects
、extra_compile_args
、extra_link_args
、export_symbols
、swig_opts
、depends
、language
、f2py_options
、module_dirs
、extra_info
、extra_f77_compile_args
、extra_f90_compile_args
。
注意,config.paths
方法应用于所有可能包含路径的列表。extra_info
是一个字典或字典列表,其内容将附加到关键字参数中。列表depends
包含到扩展模块源的文件或目录的路径。如果depends
列表中的任何路径都比扩展模块新,那么模块将被重新构建。
来源列表可能包含函数(‘源生成器’),其模式为def <funcname>(ext, build_dir): return <source(s) or None>
。如果funcname
返回None
,则不会生成任何源。如果Extension
实例在处理所有源生成器后没有源,那么将不会构建任何扩展模块。这是有条件地定义扩展模块的推荐方式。源生成器函数由numpy.distutils
的build_src
子命令调用。
例如,这是一个典型的源生成器函数:
def generate_source(ext,build_dir):
import os
from distutils.dep_util import newer
target = os.path.join(build_dir,'somesource.c')
if newer(target,__file__):
# create target file
return target
第一个参数包含 Extension 实例,可以用于访问其属性,如depends
、sources
等列表,并在构建过程中修改它们。第二个参数提供了一个构建目录的路径,必须在将文件写入磁盘时使用。
config.add_library(name, sources, **build_info)
— 将库添加到libraries
列表中。允许的关键字参数有depends
、macros
、include_dirs
、extra_compiler_args
、f2py_options
、extra_f77_compile_args
、extra_f90_compile_args
。有关参数的更多信息,请参见.add_extension()
方法。
config.have_f77c()
— 如果 Fortran 77 编译器可用,则返回 True(即:简单的 Fortran 77 代码编译成功)。
config.have_f90c()
— 如果 Fortran 90 编译器可用,则返回 True(即:简单的 Fortran 90 代码编译成功)。
config.get_version()
— 返回当前包的版本字符串,如果无法检测到版本信息,则返回None
。该方法扫描文件__version__.py
、<packagename>_version.py
、version.py
、__svn_version__.py
查找字符串变量version
、__version__
、<packagename>_version
。
config.make_svn_version_py()
— 在data_files
列表中追加一个数据函数,该函数将生成__svn_version__.py
文件到当前包目录。该文件将在 Python 退出时从源目录中删除。
config.get_build_temp_dir()
— 返回临时目录的路径。这是构建临时文件的位置。
config.get_distribution()
— 返回 distutils Distribution
实例。
config.get_config_cmd()
— 返回numpy.distutils
配置命令实例。
config.get_info(*names)
—
.src
文件NumPy distutils 支持自动转换以.src
结尾的源文件。该功能可用于保留在块之间仅需进行简单更改的非常相似代码块。在设置的构建阶段,如果遇到名为<somefile>.src
的模板文件,则将从模板构造一个新文件<somefile>
并将其放置在构建目录中以供使用。支持两种模板转换形式。第一种形式用于以扩展名(f、f90、f95、f77、for、ftn、pyf)结尾的文件。第二种形式用于所有其他情况。
此模板转换器将根据‘<…>’中的规则,复制文件中所有函数和子例程块的名称中包含‘<…>’的块。‘<…>’中以逗号分隔的单词数确定块重复的次数。这些单词表示每个块中应该用重复规则‘<…>’替换的内容。块中的所有重复规则必须包含相同数量的逗号分隔的单词,表示该块应重复的次数。如果重复规则中的单词需要逗号、左箭头或右箭头,则用反斜杠‘ '’在前面加上它。如果重复规则中的单词与‘ <index>’匹配,则它将被替换为相同重复规范中索引号-th 单词。重复规则有两种形式:命名和缩写。
当同一组重复必须在块中多次使用时,命名重复规则非常有用。它使用<rule1=item1, item2, item3,…, itemN>来指定,其中 N 是应重复块的次数。在每次重复块时,整个表达式‘<…>’将首先替换为 item1,然后替换为 item2,依此类推,直到完成 N 次重复。一旦引入了一个命名重复规则,同一重复规则可以通过仅指定名称(即)在当前块中使用。
缩写重复规则看起来像<item1, item2, item3, …, itemN>。该规则指定整个表达式‘<…>’应首先替换为 item1,然后替换为 item2,依此类推,直到完成 N 次重复。
下面列出了可用的预定义命名重复规则:
当同一组重复必须在块中多次使用时,命名重复规则非常有用。它使用<rule1=item1, item2, item3,…, itemN>来指定,其中 N 是应重复块的次数。在每次重复块时,整个表达式‘<…>’将首先替换为 item1,然后替换为 item2,依此类推,直到完成 N 次重复。一旦引入了一个命名重复规则,同一重复规则可以通过仅指定名称(即)在当前块中使用。
缩写重复规则看起来像<item1, item2, item3, …, itemN>。该规则指定整个表达式‘<…>’应首先替换为 item1,然后替换为 item2,依此类推,直到完成 N 次重复。
下面列出了��用的预定义命名重复规则:
非 Fortran 文件使用单独的语法来定义应使用类似于 Fortran 特定重复的命名重复规则的模板块。
NumPy Distutils 预处理用自定义模板语言编写的 C 源文件(扩展名:.c.src
)以生成 C 代码。@
符号用于包装宏风格变量,以实现描述(例如)一组数据类型的字符串替换机制。
模板语言块由/**begin repeat
和/**end repeat**/
行界定,也可以使用连续编号的界定行进行嵌套,如/**begin repeat1
和/**end repeat1**/
:
/**begin repeat
标志着应重复的段的开始。
#name=item1, item2, item3, ..., itemN#
定义命名变量扩展,并放置在连续行上。这些变量将在每个重复块中与相应的单词替换。同一重复块中的所有命名变量必须定义相同数量的单词。
item*N
表示item, item, ..., item
重复 N 次。此外,结合*N
的括号可以用于分组多个应重复的项目。因此,#name=(item1, item2)*4#
等同于#name=item1, item2, item1, item2, item1, item2, item1, item2#
。
*/
标志着变量扩展命名的结束。接下来的一行将是使用命名规则重复的第一行。
@name@
。
/**end repeat**/
标记着前一行作为应重复的块的最后一行。
@TYPE@
变量,用于字符串替换,它经过预处理后将成为几个其他相同的循环,带有诸如INT
、LONG
、UINT
、ULONG
等多个字符串。因此,@TYPE@
样式的语法通过模仿具有通用类型支持的语言来减少代码重复和维护负担。
以上规则在以下模板源示例中可能更清晰:
1 /* TIMEDELTA to non-float types */
2
3 /**begin repeat 4 *
5 * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG,
6 * LONGLONG, ULONGLONG, DATETIME,
7 * TIMEDELTA#
8 * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint,
9 * npy_long, npy_ulong, npy_longlong, npy_ulonglong,
10 * npy_datetime, npy_timedelta#
11 */
12
13 /**begin repeat1 14 *
15 * #FROMTYPE = TIMEDELTA#
16 * #fromtype = npy_timedelta#
17 */
18 static void
19 @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n,
20 void *NPY_UNUSED(aip), void *NPY_UNUSED(aop))
21 {
22 const @fromtype@ *ip = input;
23 @totype@ *op = output;
24
25 while (n--) {
26 *op++ = (@totype@)*ip++;
27 }
28 }
29 /**end repeat1**/ 30
31 /**end repeat**/
通用类型化 C 源文件的预处理(无论是在 NumPy 本身中还是在使用 NumPy Distutils 的任何第三方包中)由conv_template.py执行。在构建过程中由这些模块生成的特定类型的 C 文件(扩展名:.c
)可供编译。这种通用类型化也适用于 C 头文件(预处理以生成.h
文件)。
numpy.distutils.misc_util
中的有用函数get_numpy_include_dirs()
— 返回 NumPy 基本包含目录的列表。NumPy 基本包含目录包含诸如numpy/arrayobject.h
、numpy/funcobject.h
等头文件。对于已安装的 NumPy,返回的列表长度为 1,但构建 NumPy 时,列表可能包含更多目录,例如,由numpy/base/setup.py
生成并由numpy
头文件使用的config.h
文件的路径。
append_path(prefix,path)
— 将path
智能添加到prefix
上。
gpaths(paths, local_path='')
— 对路径应用 glob,并在需要时在前面添加local_path
。
njoin(*path)
— 将路径名组件联接在一起,将以/
分隔的路径转换为os.sep
分隔的路径,并解析路径中的..
、.
。例如njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g')
。
minrelpath(path)
— 解析path
中的点。
rel_path(path, parent_path)
— 返回相对于parent_path
的path
。
def get_cmd(cmdname,_cache={})
— 返回numpy.distutils
命令实例。
all_strings(lst)
has_f_sources(sources)
has_cxx_sources(sources)
filter_sources(sources)
— 返回c_sources, cxx_sources, f_sources, fmodule_sources
get_dependencies(sources)
is_local_src_dir(directory)
get_ext_source_files(ext)
get_script_files(scripts)
get_lib_source_files(lib)
get_data_files(data)
dot_join(*args)
— 用点联接非零参数。
get_frame(level=0)
— 根据指定的 level 从调用栈返回 frame 对象。
cyg2win32(path)
mingw32()
— 在使用 mingw32 环境时返回True
。
terminal_has_colors()
, red_text(s)
, green_text(s)
, yellow_text(s)
, blue_text(s)
, cyan_text(s)
get_path(mod_name,parent_path=None)
— 在给定的 parent_path 下返回模块的路径。还处理__main__
和__builtin__
模块。
allpath(name)
— 将name
中的/
替换为os.sep
。
cxx_ext_match
, fortran_ext_match
, f90_ext_match
, f90_module_name_match
numpy.distutils.system_info
模块get_info(name,notfound_action=0)
combine_paths(*args,**kws)
show_all()
numpy.distutils.cpuinfo
模块cpuinfo
numpy.distutils.log
模块set_verbosity(v)
numpy.distutils.exec_command
模块get_pythonexe()
find_executable(exe, path=None)
exec_command( command, execute_in='', use_shell=None, use_tee=None, **env )
__init__.py
文件一个典型的 SciPy __init__.py
的头部是:
"""
Package docstring, typically with a brief description and function listing.
"""
# import functions into module namespace
from .subpackage import *
...
__all__ = [s for s in dir() if not s.startswith('_')]
from numpy.testing import Tester
test = Tester().test
bench = Tester().bench
可以在 setup.py 脚本中指定 config_fc 选项。例如,使用
config.add_library(‘library’, sources=[…], config_fc={‘noopt’😦file,1)})
将编译library
源码时不使用优化标志。
建议仅以与编译器无关的方式指定那些 config_fc 选项
一些旧的 Fortran 代码需要特殊的编译器选项才能正常工作。为了指定每个源文件的编译器选项,numpy.distutils
Fortran 编译器寻找以下模式:
CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>
在源文件的前 20 行中,并使用指定类型的 fcompiler 的f77flags
(第一个字符C
是可选的)。
TODO:这个功能也可以轻松扩展到 Fortran 90 代码上。如果您需要这样的功能,请告诉我们。
可以在 setup.py 脚本中指定 config_fc 选项。例如,使用
config.add_library(‘library’, sources=[…], config_fc={‘noopt’😦file,1)})
编译library
源码时不使用优化标志。
建议仅指定那些与编译器无关的 config_fc 选项。
一些旧的 Fortran 代码需要特殊的编译器选项才能正常工作。为了指定每个源文件的编译器选项,numpy.distutils
Fortran 编译器寻找以下模式:
CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>
在源文件的前 20 行中,并使用指定类型的 fcompiler 的f77flags
(第一个字符C
是可选的)。
TODO:这个功能也可以轻松扩展到 Fortran 90 代码上。如果您需要这样的功能,请告诉我们。
numpy.distutils
的状态和迁移建议原文:
numpy.org/doc/1.26/reference/distutils_status_migration.html
numpy.distutils
已在 NumPy 1.23.0
中被弃用。它将在 Python 3.12 中被移除;对于 Python <= 3.11,它将在 Python 3.12 发布后的 2 年内不会被移除(2025 年 10 月)。
警告
numpy.distutils
仅与 setuptools < 60.0
进行了测试,更新的版本可能会出现问题。有关详细信息,请参见 numpy.distutils 与 setuptools 的交互。
有几个很好的选项可供迁移。假设您的软件包中有编译代码(如果没有,您有几个很好的选项,例如 Poetry、Hatch 或 PDM 提供的构建后端),并且您希望使用一个设计良好、现代且可靠的构建系统,我们建议使用:
如果您的需求不高(只有简单的 Cython/C 扩展;不需要 Fortran、BLAS/LAPACK、嵌套的 setup.py
文件或 numpy.distutils
的其他功能),并且迄今为止对 numpy.distutils
感到满意,您也可以考虑切换到 setuptools
。请注意,numpy.distutils
的大多数功能不太可能被移植到 setuptools
。
SciPy 在其 1.9.0 版本中已经转向使用 Meson 和 meson-python。在此过程中,解决了 Meson 的 Python 支持和与 numpy.distutils
功能对齐的剩余问题。注意:对齐意味着一个大的超集(因为 Meson 是一个很好的通用构建系统);只有一些 BLAS/LAPACK 库选择的细微差别是缺失的。SciPy 几乎使用了 numpy.distutils
提供的所有功能,因此如果 SciPy 成功地使用 Meson 作为构建系统发布了一个版本,那么应该没有障碍可以阻止迁移,SciPy 将成为其他正在迁移的软件包的良好参考。有关 SciPy 迁移的更多详细信息,请参见:
NumPy 将在 1.26 版本中迁移到 Meson。
下一代 scikit-build 称为 scikit-build-core。在旧的 scikit-build
使用 setuptools
作为底层时,重写不再使用。与 Meson 一样,CMake 是一个很好的通用构建系统。
setuptools
对于仅因历史原因而使用numpy.distutils
的项目,并且实际上没有使用setuptools
也支持的功能,迁移到setuptools
很可能是成本最低的解决方案。为评估这一点,有一些numpy.distutils
功能 不 在setuptools
中存在:
setup.py
文件
最广泛使用的功能是嵌套的setup.py
文件。该功能可能将来可能仍然被移植到setuptools
中(但需要一个志愿者,见gh-18588了解状态)。只使用该功能的项目在此之后可以迁移到setuptools
。如果一个项目只使用了几个setup.py
文件,将这些文件的所有内容聚合到一个单独的setup.py
文件中,然后迁移到setuptools
可能是有意义的。这涉及放弃所有Configuration
实例,并改用Extension
。例如:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[
Extension('foopkg.foo', ['foo.c']),
Extension('barpkg.bar', ['bar.c']),
],
)
有关更多细节,请参阅setuptools 文档
##numpy.distutils
与setuptools
的互动
建议使用setuptools < 60.0
。更新的版本可能有效,但不能保证。原因是setuptools
60.0 启用了distutils
的供应商副本,其中包含一些影响numpy.distutils
某些功能的向后不兼容更改。
如果您只使用简单的 Cython 或 C 扩展并最小限度地使用numpy.distutils
功能,超出嵌套的setup.py
文件(它最流行的特性,请参阅Configuration
),那么最新的setuptools
可能会继续运作正常。如果出现问题,您还可以尝试SETUPTOOLS_USE_DISTUTILS=stdlib
来避免setuptools
中不兼容的更改。
无论您做什么,建议在pyproject.toml
中对setuptools
的构建需求设置一个上限,以避免未来的破坏 - 请参见对下游包作者的建议。
有几种可供迁移的构建系统选择。假设您的软件包中有编译代码(如果没有,您有几个良好的选择,例如 Poetry、Hatch 或 PDM 提供的构建后端),并且您希望使用一个设计良好、现代化和可靠的构建系统,我们建议:
如果您只需要简单的 Cython/C 扩展(不需要 Fortran,BLAS/LAPACK,嵌套的setup.py
文件或其他numpy.distutils
的功能),并且迄今为止对numpy.distutils
感到满意,您也可以考虑切换到setuptools
。请注意,大多数numpy.distutils
的功能不太可能被迁移到setuptools
。
SciPy 在其 1.9.0 版本中已经迁移到了 Meson 和 meson-python。在此过程中,已解决了 Meson 的 Python 支持以及与numpy.distutils
功能平等的剩余问题。注意:平等意味着一个大的超集(因为 Meson 是一个很好的通用构建系统);只有一些 BLAS/LAPACK 库选择上的小问题缺失。SciPy 几乎使用了numpy.distutils
提供的所有功能,因此如果 SciPy 成功发布了使用 Meson 作为构建系统的版本,那么应该没有任何阻碍可以迁移,SciPy 将成为其他正在迁移的软件包的良好参考。有关 SciPy 迁移的更多详细信息,请参见:
NumPy 将在 1.26 版本中迁移到 Meson。
下一代 scikit-build 称为scikit-build-core。旧版的scikit-build
使用了setuptools
作为底层,但重写并没有。与 Meson 一样,CMake 是一个很好的通用构建系统。
setuptools
对于仅出于历史原因使用numpy.distutils
的项目,并且实际上没有使用setuptools
也支持的功能,移至setuptools
很可能是耗费最少努力的解决方案。为了评估,这里是setuptools
中没有存在的numpy.distutils
功能:
setup.py
文件
最广泛使用的功能是嵌套的setup.py
文件。这个功能可能将来仍然被移植到setuptools
中(需要一个志愿者,参见gh-18588了解状态)。只使用该功能的项目在完成后可以转移到setuptools
。如果一个项目只使用了几个setup.py
文件,将这些文件的所有内容聚合到一个单独的setup.py
文件中,然后转移到setuptools
也是有意义的。这涉及放弃所有Configuration
实例,并改用Extension
。例如:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[
Extension('foopkg.foo', ['foo.c']),
Extension('barpkg.bar', ['bar.c']),
],
)
更多详细信息,请参见setuptools 文档。
SciPy 已经在其 1.9.0 版本中迁移到 Meson 和 meson-python。在此过程中,解决了 Meson 的 Python 支持和与numpy.distutils
功能平衡的剩余问题。注意:平衡意味着一个大的超集(因为 Meson 是一个很好的通用构建系统);只有一些 BLAS/LAPACK 库选择的细微差别缺失。SciPy 几乎使用了numpy.distutils
提供的所有功能,因此如果 SciPy 成功使用 Meson 作为构建系统发布了一个版本,那么应该没有障碍可以迁移,SciPy 将成为其他正在迁移的软件包的良好参考。有关 SciPy 迁移的更多详细信息,请参见:
NumPy 将在 1.26 版本中迁移到 Meson。
下一代 scikit-build 被称为scikit-build-core。旧的scikit-build
使用setuptools
作为底层,而重写则不是。与 Meson 一样,CMake 是一个很好的通用构建系统。
setuptools
对于仅出于历史原因使用numpy.distutils
的项目,并且实际上没有使用setuptools
支持的功能,转移到setuptools
可能是付出最少努力的解决方案。为了评估这一点,有一些numpy.distutils
功能不存在于setuptools
中:
setup.py
文件。
最广泛使用的功能是嵌套的setup.py
文件。这个功能可能在将来仍然会被移植到setuptools
中(不过需要有志愿者,参见gh-18588了解状态)。只使用该功能的项目在完成后可以转移到setuptools
。如果一个项目只使用了几个setup.py
文件,将这些文件的所有内容聚合到一个单独的setup.py
文件中,然后转移到setuptools
也是有意义的。这涉及放弃所有的Configuration
实例,并使用Extension
代替。例如:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[
Extension('foopkg.foo', ['foo.c']),
Extension('barpkg.bar', ['bar.c']),
],
)
更多详情,请参阅setuptools 文档
numpy.distutils
与setuptools
的交互建议使用setuptools < 60.0
。更新的版本可能会工作,但不能保证。原因是setuptools
60.0 启用了distutils
的一个供应商副本,其中包含影响numpy.distutils
某些功能的不兼容更改。
如果你只是简单地使用 Cython 或 C 扩展,并且最多只是在嵌套的setup.py
文件之外使用了少量numpy.distutils
功能(它最受欢迎的功能,请参见Configuration
),那么最新的setuptools
可能会继续工作。如果出现问题,你也可以尝试SETUPTOOLS_USE_DISTUTILS=stdlib
来避免setuptools
中的不兼容更改。
无论你做什么,建议在pyproject.toml
中对你的setuptools
构建需求设置一个上限,以避免未来的破坏 - 参见对下游包作者的建议。
当心那些不愿意去理会细节的人。— *威廉·菲瑟(William Feather, Sr.)*真相在那里。— 克里斯·卡特(Chris Carter),《X 档案》
NumPy 提供了一个 C-API,使用户能够扩展系统并访问数组对象以在其他程序中使用。真正理解 C-API 的最佳方式是阅读源代码。然而,如果你不熟悉(C)源代码,这一开始可能会让人望而生畏。请放心,随着练习,这个任务会变得更容易,你可能会惊讶于理解 C 代码的简单程度。即使你认为自己无法从头编写 C 代码,理解和修改已经编写好的源代码要比从零开始编写要容易得多。
Python 扩展特别容易理解,因为它们都具有非常相似的结构。诚然,NumPy 不是 Python 的一个琐碎扩展,可能需要更多的探索才能理解。这尤其是因为代码生成技术简化了非常相似的代码的维护,但对初学者来说可能会使代码稍微难以阅读。不过,稍加坚持,你就能理解这些代码。我希望这份关于 C-API 的指南能够帮助你熟悉使用 NumPy 进行编译级工作的过程,以便从你的代码中挤出最后一丝必要的速度。
np.lib.tracemalloc_domain
进行内存跟踪的示例
原文:
numpy.org/doc/1.26/reference/c-api/types-and-structures.html
几种新类型在 C 代码中定义。其中大多数可以从 Python 中访问,但由于使用受限,有些则没有暴露出来。每个新的 Python 类型都有一个关联的PyObject*,其内部结构包括指向“方法表”的指针,定义了新对象在 Python 中的行为。在 C 代码中接收到 Python 对象时,始终会得到一个指向PyObject
结构的指针。因为 PyObject
结构非常通用,仅定义了 PyObject_HEAD
,因此本身并不是很有趣。但是,不同类型的对象在PyObject_HEAD
之后包含更多细节(但你必须将其转换为正确的类型才能访问它们 - 或者使用访问器函数或宏)。
Python 类型在 C 语言中等效于 Python 中的类。通过构建新的 Python 类型,可以为 Python 提供一个新的对象。ndarray 对象就是在 C 中定义的一个新类型的例子。通过两个基本步骤在 C 中定义新类型:
Py{Name}Object
),它与 PyObject
结构本身二进制兼容,但包含了特定对象需要的额外信息;
PyTypeObject
表(由PyObject
结构的 ob_type 成员指向)填充。
不再使用定义 Python 类的特殊方法名,而是使用指向实现所需结果的函数的“函数表”。自从 Python 2.2 开始,PyTypeObject 本身变得动态,允许从其他 C 类型“子类型化” C 类型,并在 Python 中派生子类。子类型继承其父类的属性和方法。
有两种主要的新类型:ndarray( PyArray_Type
)和 ufunc( PyUFunc_Type
)。其他类型起着支持作用:PyArrayIter_Type
、PyArrayMultiIter_Type
和 PyArrayDescr_Type
。PyArrayIter_Type
是用于 ndarray 的平面迭代器的类型(获取 flat 属性时返回的对象)。PyArrayMultiIter_Type
是在调用 broadcast
() 时返回的对象的类型。它处理对嵌套序列集合的迭代和广播。此外,PyArrayDescr_Type
是描述数据的数据类型描述符类型,其实例描述数据。最后,有 21 种新的标量数组类型,它们是与数组可用的每种基本数据类型对应的新的 Python 标量。另外还有 10 种其他类型是占位符,允许数组标量适应实际 Python 类型的层次结构。
PyArray_Type
ndarray 的 Python 类型是 PyArray_Type
。在 C 中,每个 ndarray 都是指向 PyArrayObject
结构的指针。此结构的 ob_type
成员包含指向 PyArray_Type
类型对象的指针。
type PyArrayObject
type NPY_AO
PyArrayObject
C 结构包含数组的所有必需信息。所有 ndarray(及其子类)的实例都将具有此结构。为了未来的兼容性,应该通常使用提供的宏来访问这些结构成员。如果需要更短的名称,那么可以使用已弃用的 NPY_AO
,它被定义为等同于 PyArrayObject
。直接访问结构字段已被弃用。请改用 PyArray_*(arr)
形式。截至 NumPy 1.20,此结构的大小不被视为 NumPy ABI 的一部分(请参见成员列表末尾的注释)。
typedef struct PyArrayObject {
PyObject_HEAD
char *data;
int nd;
npy_intp *dimensions;
npy_intp *strides;
PyObject *base;
PyArray_Descr *descr;
int flags;
PyObject *weakreflist;
/* version dependent private members */
} PyArrayObject;
PyObject_HEAD
这是所有 Python 对象所需的。它至少包含一个引用计数成员( ob_refcnt
)和一个指向类型对象的指针( ob_type
)。(如果 Python 是使用特殊选项编译的,还可能存在其他元素,请参阅 Python 源树中的 Include/object.h 了解更多信息)。ob_type
成员指向 Python 类型对象。
char *data
通过 PyArray_DATA
可访问,此数据成员是数组的第一个元素的指针。这个指针可以(通常应该)重新转换为数组的数据类型。
int nd
一个整数,提供此数组的维数。当 nd 为 0 时,有时称为秩-0 数组。这种数组具有未定义的维度和步幅,无法访问。宏PyArray_NDIM
定义在ndarraytypes.h
指向这个数据成员。NPY_MAXDIMS
是任何数组的最大维数。
*dimensions
一个整数数组,为每个维度提供该维度中的形状,只要 nd (\geq) 1。这个整数总是足够大,能够在平台上保存一个指针,所以维度大小仅受内存限制。PyArray_DIMS
是与这个数据成员相关联的宏。
*strides
一个整数数组,为每个维度提供必须跳过的字节数,以到达该维度中的下一个元素。与宏PyArray_STRIDES
相关联。
*base
由PyArray_BASE
指向,这个成员用于保存与这个数组相关的另一个 Python 对象的指针。有两个用例:
NPY_ARRAY_WRITEBACKIFCOPY
标志,那么这个数组是一个“不良”数组的工作副本。
当调用PyArray_ResolveWritebackIfCopy
时,base 指向的数组将使用这个数组的内容更新。
*descr
指向数据类型描述符对象的指针(见下文)。数据类型描述符对象是新建的内置类型的实例,它允许对内存进行通用描述。对每个支持的数据类型都存在一个描述符结构。这个描述符结构包含有关类型的有用信息,以及一个指向实现特定功能的函数指针表的指针。顾名思义,它与宏PyArray_DESCR
相关联。
int flags
由宏PyArray_FLAGS
指向,这个数据成员表示标志,指示数据指针指向的内存应如何解释。可能的标志是NPY_ARRAY_C_CONTIGUOUS
,NPY_ARRAY_F_CONTIGUOUS
,NPY_ARRAY_OWNDATA
,NPY_ARRAY_ALIGNED
,NPY_ARRAY_WRITEABLE
,NPY_ARRAY_WRITEBACKIFCOPY
。
*weakreflist
这个成员允许数组对象具有弱引用(使用 weakref 模块)。
注意
其他成员被视为私有和与版本有关。如果结构的大小对您的代码很重要,必须特别小心。当这一点相关时的一种可能的用例是在 C 中进行子类化。如果您的代码依赖于 sizeof(PyArrayObject)
的大小是不变的,您在导入时必须添加以下检查:
if (sizeof(PyArrayObject) < PyArray_Type.tp_basicsize) {
PyErr_SetString(PyExc_ImportError,
"Binary incompatibility with NumPy, must recompile/update X.");
return NULL;
}
为了确保您的代码不必为特定的 NumPy 版本进行编译,您可以添加一个常数,留出 NumPy 变化的空间。可确保与将来任何 NumPy 版本兼容的解决方案需要使用运行时计算偏移和分配大小。
PyArrayDescr_Type
PyArrayDescr_Type
是用于描述数组所包含的字节应如何解释的数据类型描述对象的内置类型。内置数据类型有 21 个静态定义的 PyArray_Descr
对象。虽然这些对象参与引用计数,但它们的引用计数永远不应该达到零。还有一个动态的用户自定义 PyArray_Descr
对象表也会被维护。一旦数据类型描述对象被“注册”,它就不应该被释放。函数 PyArray_DescrFromType
(…) 可以用来从一个枚举类型编号(内置或用户自定义)中检索出一个 PyArray_Descr
对象。
type PyArray_Descr
PyArray_Descr
结构位于 PyArrayDescr_Type
的核心。虽然这里描述了它,但它应被视为 NumPy 的内部部分,并通过 PyArrayDescr_*
或 PyDataType*
函数和宏进行操作。这个结构的大小会随着 NumPy 的版本变化而变化。为确保兼容性:
sizeof(PyArray_Descr)
它具有以下结构:
typedef struct {
PyObject_HEAD
PyTypeObject *typeobj;
char kind;
char type;
char byteorder;
char flags;
int type_num;
int elsize;
int alignment;
PyArray_ArrayDescr *subarray;
PyObject *fields;
PyObject *names;
PyArray_ArrFuncs *f;
PyObject *metadata;
NpyAuxData *c_metadata;
npy_hash_t hash;
} PyArray_Descr;
*typeobj
指向该数组元素对应的 Python 类型的类型对象的指针。对于内置类型,这将指向对应的 array scalar。对于用户定义类型,这应该指向用户定义的类型对象。这个类型对象可以继承自数组标量,也可以不继承。如果它不继承自数组标量,那么flags
成员中应该设置 NPY_USE_GETITEM
和 NPY_USE_SETITEM
标志。
char kind
表示数组种类的字符代码(使用数组接口类型字符串表示)。‘b’ 表示布尔型,‘i’ 表示有符号整数,‘u’ 表示无符号整数,‘f’ 表示浮点型,‘c’ 表示复数浮点型,‘S’ 表示 8 位零终结字节,‘U’ 表示 32 位/字符 Unicode 字符串,‘V’ 表示任意类型。
char type
指示数据类型的传统字符代码。
char byteorder
表示字节顺序的字符:‘>’(大端),‘<’(小端),‘=’(本地),‘|’(不相关,忽略)。所有内置数据类型的字节顺序为‘=’。
char flags
决定数据类型是否具有对象数组行为的数据类型位标志。此成员中的每个位都是一个标志,其命名为:
NPY_ITEM_REFCOUNT
NPY_ITEM_HASOBJECT
NPY_LIST_PICKLE
NPY_ITEM_IS_POINTER
NPY_NEEDS_INIT
NPY_NEEDS_PYAPI
NPY_USE_GETITEM
NPY_USE_SETITEM
NPY_FROM_FIELDS
NPY_OBJECT_DTYPE_FLAGS
int type_num
唯一标识数据类型的数字。对于新的数据类型,当数据类型注册时会分配此数字。
int elsize
对于始终大小相同的数据类型(例如 long),这表示数据类型的大小。对于灵活的数据类型,其中不同的数组可以具有不同的元素大小,这应为 0。
int alignment
提供此数据类型的对齐信息的数字。具体来说,它显示编译器在从 2 个元素结构的开始(其第一个元素是一个 char
)放置此类型的项目的距离:offsetof(struct {char c; type v;}, v)
*subarray
如果此为非 NULL
,则此数据类型描述符是另一个数据类型描述符的 C 风格连续数组。换句话说,此描述符描述的每个元素实际上是另一个基本描述符的数组。如果此为非 NULL
,则字段成员应为 NULL
(但是基本描述符的字段成员可以为非 NULL
)。
type PyArray_ArrayDescr
typedef struct {
PyArray_Descr *base;
PyObject *shape;
} PyArray_ArrayDescr;
*base
基本类型的数据类型描述符对象。
*shape
子数组的形状(始终为 C 风格连续)作为 Python 元组。
*fields
如果此为非空,则此数据类型描述符具有由 Python 字典描述的字段,其键是名称(如果给定也是标题),其值是描述字段的元组。请注意,数据类型描述符始终描述一组固定长度的字节。字段是总的、固定长度集合的命名子区域。字段由另一个数据类型描述符和字节偏移量组成的元组描述。可选地,元组可能包含通常为 Python 字符串的标题。这些元组根据名称(如果给定还有标题)放置在此字典中。
*names
字段名称的有序元组。如果未定义字段,则为 NULL。
*f
指向一个包含类型需要实现内部特性的函数的结构体的指针。这些函数不同于后面描述的通用函数(ufuncs)。它们的签名可以任意变化。
*metadata
关于此数据类型的元数据。
*c_metadata
这些特定于特定 dtype 的 C 实现的元数据。增加于 NumPy 1.7.0。
type npy_hash_t
*hash
目前未使用。保留以用于在缓存哈希值中将来使用。
NPY_ITEM_REFCOUNT
表示此数据类型的项必须进行引用计数(使用Py_INCREF
和 Py_DECREF
)。
NPY_ITEM_HASOBJECT
等同于NPY_ITEM_REFCOUNT
。
NPY_LIST_PICKLE
表示必须将这种数据类型的数组在 pickling 之前转换为列表。
NPY_ITEM_IS_POINTER
表示该项是指向其他数据类型的指针。
NPY_NEEDS_INIT
表示必须在创建时初始化(设置为 0)此数据类型的内存。
NPY_NEEDS_PYAPI
表示在访问时此数据类型需要 Python C-API(因此如果需要数组访问,请不要放弃 GIL)。
NPY_USE_GETITEM
在数组访问时,使用f->getitem
函数指针,而不是标准的转换为数组标量。如果没有定义与数据类型相匹配的数组标量,必须使用。
NPY_USE_SETITEM
当从数组标量创建 0 维数组时,使用f->setitem
而不是标准的从数组标量复制。如果你没有定义与数据类型相匹配的数组标量,必须使用。
NPY_FROM_FIELDS
如果在数据类型的任何字段中设置了这些位,则从父数据类型继承这些位。目前(NPY_NEEDS_INIT
| NPY_LIST_PICKLE
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_PYAPI
)。
NPY_OBJECT_DTYPE_FLAGS
为对象数据类型设置的位:(NPY_LIST_PICKLE
| NPY_USE_GETITEM
| NPY_ITEM_IS_POINTER
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_INIT
| NPY_NEEDS_PYAPI
)。
int PyDataType_FLAGCHK( *dtype, int flags)
如果数据类型对象的所有给定标志都设置为真,则返回真。
int PyDataType_REFCHK( *dtype)
等同于PyDataType_FLAGCHK
(dtype,NPY_ITEM_REFCOUNT
)。
type PyArray_ArrFuncs
实现内部特性的函数。并非必须为给定类型定义所有这些函数指针。必须定义的成员包括nonzero
、copyswap
、copyswapn
、setitem
、getitem
和cast
。这默认假定为非 NULL
,而NULL
条目将导致程序崩溃。其他函数可以是 NULL
,这只会导致该数据类型的功能减少。(此外,如果在注册用户定义的数据类型时nonzero
函数为空,将使用默认函数填充nonzero
函数)。
typedef struct {
PyArray_VectorUnaryFunc *cast[NPY_NTYPES];
PyArray_GetItemFunc *getitem;
PyArray_SetItemFunc *setitem;
PyArray_CopySwapNFunc *copyswapn;
PyArray_CopySwapFunc *copyswap;
PyArray_CompareFunc *compare;
PyArray_ArgFunc *argmax;
PyArray_DotFunc *dotfunc;
PyArray_ScanFunc *scanfunc;
PyArray_FromStrFunc *fromstr;
PyArray_NonzeroFunc *nonzero;
PyArray_FillFunc *fill;
PyArray_FillWithScalarFunc *fillwithscalar;
PyArray_SortFunc *sort[NPY_NSORTS];
PyArray_ArgSortFunc *argsort[NPY_NSORTS];
PyObject *castdict;
PyArray_ScalarKindFunc *scalarkind;
int **cancastscalarkindto;
int *cancastto;
PyArray_FastClipFunc *fastclip; /* deprecated */
PyArray_FastPutmaskFunc *fastputmask; /* deprecated */
PyArray_FastTakeFunc *fasttake; /* deprecated */
PyArray_ArgFunc *argmin;
} PyArray_ArrFuncs;
描述函数指针时使用了行为良好段的概念。行为良好的段是指与数据类型对齐且符合本机字节顺序的段。nonzero
、copyswap
、copyswapn
、getitem
和 setitem
函数可以(必须)处理不规范的数组。其他函数则需要行为良好的内存段。
void cast(void *from, void *to, n, void *fromarr, void *toarr)
一个函数指针数组,用于将当前类型转换为所有其他内置类型。每个函数都将由 from 指向的连续、对齐且未交换的缓冲区转换为由 to 指向的连续、对齐且未交换的缓冲区。要转换的项数由 n 给出,并且参数 fromarr 和 toarr 被解释为灵活数组的 PyArrayObjects 以获取 itemsize 信息。
*getitem(void *data, void *arr)
一个函数指针,用于从由 data 指向的数组对象 arr 的单个元素返回标准 Python 对象。此函数必须能够正确处理“不规范”的(未对齐和/或交换的)数组。
int setitem( *item, void *data, void *arr)
一个函数指针,用于将 Python 对象 item 设置到由 data 指向的数组 arr 中的位置。此函数处理“不规范”的数组。如果成功,返回零,否则返回负一(并设置 Python 错误)。
void copyswapn(void *dest, dstride, void *src, sstride, n, int swap, void *arr)
void copyswap(void *dest, void *src, int swap, void *arr)
这些成员都是指向从 src 复制数据到 dest 并在需要时交换的函数的指针。仅当为灵活数组(NPY_STRING
、NPY_UNICODE
和 NPY_VOID
)时才使用 arr 的值(从 arr->descr->elsize
获取)。第二个函数复制单个值,而第一个函数则使用提供的步幅循环 n 值。这些函数可以处理不规范的 src 数据。如果 src 为 NULL,则不执行复制。如果 swap 为 0,则不执行字节交换。假设 dest 和 src 不重叠。如果它们重叠,则首先使用 memmove
(…)再使用值为 NULL 的 src
执行 copyswap(n)
。
int compare(const void *d1, const void *d2, void *arr)
一个函数指针,用于比较由 d1
和 d2
指向的数组 arr
的两个元素。此函数需要行为良好(对齐且未交换)的数组。如果 * d1
> * d2
,返回值为 1;如果 * d1
== * d2
,返回值为 0;如果 * d1
< * d2
,返回值为 -1。数组对象 arr
用于检索灵活数组的 itemsize 和字段信息。
int argmax(void *data, n, *max_ind, void *arr)
一个函数指针,用于从由 data 指向的元素开始的 n
个元素中检索最大的索引。此函数要求内存段是连续且行为良好的。返回值始终为 0。最大元素的索引在 max_ind
中返回。
void dotfunc(void *ip1, is1, void *ip2, is2, void *op, n, void *arr)
一个指向将两个n
长度的序列相乘,相加,并将结果放置到由op
指向的arr
元素中的函数的指针。序列的开始分别由ip1
和ip2
指向。要获取每个序列中的下一个元素需要跳过is1
和is2
字节。此函数需要表现良好的(尽管不一定是连续的)内存。
int scanfunc(FILE *fd, void *ip, void *arr)
一个指向从文件描述符fd
中扫描(类似于 scanf)相应类型的元素到由ip
指向的数组内存中的函数的指针。假定数组表现良好。最后一个参数arr
是要扫描的数组。返回成功分配的接收参数的数量(如果在分配第一个接收参数之前匹配失败,则可能为零),或者如果在分配第一个接收参数之前发生输入故障,则为 EOF。调用此函数时应该不持有 Python GIL,并且必须为错误报告抓取它。
int fromstr(char *str, void *ip, char **endptr, void *arr)
一个指向将str
指向的字符串转换为相应类型的一个元素并将其放置到由ip
指向的内存位置的函数的指针。转换完成后,*endptr
指向字符串的其余部分。最后一个参数arr
是 ip 指向的数组(需要用于变量大小数据类型)。成功返回 0,失败返回-1。需要一个表现良好的数组。调用此函数时应该不持有 Python GIL,并且必须为错误报告抓取它。
nonzero(void *data, void *arr)
一个指向如果data
指向的arr
的项为非零则返回 TRUE 的函数的指针。此函数可以处理行为不当的数组。
void fill(void *data, length, void *arr)
一个指向填充给定长度的连续数组数据的函数的指针。数组的前两个元素必须已经被填充。根据这两个值,将计算出一个增量,然后从第 3 个元素到最后的值将通过重复添加这个计算出的增量来计算。数据缓冲区必须表现良好。
void fillwithscalar(void *buffer, length, void *value, void *arr)
一个指向使用单个标量value
的地址填充给定长度
的连续buffer
的函数的指针。最后一个参数是所需用于变长数组的 itemsize 的数组。
int sort(void *start, length, void *arr)
一个指向特定排序算法的函数指针数组。可以使用一个关键字(到目前为止定义了NPY_QUICKSORT
、NPY_HEAPSORT
和NPY_MERGESORT
)。这些排序是在假定连续和对齐的数据上进行的。
int argsort(void *start, *result, length, void *arr)
一个指向此数据类型的排序算法的函数指针数组。可用于排序的排序算法与 sort 相同。产生排序的索引被返回到result
中(必须初始化为包含 0 到length-1
的索引)。
*castdict
可以是NULL
或包含用户定义数据类型的低级转换函数的字典。每个函数都包装在一个PyCapsule*中,并以数据类型编号为键。
scalarkind( *arr)
用于确定此类型的标量应如何解释的函数。参数是一个包含数据的 0 维数组(如果需要确定标量的种类,则需要该数据)。返回值必须是类型为NPY_SCALARKIND
的值。
int **cancastscalarkindto
可以是NULL
或一个包含NPY_NSCALARKINDS
指针的数组。这些指针每个都应该是NULL
,或者是指向整数数组的指针(以NPY_NOTYPE
终止),指示此数据类型的指定种类的标量可以安全地转换为的数据类型(通常意味着不会失去精度)。
int *cancastto
可以是NULL
或一个整数数组(以NPY_NOTYPE
终止),指示此数据类型可以安全地转换为的数据类型(通常意味着不会失去精度)。
void fastclip(void *in, n_in, void *min, void *max, void *out)
自版本 1.17 起弃用:使用此函数将在np.clip
时产生弃用警告。不再使用此函数,数据类型必须使用PyUFunc_RegisterLoopForDescr
将自定义循环附加到np.core.umath.clip
、np.minimum
和np.maximum
。
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
从in
中读取n_in
项,并在其指向的限制范围内写入到out
,如果超出范围,则写入相应的限制值。内存段必须是连续的且行为良好,min
和max
中的一个可以是NULL
,但不能同时为空。
void fastputmask(void *in, void *mask, n_in, void *values, nv)
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
一个函数,它接受一个指向长度为n_in
项的数组的指针in
,一个指向长度为n_in
的布尔值数组的指针mask
,以及一个指向长度为nv
项的数组的指针vals
。将vals
中的项复制到in
中,其中mask
中的值为非零,如果nv < n_in
,则根据需要平铺vals
。所有数组必须是连续的且行为良好。
void fasttake(void *dest, void *src, *indarray, nindarray, n_outer, m_middle, nelem, clipmode)
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
一个函数,它接受一个指针src
,指向一个 C 连续的、行为良好的段,解释为形状为(n_outer, nindarray, nelem)
的三维数组,一个指向m_middle
整数索引的 C 连续的、行为良好的段的指针indarray
,以及一个指向 C 连续的、行为良好的段的指针dest
,解释为形状为(n_outer, m_middle, nelem)
的三维数组。indarray
中的索引用于沿第二维索引src
,并将对应的nelem
项的块复制到dest
中。clipmode
(可以取值NPY_RAISE
、NPY_WRAP
或 NPY_CLIP
)确定如何处理小于 0 或大于nindarray
的索引。
int argmin(void *data, n, *min_ind, void *arr)
一个函数指针,用于检索从data
指向的元素开始的包含n
个元素的arr
中最小元素的索引。此函数要求内存段是连续的且行为良好。返回值始终为 0。最小元素的索引将返回到min_ind
中。
PyArray_Type
类型对象实现了许多Python 对象
的特性,包括tp_as_number
、tp_as_sequence
、tp_as_mapping
和tp_as_buffer
接口。富比较
也与新式属性查找一起使用,用于成员(tp_members
)和属性(tp_getset
)的访问。PyArray_Type
也可以被子类型化。
提示
tp_as_number
方法使用一种通用方法调用已注册用于处理操作的任何函数。当导入_multiarray_umath 模块
时,它将所有数组的数值操作设置为相应的 ufuncs。可以使用PyUFunc_ReplaceLoopBySignature
来更改此选择。tp_str
和 tp_repr
方法也可以使用PyArray_SetStringFunction
进行更改。
PyUFunc_Type
ufunc 对象是通过创建PyUFunc_Type
来实现的。 它是一种非常简单的类型,只实现了基本的 getAttribute 行为、打印行为,并且有 call 行为,这样这些对象就可以像函数一样。 ufunc 的基本思想是保存对支持操作的数据类型的快速 1 维(向量)循环的引用。 所有这些一维循环都具有相同的签名,并且是创建新 ufunc 的关键。 它们由通用循环代码适当调用以实现 N 维函数。 还为浮点和复数浮点数组定义了一些通用的一维循环,允许你使用单个标量函数(例如atanh)来定义 ufunc。
type PyUFuncObject
ufunc 的核心是PyUFuncObject
,其中包含调用执行实际工作的基础 C 代码循环所需的所有信息。 虽然这里描述了这一点以确保兼容性: infotext default可以在不同的 NumPy 版本中更改。 为了确保兼容性。
sizeof(PyUFuncObject)
它具有以下结构:
typedef struct {
PyObject_HEAD
int nin;
int nout;
int nargs;
int identity;
PyUFuncGenericFunction *functions;
void **data;
int ntypes;
int reserved1;
const char *name;
char *types;
const char *doc;
void *ptr;
PyObject *obj;
PyObject *userloops;
int core_enabled;
int core_num_dim_ix;
int *core_num_dims;
int *core_dim_ixs;
int *core_offsets;
char *core_signature;
PyUFunc_TypeResolutionFunc *type_resolver;
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector;
void *reserved2;
npy_uint32 *op_flags;
npy_uint32 *iter_flags;
/* new in API version 0x0000000D */
npy_intp *core_dim_sizes;
npy_uint32 *core_dim_flags;
PyObject *identity_value;
/* Further private slots (size depends on the NumPy version) */
} PyUFuncObject;
int nin
输入参数的数量。
int nout
输出参数的数量。
int nargs
参数的总数(* nin * + * nout *)。 这必须小于NPY_MAXARGS
。
int identity
PyUFunc_One
,PyUFunc_Zero
,PyUFunc_MinusOne
,PyUFunc_None
,PyUFunc_ReorderableNone
或PyUFunc_IdentityValue
中的任何一个,以指示此操作的标识。 仅用于对空数组进行类似缩小的调用。
void functions(char **args, *dims, *steps, void *extradata)
一个函数指针数组 - 每种 ufunc 支持的数据类型一个。 这是被调用以实现基础功能dims[0]次的向量循环。 第一个参数args是一个* nargs 指针的数组,指向行为良好的内存。 首先是输入参数数据的指针,然后是输出参数数据的指针。 必须跳过多少字节才能到达序列中下一个元素由 steps 数组中的对应条目指定。 最后一个参数允许循环接收额外信息。 通常用于使单个通用向量循环用于多个函数。 在这种情况下,要调用的实际标量函数被传递为 extradata *。 这个函数指针数组的大小是 ntypes。
void **data
要传递给 1-d 向量循环的额外数据,如果不需要额外数据则为NULL
。这个 C 数组必须与函数数组的大小相同(即 ntypes)。如果不需要额外数据,则使用NULL
。几个用于 UFuncs 的 C API 调用只是利用这些额外数据运行 1-d 向量循环,以接收指向要调用的实际函数的指针。
int ntypes
ufunc 支持的数据类型数。这个数字指定可用的不同 1-d 循环(内置数据类型)的数量。
int reserved1
未使用。
char *name
为 ufunc 的字符串名称。这是动态使用的,用于构建 ufunc 的 doc 属性。
char *types
一个 (nargs \times ntypes) 8 位类型编号数组,包含每个支持(内置)数据类型的函数的类型签名。对于每个 ntypes 函数,该数组中对应的一组类型编号显示了如何在 1-d 向量循环中解释 args 参数。这些类型编号不必是相同类型,支持混合类型的 ufunc。
char *doc
用于 ufunc 的文档。不应包含函数签名,因为在检索 doc 时动态生成。
void *ptr
任何动态分配的内存。目前,这用于从 Python 函数创建动态 ufunc,用于存储类型、数据和名称成员的空间。
*obj
对于从 Python 函数动态创建的 ufunc,该成员持有对底层 Python 函数的引用。
*userloops
用户定义的 1-d 向量循环的字典(存储为 CObject 指针)用于用户定义的类型。用户可以为任何用户定义的类型注册循环。它通过类型编号检索。用户定义的类型编号总是大于NPY_USERDEF
。
int core_enabled
标量 ufunc 为 0;广义 ufunc 为 1
int core_num_dim_ix
签名中具有不同核心维度名称的数量
int *core_num_dims
每个参数的核心维度数
int *core_dim_ixs
展平形式的维度索引;参数k
的索引存储在core_dim_ixs[core_offsets[k] : core_offsets[k] + core_numdims[k]]
中
int *core_offsets
每个参数在core_dim_ixs
中第 1 个核心维度的位置,相当于 cumsum(core_num_dims
)
char *core_signature
核心签名字符串
PyUFunc_TypeResolutionFunc *type_resolver
解析类型并填充输入和输出的 dtypes 的函数
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector
从版本 1.22 起弃用:此插槽存在一些回退支持,但最终将被移除。依赖于此的通用函数最终将需要移植。请参阅NEP 41 和 NEP 43
void *reserved2
用于可能的具有不同签名的未来循环选择器。
op_flags
为每个 ufunc 操作数覆盖默认操作数标志。
iter_flags
为 ufunc 覆盖默认 nditer 标志。
添加在 API 版本 0x0000000D 中
*core_dim_sizes
对于每个不同的核心维度,如果 UFUNC_CORE_DIM_SIZE_INFERRED
为 0
,则可能的 frozen 大小
*core_dim_flags
对于每个不同的核心维度,一组标志(UFUNC_CORE_DIM_CAN_IGNORE
和 UFUNC_CORE_DIM_SIZE_INFERRED
)
*identity_value
缩减的标识,当 PyUFuncObject.identity
等于 PyUFunc_IdentityValue
时。
UFUNC_CORE_DIM_CAN_IGNORE
如果维度名称以 ?
结尾
UFUNC_CORE_DIM_SIZE_INFERRED
如果维度大小将由操作数而不是从 frozen 签名确定
PyArrayIter_Type
这是一个迭代器对象,使得可以轻松地循环遍历 N 维数组。它是从 ndarray 的 flat 属性返回的对象。它还在整个实现内部广泛使用,以循环遍历 N 维数组。实现了 tp_as_mapping 接口,以便可以索引迭代器对象(使用 1-d 索引),并且通过 tp_methods 表实现了一些方法。此对象实现了 next 方法,并且可以在 Python 中使用任何迭代器可以使用的地方。
type PyArrayIterObject
与 PyArrayIter_Type
对象对应的 C 结构是 PyArrayIterObject
。 PyArrayIterObject
用于跟踪指向 N 维数组的指针。它包含用于快速遍历数组的相关信息。指针可以通过三种基本方式进行调整:1)以 C 风格连续地前进到数组中的“下一个”位置,2)前进到数组中的任意 N 维坐标,和 3)前进到数组中的任意一维索引。PyArrayIterObject
结构的成员在这些计算中使用。迭代器对象保存关于数组的自己的维度和跨度信息。这可以根据需要进行“广播”,或者仅循环特定维度。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index;
npy_intp size;
npy_intp coordinates[NPY_MAXDIMS];
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
} PyArrayIterObject;
int nd_m1
(N-1) 其中 (N) 是底层数组中的维数。
index
数组中的当前 1-d 索引。
size
底层数组的总大小。
*coordinates
对数组的 (N) 维索引。
*dims_m1
数组在每个维度上的大小减去 1。
*strides
数组的跨度。在每个维度跳转到下一个元素所需的字节数。
*backstrides
从维度的末尾跳回到其开头所需的字节数。请注意 backstrides[k] == strides[k] * dims_m1[k]
,但这里存储为一种优化。
*factors
此数组用于从 1-d 索引计算 N-d 索引。它包含维度的所需乘积。
*ao
指向创建此迭代器代表的底层 ndarray 的指针。
char *dataptr
此成员指向由索引指示的 ndarray 中的元素。
contiguous
若底层数组是 NPY_ARRAY_C_CONTIGUOUS
,则此标志为真。在可能的情况下,可用于简化计算。
如何在 C 级别上使用数组迭代器在后续章节中有更详细的解释。通常,您无需关心迭代器对象的内部结构,只需通过宏 PyArray_ITER_NEXT
(it)、PyArray_ITER_GOTO
(it, dest) 或 PyArray_ITER_GOTO1D
(it, index) 与其进行交互。所有这些宏都需要参数 it 为 PyArrayIterObject*。
PyArrayMultiIter_Type
该类型提供了一种封装广播概念的迭代器。它允许 (N) 个数组一起进行广播,使循环以 C 样式连续方式在广播的数组上进行。相应的 C 结构是 PyArrayMultiIterObject
,其内存布局必须以传递给 PyArray_Broadcast
(obj) 函数的任何对象 obj 开始。通过调整数组迭代器来执行广播,使得每个迭代器表示广播的形状和大小,但其步长被调整,以便在每次迭代中使用数组的正确元素。
type PyArrayMultiIterObject
typedef struct {
PyObject_HEAD
int numiter;
npy_intp size;
npy_intp index;
int nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject *iters[NPY_MAXDIMS];
} PyArrayMultiIterObject;
int numiter
需要广播到相同形状的数组的数量。
size
总的广播大小。
index
当前(1-d)索引进入广播结果。
int nd
广播结果中的维数。
*dimensions
广播结果的形状(仅使用 nd
槽)。
**iters
一个迭代器对象数组,其中包含要一起广播的数组的迭代器。返回时,迭代器会进行调整以进行广播。
PyArrayNeighborhoodIter_Type
这是一个迭代器对象,可轻松循环遍历 N 维邻域。
type PyArrayNeighborhoodIterObject
与 PyArrayNeighborhoodIter_Type
对象对应的 C 结构是 PyArrayNeighborhoodIterObject
。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index, size;
npy_intp coordinates[NPY_MAXDIMS]
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
npy_intp bounds[NPY_MAXDIMS][2];
npy_intp limits[NPY_MAXDIMS][2];
npy_intp limits_sizes[NPY_MAXDIMS];
npy_iter_get_dataptr_t translate;
npy_intp nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject* _internal_iter;
char* constant;
int mode;
} PyArrayNeighborhoodIterObject;
PyArrayFlags_Type
当从 Python 中检索 flags 属性时,将构建此特殊内置对象的特殊类型。这种特殊类型通过将它们作为属性访问或通过将对象视为具有标志名称条目的字典来访问它们,使得更容易处理不同的标志。
type PyArrayFlagsObject
typedef struct PyArrayFlagsObject {
PyObject_HEAD
PyObject *arr;
int flags;
} PyArrayFlagsObject;
每种内置数据类型在 Python 中都有一个类型。这些大多是简单的包装器,用于对应 C 中的相应数据类型。这些类型的 C 名称为Py{TYPE}ArrType_Type
,其中{TYPE}
可以是
Bool,Byte,Short,Int,Long,LongLong,UByte,UShort,UInt,ULong,ULongLong,Half,Float,Double,LongDouble,CFloat,CDouble,CLongDouble,String,Unicode,Void和Object。
这些类型名称是 C-API 的一部分,因此可以在扩展的 C 代码中创建。还有一个PyIntpArrType_Type
和一个PyUIntpArrType_Type
,它们是平台上可以容纳指针的整数类型之一的简单替代品。这些标量对象的结构对 C 代码不可见。函数PyArray_ScalarAsCtype
(…)可以用于从数组标量中提取 C 类型值,函数PyArray_Scalar
(…)可以用于从 C 值构造数组标量。
开发 NumPy 时发现有几个新的 C-结构很有用。这些 C 结构至少在一个 C-API 调用中使用,因此在这里记录。定义这些结构的主要原因是为了方便使用 Python ParseTuple C-API,将 Python 对象转换为有用的 C 对象。
type PyArray_Dims
当形状和/或步幅信息被解释时,这个结构非常有用。结构如下:
typedef struct {
npy_intp *ptr;
int len;
} PyArray_Dims;
这个结构体的成员是
*ptr
一个指向(npy_intp
)整数列表的指针,通常表示数组形状或数组步幅。
int len
整数列表的长度。假定安全访问ptr[0]至ptr[len-1]。
type PyArray_Chunk
这与 Python 中的缓冲对象结构相当,直到 ptr 成员。在 32 位平台上(即如果NPY_SIZEOF_INT
== NPY_SIZEOF_INTP
),len 成员也与缓冲对象的等效成员匹配。它用于表示通用的单段内存块。
typedef struct {
PyObject_HEAD
PyObject *base;
void *ptr;
npy_intp len;
int flags;
} PyArray_Chunk;
成员是
*base
此内存块来自的 Python 对象。需要这样才能正确地计算内存。
void *ptr
单段内存块开始的指针。
len
段的长度(以字节为单位)。
int flags
用来解释内存的任何数据标志(例如 NPY_ARRAY_WRITEABLE
)。
另请参阅
数组接口协议
type PyArrayInterface
PyArrayInterface
结构被定义为 NumPy 和其他扩展模块可以使用快速的数组接口协议。支持快速数组接口协议的对象的__array_struct__
方法应该返回一个包含指向PyArrayInterface
结构的相关细节的指针的PyCapsule
。创建新数组后,应该对属性执行DECREF
,这将释放PyArrayInterface
结构。记得对检索到的__array_struct__
属性的对象进行INCREF
,并将新的PyArrayObject
的 base 成员指向这个相同的对象。以这种方式管理数组的内存将是正确的。
typedef struct {
int two;
int nd;
char typekind;
int itemsize;
int flags;
npy_intp *shape;
npy_intp *strides;
void *data;
PyObject *descr;
} PyArrayInterface;
int two
整数 2 作为一个健全性检查。
int nd
数组中的维数。
char typekind
根据类型字符串约定,指示存在何种类型数组的字符,‘t’ -> 位域,‘b’ -> 布尔值,‘i’ -> 有符号整数,‘u’ -> 无符号整数,‘f’ -> 浮点数,‘c’ -> 复数浮点数,‘O’ -> 对象,‘S’ -> (字节)字符串,‘U’ -> Unicode,‘V’ -> 空。
int itemsize
数组中每个项需要的字节数。
int flags
位NPY_ARRAY_C_CONTIGUOUS
(1), NPY_ARRAY_F_CONTIGUOUS
(2), NPY_ARRAY_ALIGNED
(0x100), NPY_ARRAY_NOTSWAPPED
(0x200),或NPY_ARRAY_WRITEABLE
(0x400),指示有关数据的一些信息。NPY_ARRAY_ALIGNED
、NPY_ARRAY_C_CONTIGUOUS
和NPY_ARRAY_F_CONTIGUOUS
标志实际上可以从其他参数中确定。标志NPY_ARR_HAS_DESCR
(0x800)也可以设置为指示给消耗版本 3 数组接口的对象,这个结构的 descr 成员是存在的(对消耗版本 2 数组接口的对象会被忽略)。
*shape
包含每个维度中数组大小的数组。
*strides
包含每个维度中移动到下一个元素所需的字节数的数组。
void *data
数组的第一个元素的指针。
*descr
更详细描述数据类型的 Python 对象(与__array_interface__
中的descr键相同)。如果typekind和itemsize提供足够信息,则这可以是NULL
。除非flags中打开了 NPY_ARR_HAS_DESCR
标志,否则也会忽略此字段。
内部,代码使用一些额外的 Python 对象主要用于内存管理。这些类型无法直接从 Python 访问,也不暴露给 C-API。它们在这里仅用于完整性和帮助理解代码。
type PyUFunc_Loop1d
一个包含为每个用户定义的数据类型的每个已定义签名定义 1-d 循环的信息的 C-结构的简单链接列表。
PyArrayMapIter_Type
高级索引由这种 Python 类型处理。它只是包装了包含高级数组索引所需变量的 C 结构的松散包装。
type PyArrayMapIterObject
与PyArrayMapIter_Type
相关联的 C 结构。如果您试图理解高级索引映射代码,这个结构很有用。它在arrayobject.h
头文件中定义。这种类型不暴露给 Python,可以用 C 结构替换。作为 Python 类型,它利用了引用计数内存管理。
Python 类型相当于 Python 中的类。通过构建一个新的 Python 类型,您可以为 Python 提供一个新对象。ndarray
对象就是在 C 中定义的一个新类型的示例。定义新类型的基本步骤是:
PyObject
结构本身二进制兼容但包含特定对象所需的附加信息的 C 结构(通常命名为Py{Name}Object
);
PyTypeObject
表(由PyObject
结构的 ob_type 成员指向的):指向实现该类型所需行为的函数的指针。
而不是为 Python 类定义行为的特殊方法名称,有指向实现所需结果的功能表。自 Python 2.2 以来,PyTypeObject
本身已经变得动态,允许在 C 中“从其他 C 类型”继承的 C 类型,“在 Python 中子类化”。子类型从其父类型继承属性和方法。
有两个主要的新类型:ndarray
( PyArray_Type
)和ufunc
( PyUFunc_Type
)。其他类型起辅助作用:PyArrayIter_Type
,PyArrayMultiIter_Type
和PyArrayDescr_Type
。PyArrayIter_Type
是ndarray
的平坦迭代器类型(在获取 flat 属性时返回的对象)。PyArrayMultiIter_Type
是调用broadcast
()时返回的对象类型。它处理迭代和广播集合的嵌套序列。另外,PyArrayDescr_Type
是数据类型描述符类型,其实例描述数据。最后,还有 21 种新的标量数组类型,这些是对应于数组的可用基本数据类型的新 Python 标量。另外还有 10 种其他类型,这些是占位符,允许数组标量适应实际 Python 类型的层次结构。
PyArray_Type
和PyArrayObject
PyArray_Type
ndarray
的 Python 类型是PyArray_Type
。在 C 中,每个ndarray
都是指向PyArrayObject
结构体的指针。该结构体的ob_type
成员包含一个指向PyArray_Type
类型对象的指针。
type PyArrayObject
type NPY_AO
PyArrayObject
C 结构体包含数组的所有必需信息。所有ndarray
实例(及其子类)都将具有此结构。为了将来的兼容性,应使用提供的宏来访问这些结构成员。如果需要更短的名称,可以使用NPY_AO
(已弃用),其定义等同于PyArrayObject
。已弃用直接访问结构字段。请改为使用PyArray_*(arr)
形式。从 NumPy 1.20 开始,这个结构的大小不被视为 NumPy ABI 的一部分(请参见成员列表末尾的说明)。
typedef struct PyArrayObject {
PyObject_HEAD
char *data;
int nd;
npy_intp *dimensions;
npy_intp *strides;
PyObject *base;
PyArray_Descr *descr;
int flags;
PyObject *weakreflist;
/* version dependent private members */
} PyArrayObject;
PyObject_HEAD
这对所有 Python 对象都是必需的。它由(至少)一个引用计数成员(ob_refcnt
)和一个指向类型对象(ob_type
)的指针组成。(如果 Python 是使用特殊选项编译的,可能还有其他元素,请参见 Python 源代码树中的 Include/object.h 了解更多信息)。ob_type 成员指向 Python 类型对象。
char *data
可通过PyArray_DATA
访问,此数据成员是数组的第一个元素的指针。这个指针可以(通常应该)重塑为数组的数据类型。
int nd
一个整数,提供此数组的维度数量。当 nd 为 0 时,该数组有时被称为 0 阶数组。这样的数组具有未定义的维度和跨度,无法访问。宏 PyArray_NDIM
在 ndarraytypes.h
中定义,指向这个数据成员。NPY_MAXDIMS
是任何数组的最大维度数量。
*dimensions
一个整数数组,为每个维度提供每个维度的形状,只要 nd (\geq) 1。整数总是足够大以容纳平台上的指针,因此维度大小仅受内存限制。PyArray_DIMS
是与这个数据成员相关联的宏。
*strides
一个整数数组,为每个维度提供跳过的字节数,以便在该维度中到达下一个元素。与宏 PyArray_STRIDES
相关联。
*base
由 PyArray_BASE
指向,这个成员用于保存与该数组相关的另一个 Python 对象的指针。有两种用例:
NPY_ARRAY_WRITEBACKIFCOPY
标志,那么这个数组是一个“行为异常”的数组的工作副本。
当调用 PyArray_ResolveWritebackIfCopy
时,将使用该数组的内容更新由 base 指向的数组。
*descr
一个指向数据类型描述对象的指针(参见下文)。数据类型描述对象是一个新的内置类型的实例,它允许对内存进行通用描述。每种支持的数据类型都有一个描述结构体。这个描述结构体包含有关该类型的有用信息,以及指向实现特定功能的函数指针表的指针。顾名思义,它与宏 PyArray_DESCR
相关联。
int flags
由宏 PyArray_FLAGS
指向,这个数据成员表示数据指向的内存应该如何解释的标志。可能的标志有 NPY_ARRAY_C_CONTIGUOUS
、NPY_ARRAY_F_CONTIGUOUS
、NPY_ARRAY_OWNDATA
、NPY_ARRAY_ALIGNED
、NPY_ARRAY_WRITEABLE
、NPY_ARRAY_WRITEBACKIFCOPY
。
*weakreflist
这个成员允许数组对象具有弱引用(使用 weakref 模块)。
注意
进一步的成员被视为私有和版本相关。如果结构的大小对您的代码很重要,必须特别小心。当这是相关的可能使用案例是在 C 中进行子类化。如果您的代码依赖于sizeof(PyArrayObject)
是恒定的,您必须在导入时添加以下检查:
if (sizeof(PyArrayObject) < PyArray_Type.tp_basicsize) {
PyErr_SetString(PyExc_ImportError,
"Binary incompatibility with NumPy, must recompile/update X.");
return NULL;
}
要确保您的代码不必针对特定的 NumPy 版本进行编译,可以添加一个常量,为 NumPy 中的变化留出空间。 确保兼容任何未来的 NumPy 版本的解决方案需要运行时计算偏移量和分配大小。
PyArrayDescr_Type
PyArrayDescr_Type
是用于描述组成数组的字节该如何解释的数据类型描述符对象的内置类型。有 21 个静态定义的PyArray_Descr
对象用于内置数据类型。尽管这些参与引用计数,但它们的引用计数永远不应该为零。还维护了一个动态的用户定义的PyArray_Descr
对象表。一旦“注册”了数据类型描述符对象,就不应该将其释放。函数PyArray_DescrFromType
(…)可以用于从枚举类型号(内置或用户定义的)中检索PyArray_Descr
对象。
type PyArray_Descr
PyArray_Descr
结构位于PyArrayDescr_Type
的核心。虽然此处为完整起见进行了描述,但应将其视为 NumPy 的内部结构,并通过PyArrayDescr_*
或PyDataType*
函数和宏来操作。此结构的大小可能会随 NumPy 的不同版本而发生变化。为确保兼容性:
sizeof(PyArray_Descr)
它有以下结构:
typedef struct {
PyObject_HEAD
PyTypeObject *typeobj;
char kind;
char type;
char byteorder;
char flags;
int type_num;
int elsize;
int alignment;
PyArray_ArrayDescr *subarray;
PyObject *fields;
PyObject *names;
PyArray_ArrFuncs *f;
PyObject *metadata;
NpyAuxData *c_metadata;
npy_hash_t hash;
} PyArray_Descr;
*typeobj
指向对应于此数组元素的 Python 类型的类型对象的指针。 对于内置类型,这指向对应的数组标量。 对于用户定义的类型,这应指向用户定义的类型对象。 这个类型对象可以继承自数组标量,也可以不继承。 如果它不继承自数组标量,那么flags
成员中应该设置NPY_USE_GETITEM
和NPY_USE_SETITEM
标志。
char kind
表示数组种类的字符代码(使用数组接口类型字符串表示法)。 'b’代表布尔值,'i’代表有符号整数,'u’代表无符号整数,'f’代表浮点数,'c’代表复数浮点数,'S’代表 8 位零终止字节,'U’代表 32 位/字符的 unicode 字符串,'V’代表任意长度。
char type
传统的字符代码,表示数据类型。
char byteorder
一个表示字节顺序的字符:‘>’(大端),‘<’(小端),‘=’(本机),‘|’(无关,忽略)。所有内置数据类型都具有字节顺序‘=’。
char flags
一个确定数据类型是否展示类似对象数组的行为的数据类型位标志。这个成员中的每个位是一个标志,它们被命名为:
NPY_ITEM_REFCOUNT
NPY_ITEM_HASOBJECT
NPY_LIST_PICKLE
NPY_ITEM_IS_POINTER
NPY_NEEDS_INIT
NPY_NEEDS_PYAPI
NPY_USE_GETITEM
NPY_USE_SETITEM
NPY_FROM_FIELDS
NPY_OBJECT_DTYPE_FLAGS
int type_num
一个唯一标识数据类型的数字。对于新数据类型,当数据类型被注册时,分配这个数字。
int elsize
对于始终相同大小的数据类型(例如 long),这个成员保存数据类型的大小。对于灵活的数据类型,不同的数组可能具有不同的元素大小,这个值应该为 0。
int alignment
提供这个数据类型的对齐信息的数字。具体来说,它显示编译器在一个 2 元结构的开始处(其第一个元素是一个char
)放置该类型的项的距离:offsetof(struct {char c; type v;}, v)
*subarray
如果这是非NULL
,那么这个数据类型描述符是另一个数据类型描述符的 C 风格连续数组。换句话说,这个描述符描述的每个元素实际上是另一个基本描述符的数组。这对于作为另一个数据类型描述符中字段的数据类型描述符最有用。如果这个非NULL
,则 fields 成员应该为NULL
(但是 base 描述符的 fields 成员可以为非NULL
)。
type PyArray_ArrayDescr
typedef struct {
PyArray_Descr *base;
PyObject *shape;
} PyArray_ArrayDescr;
*base
基本类型的数据类型描述符对象。
*shape
子数组的形状(始终是 C 风格连续)作为 Python 元组。
*fields
如果这不是 NULL,则这个数据类型描述符具有由 Python 字典描述的字段,其键是名称(如果给定也是标题),其值是描述字段的元组。请记住,数据类型描述符总是描述一组固定长度的字节。字段是该总体固定长度集合的命名子区域。字段由另一个数据类型描述符和一个字节偏移的元组描述。可选地,元组可以包含通常是 Python 字符串的标题。这些元组被放在此字典中,以名称(如果给定也是标题)为键。
*names
字段名称的有序元组。如果未定义字段,则为 NULL。
*f
指向包含类型需要实现内部功能的函数的结构的指针。这些函数与后面描述的通用函数(ufuncs)不同。它们的签名可以任意变化。
*metadata
关于这个数据类型的元数据。
*c_metadata
特定于特定 dtype 的 C 实现的元数据。添加到 NumPy 1.7.0 中。
type npy_hash_t
*hash
当前未使用。保留用于将来缓存哈希值。
NPY_ITEM_REFCOUNT
表示这种数据类型的项必须进行引用计数(使用Py_INCREF
和Py_DECREF
)。
NPY_ITEM_HASOBJECT
与NPY_ITEM_REFCOUNT
相同。
NPY_LIST_PICKLE
表示必须在将数组转储为列表之前将这种数据类型的数组转换为列表。
NPY_ITEM_IS_POINTER
表示该项是指向其他数据类型的指针
NPY_NEEDS_INIT
表示必须初始化此数据类型的内存(设置为 0)。
NPY_NEEDS_PYAPI
表明此数据类型在访问时需要 Python C-API(因此,在需要数组访问时不要放弃 GIL)。
NPY_USE_GETITEM
在数组访问时,使用f->getitem
函数指针而不是标准的转换为数组标量。如果不定义与数据类型一起使用的数组标量,必须使用。
NPY_USE_SETITEM
从数组标量创建一个 0-d 数组时,请使用f->setitem
而不是标准的从数组标量复制。如果不定义与数据类型一起使用的数组标量,必须使用。
NPY_FROM_FIELDS
如果数据类型的任何字段中设置了这些位,则从父数据类型继承的位。目前(NPY_NEEDS_INIT
| NPY_LIST_PICKLE
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_PYAPI
)。
NPY_OBJECT_DTYPE_FLAGS
对象数据类型的设置位:(NPY_LIST_PICKLE
| NPY_USE_GETITEM
| NPY_ITEM_IS_POINTER
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_INIT
| NPY_NEEDS_PYAPI
)。
int PyDataType_FLAGCHK( *dtype, int flags)
如果对于数据类型对象设置了所有给定的标志,则返回 true。
int PyDataType_REFCHK( *dtype)
等效于PyDataType_FLAGCHK
(dtype,NPY_ITEM_REFCOUNT
)。
type PyArray_ArrFuncs
实现内部功能的函数。并非所有这些函数指针必须对给定类型进行定义。所需成员是nonzero
、copyswap
、copyswapn
、setitem
、getitem
和cast
。假定这些都是非NULL
的,而NULL
条目将导致程序崩溃。其他函数可能是NULL
,这意味着该数据类型的功能将减少。 (同时,如果在注册用户定义的数据类型时NULL
,则nonzero
函数将填充为默认函数)。
typedef struct {
PyArray_VectorUnaryFunc *cast[NPY_NTYPES];
PyArray_GetItemFunc *getitem;
PyArray_SetItemFunc *setitem;
PyArray_CopySwapNFunc *copyswapn;
PyArray_CopySwapFunc *copyswap;
PyArray_CompareFunc *compare;
PyArray_ArgFunc *argmax;
PyArray_DotFunc *dotfunc;
PyArray_ScanFunc *scanfunc;
PyArray_FromStrFunc *fromstr;
PyArray_NonzeroFunc *nonzero;
PyArray_FillFunc *fill;
PyArray_FillWithScalarFunc *fillwithscalar;
PyArray_SortFunc *sort[NPY_NSORTS];
PyArray_ArgSortFunc *argsort[NPY_NSORTS];
PyObject *castdict;
PyArray_ScalarKindFunc *scalarkind;
int **cancastscalarkindto;
int *cancastto;
PyArray_FastClipFunc *fastclip; /* deprecated */
PyArray_FastPutmaskFunc *fastputmask; /* deprecated */
PyArray_FastTakeFunc *fasttake; /* deprecated */
PyArray_ArgFunc *argmin;
} PyArray_ArrFuncs;
"表现正常"的概念用于描述函数指针。一个表现正常的段是指对齐且符合数据类型本机字节顺序的段。nonzero
、copyswap
、copyswapn
、getitem
和setitem
函数可以(而且必须)处理不正常的数组。其他函数要求内存段表现正常。
void cast(void *from, void *to, n, void *fromarr, void *toarr)
一串函数指针,用于将当前类型向所有其他内置类型转换。每个函数将一个由from指向的连续、对齐、未交换的缓冲区转换为一个由to指向的连续、对齐和未交换的缓冲区。要转换的项数由n给出,参数fromarr和toarr被解释为灵活数组的 PyArrayObjects 以获取 itemsize 信息。
*getitem(void *data, void *arr)
一个指向从由data指向的数组对象arr的单个元素返回标准 Python 对象的函数的指针。这个函数必须能够正确处理“不正常”(不对齐和/或交换)的数组。
int setitem( *item, void *data, void *arr)
一个指向将 Python 对象item设置到由data指向的数组arr中的函数的指针。这个函数处理“不正常的”数组。如果成功,返回值为零,否则返回一个负数(并设置一个 Python 错误)。
void copyswapn(void *dest, dstride, void *src, sstride, n, int swap, void *arr)
void copyswap(void *dest, void *src, int swap, void *arr)
这些成员都是指向从src复制数据到dest并在需要时进行swap的函数的指针。arr 的值仅用于灵活(NPY_STRING
,NPY_UNICODE
和NPY_VOID
)数组(并且从arr->descr->elsize
中获取)。第二个函数复制单个值,而第一个函数使用提供的步幅循环 n 次。这些函数可以处理不正常的src数据。如果src为 NULL,则不执行复制。如果swap为 0,则不进行字节交换。假定dest和src不重叠。如果它们重叠,则首先使用memmove
(…),然后使用带有空值src
的copyswap(n)
。
int compare(const void *d1, const void *d2, void *arr)
一个指向比较数组arr
中由d1
和d2
指向的两个元素的函数的指针。这个函数要求数组表现正常(对齐并且没有交换)。如果* d1
> * d2
,返回值为 1;如果* d1
== * d2
,返回值为 0;如果* d1
< * d2
,返回值为-1。数组对象arr
用于获取灵活数组的 itemsize 和字段信息。
int argmax(void *data, n, *max_ind, void *arr)
一个指向从由data
指向的元素开始的arr
中n
个元素中最大值的索引的函数的指针。这个函数要求内存段是连续的并且表现正常。返回值总是 0。最大元素的索引存储在max_ind
中。
void dotfunc(void *ip1, is1, void *ip2, is2, void *op, n, void *arr)
指向一个函数的指针,用于将两个长度为n
的序列相乘,并将它们相加,然后将结果放在arr
的op
指向的元素中。两个序列的起始位置分别由ip1
和ip2
指向。要达到每个序列的下一个元素,需要分别跳过is1
和is2
字节。此函数需要被认为是行为良好的(尽管不一定连续)内存。
int scanfunc(FILE *fd, void *ip, void *arr)
指向一个函数的指针,用于从文件描述符fd
中以(scanf 风格)扫描相应类型的元素到ip
指向的数组内存中。假定该数组是被认为是行为良好的。最后一个参数arr
是要扫描到的数组。返回成功分配的接收参数的数量(如果在分配第一个接收参数之前发生匹配失败,这个数量可能为零),或者在分配第一个接收参数之前发生输入失败时返回 EOF。在报告错误时应该在不持有 Python GIL 的情况下调用此函数,并且会为错误报告而调用 GIL。
int fromstr(char *str, void *ip, char **endptr, void *arr)
指向一个函数的指针,用于将由str
指向的字符串转换为相应类型的一个元素,并将其放入ip
指向的内存位置。转换完成后,*endptr
指向字符串的其余部分。最后一个参数arr
是 ip 指向的数组(对于可变大小数据类型是必需的)。成功返回 0,失败返回-1。需要一个被认为是行为良好的数组。在报告错误时应该在不持有 Python GIL 的情况下调用此函数,并且会为错误报告而调用 GIL。
nonzero(void *data, void *arr)
指向一个函数的指针,如果data
指向的arr
项是非零则返回 TRUE。此函数可以处理行为不良的数组。
void fill(void *data, length, void *arr)
指向一个函数的指针,用于用数据填充给定长度的连续数组。数组的前两个元素必须已经填充了值。从这两个值中,将计算出一个增量,并且从第 3 个到最后一个元素将会反复地加上这个计算出的增量。数据缓冲区必须是行为良好的。
void fillwithscalar(void *buffer, length, void *value, void *arr)
指向一个函数的指针,用于使用给定的单一标量值向长度为给定length
的连续buffer
填充数据。最后一个参数是所需的用于变长数组获取 itemsize 的数组。
int sort(void *start, length, void *arr)
一个指向特定排序算法的函数指针数组。使用键(到目前为止已经定义了NPY_QUICKSORT
,NPY_HEAPSORT
,和NPY_MERGESORT
)可以获取特定的排序算法。这些排序是就地进行的,假定数据是连续的和对齐的。
int argsort(void *start, *result, length, void *arr)
一个指向用于此数据类型的排序算法的函数指针数组。与 sort 相同的排序算法可用。产生排序的索引将会返回在result
中(必须初始化为包含 0 到length-1
的索引)。
*castdict
要么为NULL
,要么是包含用户定义数据类型的低级转换函数的字典。每个函数都被包装在一个PyCapsule*中,并由数据类型编号键入。
scalarkind( *arr)
用于确定应如何解释此类型标量的函数。参数为NULL
或包含数据的 0 维数组(如果需要确定标量类型)。返回值必须是NPY_SCALARKIND
类型。
int **cancastscalarkindto
要么为NULL
,要么为NPY_SCALARKINDS
指针数组。这些指针应该是NULL
,或者指向整数数组(以NPY_NOTYPE
结尾),表示此数据类型的标量可以安全转换为指定类型的数据类型(通常表示不会失去精度)。
int *cancastto
要么为NULL
,要么为整数数组(以NPY_NOTYPE
结尾),表示此数据类型可以安全转换为的数据类型(通常表示不会失去精度)。
void fastclip(void *in, n_in, void *min, void *max, void *out)
自版本 1.17 起弃用:使用此函数将在np.clip
时产生弃用警告。该数据类型必须使用PyUFunc_RegisterLoopForDescr
来将自定义循环附加到np.core.umath.clip
,np.minimum
和np.maximum
。
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
一个函数,从in
中读取n_in
个项目,并将读取的值写入out
,如果在min
和max
指向的限制范围内,则在外部使用对应的限制。内存段必须是连续的且规范化的,并且min
或max
可能为NULL
,但不能同时为NULL
。
void fastputmask(void *in, void *mask, n_in, void *values, nv)
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
一个函数,接受指向n_in
个项目数组的指针in
,指向n_in
个布尔值的数组的指针mask
,以及指向nv
个项目的数组的指针vals
。无需失去精度地将vals
中的项复制到in
中,只需在mask
中的值非零时,根据需要平铺vals
,如果nv < n_in
。所有数组都必须是连续的且规范化的。
void fasttake(void *dest, void *src, *indarray, nindarray, n_outer, m_middle, nelem, clipmode)
自版本 1.19 起弃用:设置此函数已被弃用,应始终为NULL
,如果设置,将被忽略。
一个函数,它接受一个指向 C 连续、行为良好的段src
,解释为形状为(n_outer, nindarray, nelem)
的三维数组的指针,一个指向m_middle
整数索引的连续的、行为良好的段indarray
的指针,以及一个指向 C 连续、行为良好的段的指针dest
,解释为形状为(n_outer, m_middle, nelem)
的三维数组。indarray
中的索引用于沿着第二维对src
进行索引,并将相应的nelem
项的块复制到dest
中。clipmode
(可以取值NPY_RAISE
、NPY_WRAP
或NPY_CLIP
一个指针,指向一个函数,用于检索`data`指向的开始于`arr`的`n`个元素中最小元素的索引。此函数要求内存段是连续的且行为良好。返回值始终为 0。最小元素的索引存储在`min_ind`中。
`PyArray_Type` 类型对象实现了许多[`Python 对象`](https://docs.python.org/3/c-api/type.html#c.PyTypeObject "(in Python v3.11)")的特性,包括[`tp_as_number`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_number "(in Python v3.11)")、[`tp_as_sequence`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_sequence "(in Python v3.11)")、[`tp_as_mapping`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_mapping "(in Python v3.11)")和[`tp_as_buffer`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_buffer "(in Python v3.11)")接口。还使用了[`rich comparison`](https://docs.python.org/3/c-api/typeobj.html#c.richcmpfunc "(in Python v3.11)"),以及新式属性查找用于成员([`tp_members`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_members "(in Python v3.11)"))和属性([`tp_getset`](https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_getset "(in Python v3.11)"))。`PyArray_Type` 也可以被子类型化。
提示
`tp_as_number`方法使用一种通用方法来调用已注册用于处理操作的任何函数。导入`_multiarray_umath`模块时,它将为所有数组设置相应的 ufunc 数值操作。这个选择可以使用`PyUFunc_ReplaceLoopBySignature`进行更改。还可以使用`PyArray_SetStringFunction`更改`tp_str`和`tp_repr`方法。
### PyUFunc_Type 和 PyUFuncObject
```py
PyUFunc_Type
ufunc 对象是通过创建PyUFunc_Type
来实现的。它是一个非常简单的类型,仅实现了基本的 getattribute 行为、打印行为,并具有调用行为,使这些对象可以像函数一样工作。ufunc 背后的基本思想是存储每种支持操作的数据类型的快速一维(向量)循环的引用. 所有这些一维循环都有相同的标识并且是创建新 ufunc 的关键。它们由通用循环代码在适当时调用以实现 N 维功能。还为浮点和复数浮点数组定义了一些通用的 1-d 循环,使您可以使用单个标量函数(例如atanh)来定义一个 ufunc。
type PyUFuncObject
ufunc 的核心是PyUFuncObject
,它包含调用执行实际工作的底层 C 代码循环所需的所有信息。虽然这里对其进行了描述以便完整,但应该将其视为 NumPy 的内部部分,并通过PyUFunc_*
函数进行操作。该结构的大小可能会随着 NumPy 版本的更改而改变。为确保兼容性:
sizeof(PyUFuncObject)
它具有以下结构:
typedef struct {
PyObject_HEAD
int nin;
int nout;
int nargs;
int identity;
PyUFuncGenericFunction *functions;
void **data;
int ntypes;
int reserved1;
const char *name;
char *types;
const char *doc;
void *ptr;
PyObject *obj;
PyObject *userloops;
int core_enabled;
int core_num_dim_ix;
int *core_num_dims;
int *core_dim_ixs;
int *core_offsets;
char *core_signature;
PyUFunc_TypeResolutionFunc *type_resolver;
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector;
void *reserved2;
npy_uint32 *op_flags;
npy_uint32 *iter_flags;
/* new in API version 0x0000000D */
npy_intp *core_dim_sizes;
npy_uint32 *core_dim_flags;
PyObject *identity_value;
/* Further private slots (size depends on the NumPy version) */
} PyUFuncObject;
int nin
输入参数的数量。
int nout
输出参数的数量。
int nargs
参数的总数(nin + nout)。这个必须小于NPY_MAXARGS
。
int identity
使用PyUFunc_One
、PyUFunc_Zero
、PyUFunc_MinusOne
、PyUFunc_None
、PyUFunc_ReorderableNone
或PyUFunc_IdentityValue
来指示此操作的标识。它仅在对空数组进行类似缩减的调用时使用。
void functions(char **args, *dims, *steps, void *extradata)
一个函数指针数组,每一种数据类型都有一个。这个向量循环用于实现底层函数dims[0]次。第一个参数args是一个具有nargs指针的数组,指向行为良好的内存。首先是指向输入参数数据的指针,然后是指向输出参数数据的指针。必须跳过多少字节才能到达序列中的下一个元素由steps数组中的相应条目指定。最后一个参数允许循环接收额外的信息。这通常是为了使单个通用向量循环用于多个函数。在这种情况下,要调用的实际标量函数以extradata的形式传递。此函数指针数组的大小为 ntypes。
void **data
传递给 1 维向量循环的额外数据,如果不需要额外数据,则为NULL
。这个 C 数组的大小必须与函数数组相同(即 ntypes)。如果不需要 extra_data,则使用NULL
。一些用于 UFuncs 的 C-API 调用只是 1 维向量循环,利用这些额外数据来接收要调用的实际函数的指针。
int ntypes
Ufunc 支持的数据类型数。此数字指定有多少不同的 1 维循环(内置数据类型)可用。
int reserved1
未使用。
char *name
Ufunc 的字符串名称。这个动态用于构建 ufunc 的 doc 属性。
char *types
一个(nargs \times ntypes)的 8 位 type_numbers 数组,其中包含每个支持的(内置)数据类型的函数的类型签名。对于ntypes个函数中的每一个,该数组中相应的一组类型编号显示了如何在 1 维向量循环中解释args参数。这些类型编号不必是相同的类型,支持混合类型的 ufunc。
char *doc
UFunc 的文档。不应包含函数签名,因为这在检索 doc 时动态生成。
void *ptr
任何动态分配的内存。目前,这用于从 python 函数创建的动态 ufunc 中存储类型、数据和名称成员的空间。
*obj
对于从 python 函数动态创建的 ufunc,此成员保存对底层 Python 函数的引用。
*userloops
用户定义的 1 维向量循环的字典(存储为 CObject ptrs),用于用户定义的类型。用户可以为任何用户定义的类型注册循环。它通过类型编号检索。用户定义的类型编号始终大于NPY_USERDEF
。
int core_enabled
标量 ufunc 的为 0;广义 ufunc 的为 1。
int core_num_dim_ix
签名中的不同核心维度名称数
int *core_num_dims
每个参数的核心维度数
int *core_dim_ixs
展平形式的维度索引;参数k
的索引存储在core_dim_ixs[core_offsets[k] : core_offsets[k] + core_numdims[k]]
int *core_offsets
每个参数在core_dim_ixs
中的第一个核心维度的位置,相当于 cumsum(core_num_dims
)
char *core_signature
核心签名字符串
PyUFunc_TypeResolutionFunc *type_resolver
解析类型并填充一个数组以获取输入和输出的 dtype 的函数
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector
自版本 1.22 起弃用:该插槽的一些回退支持存在,但最终将被移除。依赖于此的通用函数最终将必须进行移植。参见NEP 41和NEP 43
void *reserved2
对于可能的具有不同签名的未来循环选择器。
op_flags
覆盖每个 ufunc 操作数的默认操作数标志。
iter_flags
覆盖 ufunc 的默认 nditer 标志。
在 API 版本 0x0000000D 中添加
*core_dim_sizes
对于每个不同的核心维度,如果UFUNC_CORE_DIM_SIZE_INFERRED
为0
,则可能为冻结大小
*core_dim_flags
对于每个不同的核心维度,一组标志(UFUNC_CORE_DIM_CAN_IGNORE
和 UFUNC_CORE_DIM_SIZE_INFERRED
)
*identity_value
缩减的身份,当PyUFuncObject.identity
等于PyUFunc_IdentityValue
时。
UFUNC_CORE_DIM_CAN_IGNORE
如果维度名称以?
结尾
UFUNC_CORE_DIM_SIZE_INFERRED
如果维度大小将根据操作数确定而不是来自冻结签名
PyArrayIter_Type
这是一个迭代器对象,可以轻松地循环遍历 N 维数组。它是从 ndarray 的 flat 属性返回的对象。它还广泛地用于实现内部,以便循环遍历 N 维数组。实现了 tp_as_mapping 接口,以便可以索引迭代器对象(使用 1-d 索引),并且通过 tp_methods 表实现了一些方法。这个对象实现了 next 方法,并且可以在 Python 中的任何地方使用迭代器。
type PyArrayIterObject
与PyArrayIter_Type
对象对应的 C 结构是PyArrayIterObject
。 PyArrayIterObject
用于跟踪 N 维数组中的指针。它包含用于快速遍历数组的相关信息。指针可以通过三种基本方式进行调整:1)以 C 样式的连续方式前进到数组中的“下一个”位置,2)前进到数组中的任意 N 维坐标,3)前进到数组中的任意一维索引。PyArrayIterObject
结构的成员用于进行这些计算。迭代器对象保留有关数组的自己的维度和跨度信息。这可根据需要进行调整,以进行“广播”,或仅在特定维度上循环。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index;
npy_intp size;
npy_intp coordinates[NPY_MAXDIMS];
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
} PyArrayIterObject;
int nd_m1
(N-1),其中(N)是底层数组中的维数。
index
到数组的当前一维索引。
size
底层数组的总大小。
*coordinates
到数组的(N) -维索引。
*dims_m1
每个维度中数组大小减 1。
*strides
数组的跨度。在每个维度中跳到下一个元素需要多少字节。
*backstrides
从维度末尾跳回到开头需要多少字节。注意backstrides[k] == strides[k] * dims_m1[k]
,但它被存储在这里作为一种优化。
*factors
该数组用于从一维索引计算 N 维索引。它包含所需的维度的乘积。
*ao
指向此迭代器被创建以代表的基础 ndarray 的指针。
char *dataptr
此成员指向由索引指示的 ndarray 中的一个元素。
contiguous
如果底层数组是NPY_ARRAY_C_CONTIGUOUS
,则此标志为 true。它用于在可能的情况下简化计算。
如何在 C 级别上使用数组迭代器在后续章节中有更详细的解释。通常情况下,您不需要关心迭代器对象的内部结构,只需通过宏PyArray_ITER_NEXT
(it)、PyArray_ITER_GOTO
(it, dest)或者PyArray_ITER_GOTO1D
(it, index)来与之交互即可,所有这些宏要求参数it是 PyArrayIterObject*。
PyArrayMultiIter_Type
这种类型提供了一种封装广播概念的迭代器。它允许将(N)个数组一起广播,使得循环按照广播数组的 C 风格连续方式进行。相应的 C 结构是PyArrayMultiIterObject
,其内存布局必须从任何传递给PyArray_Broadcast
(obj)函数的对象obj开始。通过调整数组迭代器执行广播,使得每个迭代器表示广播的形状和大小,但其步幅调整为每次迭代中使用数组的正确元素。
type PyArrayMultiIterObject
typedef struct {
PyObject_HEAD
int numiter;
npy_intp size;
npy_intp index;
int nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject *iters[NPY_MAXDIMS];
} PyArrayMultiIterObject;
int numiter
需要广播到相同形状的数组的数量。
size
广播的总大小。
index
广播结果的当前(1-D)索引。
int nd
广播结果中的维数。
*dimensions
广播结果的形状(仅使用nd
个插槽)。
**iters
一个迭代器对象数组,其中包含要合并广播的数组的迭代器。返回时,这些迭代器已调整为进行广播。
PyArrayNeighborhoodIter_Type
这是一个迭代器对象,它可以轻松地在 N 维邻域上循环。
type PyArrayNeighborhoodIterObject
与PyArrayNeighborhoodIter_Type
对象对应的 C 结构是PyArrayNeighborhoodIterObject
。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index, size;
npy_intp coordinates[NPY_MAXDIMS]
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
npy_intp bounds[NPY_MAXDIMS][2];
npy_intp limits[NPY_MAXDIMS][2];
npy_intp limits_sizes[NPY_MAXDIMS];
npy_iter_get_dataptr_t translate;
npy_intp nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject* _internal_iter;
char* constant;
int mode;
} PyArrayNeighborhoodIterObject;
PyArrayFlags_Type
当从 Python 检索 flags 属性时,会构造一个特殊的内置对象的此类型。这种特殊的类型使得通过访问它们作为属性或者假定对象是一个以标志名称为条目的字典而访问它们来更容易地处理不同的标志。
type PyArrayFlagsObject
typedef struct PyArrayFlagsObject {
PyObject_HEAD
PyObject *arr;
int flags;
} PyArrayFlagsObject;
数组中可能出现的不同内置数据类型都有对应的 Python 类型。其中大多数类型只是对应的 C 语言数据类型的简单封装。这些类型在 C 语言中的名称为 Py{TYPE}ArrType_Type
,其中 {TYPE}
可以是
Bool、Byte、Short、Int、Long、LongLong、UByte、UShort、UInt、ULong、ULongLong、Half、Float、Double、LongDouble、CFloat、CDouble、CLongDouble、String、Unicode、Void 和 Object。
这些类型名称属于 C-API 的一部分,因此可以在扩展 C 代码中创建。还有一个 PyIntpArrType_Type
和 PyUIntpArrType_Type
,它们是平台上可以保存指针的整数类型的简单替代品。这些标量对象的结构对 C 代码不可见。函数 PyArray_ScalarAsCtype
(…) 可以用于从数组标量中提取 C 类型值,而函数 PyArray_Scalar
(…) 可以用于从 C 值构造一个数组标量。
PyArray_Type
和 PyArrayObject
PyArray_Type
ndarray 的 Python 类型是PyArray_Type
。在 C 中,每个 ndarray 都是一个指向 PyArrayObject
结构的指针。这个结构的 ob_type 成员包含一个指向 PyArray_Type
类型对象的指针。
type PyArrayObject
type NPY_AO
PyArrayObject
C 结构包含数组的所有必要信息。所有 ndarray(及其子类)的实例都具有这一结构。为了未来的兼容性,应该使用提供的宏来访问这些结构成员。如果需要更短的名称,那么可以使用 NPY_AO
(已弃用),它被定义为等同于 PyArrayObject
。直接访问结构字段已被弃用。请改用 PyArray_*(arr)
形式。从 NumPy 1.20 开始,此结构的大小不被认为是 NumPy ABI 的一部分(请参见成员列表末尾的注释)。
typedef struct PyArrayObject {
PyObject_HEAD
char *data;
int nd;
npy_intp *dimensions;
npy_intp *strides;
PyObject *base;
PyArray_Descr *descr;
int flags;
PyObject *weakreflist;
/* version dependent private members */
} PyArrayObject;
PyObject_HEAD
这适用于所有 Python 对象。它至少包含一个引用计数成员( ob_refcnt
)和一个类型对象指针( ob_type
)。(如果使用特殊选项编译 Python,可能还有其他成员,请参见 Python 源代码树中的 Include/object.h 获取更多信息。)ob_type 成员指向一个 Python 类型对象。
char *data
通过 PyArray_DATA
可以访问到这个数据成员,它是数组的第一个元素的指针。这个指针可以(通常应该)被重新解释为数组的数据类型。
int nd
为此数组提供维数的整数。当 nd 为 0 时,有时称该数组为秩为 0 的数组。这样的数组具有未定义的维度和步幅,并且无法访问。宏 PyArray_NDIM
在 ndarraytypes.h
中定义,指向此数据成员。NPY_MAXDIMS
是任何数组的最大维数。
*dimensions
一个提供每个维度的形状的整数数组,只要 nd (\geq) 1. 该整数始终足够大,以容纳平台上的指针,因此维度大小仅受内存限制。PyArray_DIMS
是与此数据成员关联的宏。
*strides
为每个维度提供一个整数数组,指定必须跳过多少字节才能到达该维度中的下一个元素。与宏PyArray_STRIDES
相关。
*base
由PyArray_BASE
指向,此成员用于保存指向与此数组相关的另一个 Python 对象的指针。有两种用法:
NPY_ARRAY_WRITEBACKIFCOPY
标志,则此数组是“行为不端”数组的工作副本。
当调用 PyArray_ResolveWritebackIfCopy
时,base 指向的数组将使用此数组的内容进行更新。
*descr
指向数据类型描述符对象的指针(见下文)。数据类型描述符对象是一种新的内置类型的实例,允许对内存进行通用描述。对于支持的每种数据类型都有一个描述符结构。此描述符结构包含有关类型的有用信息以及实现特定功能的函数指针表的指针。正如名称所示,它与宏PyArray_DESCR
相关联。
int flags
由宏PyArray_FLAGS
指向的,这个数据成员表示标志,指示由 data 指向的内存应如何解释。可能的标志有NPY_ARRAY_C_CONTIGUOUS
、NPY_ARRAY_F_CONTIGUOUS
、NPY_ARRAY_OWNDATA
、NPY_ARRAY_ALIGNED
、NPY_ARRAY_WRITEABLE
、NPY_ARRAY_WRITEBACKIFCOPY
。
*weakreflist
此成员允许数组对象具有弱引用(使用 weakref 模块)。
注意
进一步的成员被视为私有和版本相关的。如果结构的大小对您的代码很重要,则必须特别小心。当这一点是相关的时候可能的用例是在 C 中进行子类化。如果您的代码依赖于sizeof(PyArrayObject)
是常量,您必须在导入时添加以下检查:
if (sizeof(PyArrayObject) < PyArray_Type.tp_basicsize) {
PyErr_SetString(PyExc_ImportError,
"Binary incompatibility with NumPy, must recompile/update X.");
return NULL;
}
为了确保您的代码不必为特定的 NumPy 版本编译,您可以添加一个常量,为 NumPy 的更改留出空间。一个确保与任何未来 NumPy 版本兼容的解决方案需要使用运行时计算偏移和分配大小。
PyArrayDescr_Type
PyArrayDescr_Type
是用于描述如何解释组成数组的字节的数据类型描述符对象的内置类型。有 21 个静态定义的PyArray_Descr
对象用于内置数据类型。虽然它们参与引用计数,但它们的引用计数永远不应该达到零。还有一个动态的用户定义的PyArray_Descr
对象表也在维护中。一旦数据类型描述符对象被“注册”,就不应该被释放。函数PyArray_DescrFromType
(…)可以用于从枚举类型编号(内置或用户定义的)检索PyArray_Descr
对象。
type PyArray_Descr
PyArray_Descr
结构位于PyArrayDescr_Type
的核心。虽然这里对其进行了描述以保持完整性,但应该将其视为 NumPy 的内部结构,并通过PyArrayDescr_*
或PyDataType*
函数和宏来操作。此结构的大小可能会随 NumPy 版本的变化而变化。为了确保兼容性:
sizeof(PyArray_Descr)
它具有以下结构:
typedef struct {
PyObject_HEAD
PyTypeObject *typeobj;
char kind;
char type;
char byteorder;
char flags;
int type_num;
int elsize;
int alignment;
PyArray_ArrayDescr *subarray;
PyObject *fields;
PyObject *names;
PyArray_ArrFuncs *f;
PyObject *metadata;
NpyAuxData *c_metadata;
npy_hash_t hash;
} PyArray_Descr;
*typeobj
指向与此数组元素对应的 Python 类型的类型对象的指针。对于内置类型,这指向相应的数组标量。对于用户定义的类型,这应该指向用户定义的类型对象。这个类型对象可以继承自数组标量,也可以不继承。如果它不继承自数组标量,则flags
成员中应设置NPY_USE_GETITEM
和NPY_USE_SETITEM
标志。
char kind
表示数组种类的字符代码(使用数组接口类型字符串表示法)。‘b’表示布尔,‘i’表示有符号整数,‘u’表示无符号整数,‘f’表示浮点数,‘c’表示复数浮点数,‘S’表示 8 位以零结尾的字节,‘U’表示 32 位/字符的 Unicode 字符串,‘V’表示任意。
char type
表示数据类型的传统字符代码。
char byteorder
表示字节顺序的字符:‘>’(大端)、‘<’(小端)、‘=’(本地)、‘|’(不相关,忽略)。所有内置数据类型的字节顺序都是‘=’。
char flags
一个数据类型位标志,确定数据类型是否表现出对象数组的行为。该成员中的每个位都是一个标志,命名为:
NPY_ITEM_REFCOUNT
NPY_ITEM_HASOBJECT
NPY_LIST_PICKLE
NPY_ITEM_IS_POINTER
NPY_NEEDS_INIT
NPY_NEEDS_PYAPI
NPY_USE_GETITEM
NPY_USE_SETITEM
NPY_FROM_FIELDS
NPY_OBJECT_DTYPE_FLAGS
int type_num
一个唯一标识数据类型的数字。对于新数据类型,当数据类型被注册时,会分配这个数字。
int elsize
对于始终大小相同的数据类型(例如 long),这保存数据类型的大小。对于灵活的数据类型,其中不同的数组可以具有不同的元素大小,这应为 0。
int alignment
为此数据类型提供对齐信息的数字。具体来说,它显示了编译器将该类型的项目放置在起始为 char
的 2 元结构中的距离:offsetof(struct {char c; type v;}, v)
*subarray
如果这是非 NULL
,那么这个数据类型描述符是另一个数据类型描述符的 C 风格连续数组。换句话说,这个描述符描述的每个元素实际上都是另一个基本描述符的数组。这在作为另一个数据类型描述符中的字段的数据类型描述符时最有用。如果这是非 NULL
,则 fields 成员应为 NULL
(但基本描述符的 fields 成员可以是非 NULL
)。
type PyArray_ArrayDescr
typedef struct {
PyArray_Descr *base;
PyObject *shape;
} PyArray_ArrayDescr;
*base
基础类型的数据类型描述符对象。
*shape
作为 Python 元组的子数组的形状(始终是 C 风格连续的)。
*fields
如果这是非 NULL,则此数据类型描述符具有由 Python 字典描述的字段,其键是名称(如果给定也是标题),其值是描述字段的元组。回想一下,数据类型描述符总是描述一组固定长度的字节。字段是该总体、固定长度的集合的命名子区域。字段由另一个数据类型描述符和字节偏移量组成的元组描述。可选地,元组可以包含一个标题,通常是 Python 字符串。这些元组被放置在此字典中,键为名称(如果给定也是标题)。
*names
一个有序的字段名称元组。如果未定义字段,则为 NULL。
*f
指向包含类型需要实现内部功能的函数的结构体指针。这些函数与后面描述的通用函数(ufuncs)不是同一种东西。它们的签名可以任意变化。
*metadata
关于此数据类型的元数据。
*c_metadata
特定于特定数据类型的 C 实现的元数据。添加于 NumPy 1.7.0。
type npy_hash_t
*hash
目前未使用。保留以缓存散列值的未来使用。
NPY_ITEM_REFCOUNT
表明此数据类型的项目必须进行引用计数(使用Py_INCREF
和Py_DECREF
)。
NPY_ITEM_HASOBJECT
与NPY_ITEM_REFCOUNT
相同。
NPY_LIST_PICKLE
表示此数据类型的数��必须在 pickling 之前转换为列表。
NPY_ITEM_IS_POINTER
表示项目是指向其他数据类型的指针。
NPY_NEEDS_INIT
表示此数据类型的内存在创建时必须初始化(设置为 0)。
NPY_NEEDS_PYAPI
表明此数据类型在访问时需要 Python C-API(因此如果需要数组访问,则不要放弃 GIL)。
NPY_USE_GETITEM
在数组访问中,使用f->getitem
函数指针,而不是标准的转换为数组标量。如果没有定义与数据类型相配套的数组标量,则必须使用。
NPY_USE_SETITEM
从数组标量创建 0 维数组时,请使用f->setitem
,而不是标准的从数组标量复制。如果没有定义与数据类型相匹配的数组标量,则必须使用。
NPY_FROM_FIELDS
如果这些位在数据类型的任何字段中设置,那么这些位就会继承自父数据类型。当前(NPY_NEEDS_INIT
| NPY_LIST_PICKLE
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_PYAPI
)。
NPY_OBJECT_DTYPE_FLAGS
为对象数据类型设置的位:(NPY_LIST_PICKLE
| NPY_USE_GETITEM
| NPY_ITEM_IS_POINTER
| NPY_ITEM_REFCOUNT
| NPY_NEEDS_INIT
| NPY_NEEDS_PYAPI
)。
int PyDataType_FLAGCHK( *dtype, int flags)
如果数据类型对象上设置了给定标志,则返回 true。
int PyDataType_REFCHK( *dtype)
等同于PyDataType_FLAGCHK
(dtype, NPY_ITEM_REFCOUNT
)。
type PyArray_ArrFuncs
实现内部功能的函数。并非所有这些函数指针都必须为给定类型定义。所需成员包括nonzero
、copyswap
、copyswapn
、setitem
、getitem
和cast
。假定这些为非NULL
,而NULL
条目将导致程序崩溃。其他功能可能为NULL
,这只会减少该数据类型的功能。(另外,如果在注册用户定义的数据类型时NULL
,则nonzero
函数将用默认函数填充)。
typedef struct {
PyArray_VectorUnaryFunc *cast[NPY_NTYPES];
PyArray_GetItemFunc *getitem;
PyArray_SetItemFunc *setitem;
PyArray_CopySwapNFunc *copyswapn;
PyArray_CopySwapFunc *copyswap;
PyArray_CompareFunc *compare;
PyArray_ArgFunc *argmax;
PyArray_DotFunc *dotfunc;
PyArray_ScanFunc *scanfunc;
PyArray_FromStrFunc *fromstr;
PyArray_NonzeroFunc *nonzero;
PyArray_FillFunc *fill;
PyArray_FillWithScalarFunc *fillwithscalar;
PyArray_SortFunc *sort[NPY_NSORTS];
PyArray_ArgSortFunc *argsort[NPY_NSORTS];
PyObject *castdict;
PyArray_ScalarKindFunc *scalarkind;
int **cancastscalarkindto;
int *cancastto;
PyArray_FastClipFunc *fastclip; /* deprecated */
PyArray_FastPutmaskFunc *fastputmask; /* deprecated */
PyArray_FastTakeFunc *fasttake; /* deprecated */
PyArray_ArgFunc *argmin;
} PyArray_ArrFuncs;
在描述函数指针时使用了行为良好的段的概念。 行为良好的段是指与数据类型的本机机器字节顺序对齐的段。 nonzero
、copyswap
、copyswapn
、getitem
和 setitem
函数可以(并且必须)处理不规范的数组。 其他函数需要行为良好的内存段。
void cast(void *from, void *to, n, void *fromarr, void *toarr)
用于将当前类型转换为所有其他内置类型的函数指针数组。 每个函数将由 from 指向的连续、对齐且未交换的缓冲区转换为由 to 指向的连续、对齐且未交换的缓冲区。 要转换的项目数由 n 给出,并且参数 fromarr 和 toarr 被解释为灵活数组的 PyArrayObjects 以获取 itemsize 信息。
*getitem(void *data, void *arr)
指向从由 data 指向的数组对象 arr 的单个元素返回标准 Python 对象的函数的指针。 该函数必须能够正确处理“不规范”(未对齐和/或已交换)的数组。
int setitem( *item, void *data, void *arr)
指向将 Python 对象 item 设置到数组 arr 中由 data 指向位置的函数的指针。 该函数处理“不规范”的数组。 如果成功,则返回零,否则返回负一(并设置 Python 错误)。
void copyswapn(void *dest, dstride, void *src, sstride, n, int swap, void *arr)
void copyswap(void *dest, void *src, int swap, void *arr)
这些成员都是指向从 src 复制数据到 dest 并在需要时交换的函数的指针。 arr 的值仅用于灵活(NPY_STRING
、NPY_UNICODE
和 NPY_VOID
)数组(并从 arr->descr->elsize
获取)。 第二个函数复制单个值,而第一个函数则使用提供的步幅循环 n 个值。 这些函数可以处理不规范的 src 数据。 如果 src 为 NULL,则不执行复制。 如果 swap 为 0,则不进行字节交换。 假定 dest 和 src 不重叠。 如果它们重叠,则首先使用 memmove
(…)后跟 NULL 值的 src
进行 copyswap(n)
。
int compare(const void *d1, const void *d2, void *arr)
指向比较数组 arr
中由 d1
和 d2
指向的两个元素的函数的指针。 此函数需要行为良好的(对齐且未交换)数组。 如果 * d1
> * d2
,则返回值为 1,如果 * d1
== * d2
,则返回值为 0,如果 * d1
< * d2
,则返回值为 -1。 用于灵活数组的数组对象 arr
用于检索 itemsize 和字段信息。
int argmax(void *data, n, *max_ind, void *arr)
指向从由 data 指向的数组中从指向的元素开始的 n
个元素中检索最大索引的函数的指针。 此函数要求内存段是连续且行为良好的。 返回值始终为 0。 最大元素的索引在 max_ind
中返回。
void dotfunc(void *ip1, is1, void *ip2, is2, void *op, n, void *arr)
指向函数的指针,将两个n
长度序列相乘,相加,并将结果放置在arr
的op
指向的元素中。两个序列的起始点由ip1
和ip2
指向。要进入每个序列的下一个元素,分别需要跳过is1
和is2
字节。此函数需要良好行为的内存(尽管不一定是连续的)。
int scanfunc(FILE *fd, void *ip, void *arr)
指向函数的指针,从文件描述符fd
中扫描(类似于 scanf)相应类型的一个元素到由ip
指向的数组内存中。假定数组良好行为。最后一个参数arr
是要扫描到的数组。返回成功分配的接收参数的数量(如果在分配第一个接收参数之前发生匹配失败,则可能为零),或者如果在分配第一个接收参数之前发生输入失败则返回 EOF。此函数应在不持有 Python GIL 的情况下调用,并且在报告错误时必须抓取它。
int fromstr(char *str, void *ip, char **endptr, void *arr)
指向函数的指针,将由str
指向的字符串转换为相应类型的一个元素,并将其放置在由ip
指向的内存位置。转换完成后,*endptr
指向字符串的剩余部分。最后一个参数arr
是ip
指向的数组(对于可变大小数据类型需要)。成功返回 0,失败返回-1。需要一个良好行为的数组。此函数应在不持有 Python GIL 的情况下调用,并且在报告错误时必须抓取它。
nonzero(void *data, void *arr)
指向函数的指针,如果data
指向的arr
的项非零,则返回 TRUE。该函数可处理不良行为的数组。
void fill(void *data, length, void *arr)
指向函数的指针,用于将给定长度的连续数组填充到数据中。数组的前两个元素必须已经填充。从这两个值中,将计算一个增量,然后通过重复添加此计算出的增量来计算从第 3 个项目到末尾的值。数据缓冲区必须良好行为。
void fillwithscalar(void *buffer, length, void *value, void *arr)
指向函数的指针,用给定的value
填充给定length
的连续buffer
。最后一个参数是需要获取变长数组的项大小。
int sort(void *start, length, void *arr)
一组函数指针的数组,指向特定的排序算法。使用键(到目前为止已定义NPY_QUICKSORT
、NPY_HEAPSORT
和NPY_MERGESORT
)获得特定的排序算法。这些排序是就地完成的,假设数据是连续且对齐的。
int argsort(void *start, *result, length, void *arr)
一组函数指针的数组,用于这种数据类型的排序算法。与 sort 相同的排序算法可用。产生排序的索引将返回到result
(必须初始化为 0 到length-1
的索引)。
*castdict
可以为NULL
,也可以是包含用户定义数据类型的低级转换函数的字典。每个函数都包装在PyCapsule*中,并以数据类型编号为键。
scalarkind( *arr)
一个确定此类型标量应该如何解释的函数。该参数为NULL
或包含数据的零维数组(如果需要确定标量的类型)。返回值必须为类型NPY_SCALARKIND
。
int **cancastscalarkindto
可以为NULL
,也可以是NPY_NSCALARKINDS
指针数组。这些指针可以是NULL
,也可以是指向整数数组的指针(以NPY_NOTYPE
结尾),指示此指定类型的标量可以安全转换的数据类型(通常意味着不会失去精度)。
int *cancastto
可以为NULL
,也可以是一个整数数组(以NPY_NOTYPE
结尾),指示此数据类型可以安全转换的数据类型(通常意味着不会失去精度)。
void fastclip(void *in, n_in, void *min, void *max, void *out)
自 1.17 版本起已弃用:当np.clip
时,使用此函数会引发弃用警告。而不是使用此函数,数据类型必须使用PyUFunc_RegisterLoopForDescr
将自定义循环附加到np.core.umath.clip
、np.minimum
和np.maximum
。
自 1.19 版本起已弃用:设置此函数已被弃用,应该始终为NULL
,如果设置了,将被忽略。
一个从in
中读取n_in
个项目,并在min
和max
指向的限制内写入out
的值的函数,如果读取的值在min
和max
指向的限制内,或者在外部,则相应的限制。内存段必须是连续的且表现良好,min
或max
可以为NULL
,但不能同时为NULL
。
void fastputmask(void *in, void *mask, n_in, void *values, nv)
自 1.19 版本起已弃用:设置此函数已被弃用,应该始终为NULL
,如果设置了,将被忽略。
一个使用指针in
指向一个包含n_in
个项目的数组,指针mask
指向一个包含n_in
个布尔值的数组,以及指针vals
指向一个包含nv
个项目的数组的函数。将vals
中的项目复制到in
中,无论mask
中的值是否为非零,在nv < n_in
的情况下需要按需平铺vals
。所有数组必须是连续的且表现良好。
void fasttake(void *dest, void *src, *indarray, nindarray, n_outer, m_middle, nelem, clipmode)
自 1.19 版本起已弃用:设置此函数已被弃用,应该始终为NULL
,如果设置了,将被忽略。
一个函数,它接受指向 C 连续、行为良好段的指针src
,解释为形状为(n_outer, nindarray, nelem)
的 3 维数组,指向indarray
指针到m_middle
整数索引的连续、行为良好段,以及指向 C 连续、行为良好段的指针dest
,解释为形状为(n_outer, m_middle, nelem)
的 3 维数组。indarray
中的索引用于沿第二维索引src
,并将nelem
项的相应块复制到dest
。clipmode
(可以取值NPY_RAISE
、NPY_WRAP
或NPY_CLIP
)确定小于 0 或大于nindarray
的索引将如何处理。
int argmin(void *data, n, *min_ind, void *arr)
指针指向一个函数,该函数检索数组arr
中从指向data
的元素开始的n
个元素中最小的索引。此函数要求内存段是连续且行为良好。返回值始终为 0。最小元素的索引在min_ind
中返回。
PyArray_Type
类型对象实现了许多Python 对象
的特性,包括tp_as_number
、tp_as_sequence
、tp_as_mapping
和tp_as_buffer
接口。还使用了rich comparison
以及成员(tp_members
)和属性(tp_getset
)的新式属性查找。PyArray_Type
也可以被子类化。
提示
tp_as_number
方法使用通用方法调用已注册用于处理操作的函数。当导入_multiarray_umath
模块时,它将所有数组的数字操作设置为相应的 ufunc。可以使用PyUFunc_ReplaceLoopBySignature
更改此选择,也可以使用PyArray_SetStringFunction
改变tp_str
和tp_repr
方法。
PyUFunc_Type
ufunc 对象是通过创建PyUFunc_Type
来实现的。这是一种非常简单的类型,仅实现了基本的 getattribute 行为、打印行为,并具有调用行为,允许这些对象像函数一样运行。ufunc 的基本思想是持有对支持该操作的每种数据类型的快速 1 维(向量)循环的引用。这些一维循环都具有相同的签名,并且是创建新 ufunc 的关键。它们由通用循环代码在适当的时候调用,以实现 N 维函数。还为浮点和复数浮点数组定义了一些通用的 1-d 循环,允许您使用单个标量函数定义 ufunc(例如atanh)。
type PyUFuncObject
ufunc 的核心是PyUFuncObject
,它包含调用执行实际工作的基础 C 代码循环所需的所有信息。虽然这里描述了完整的内容,但它应被视为 NumPy 的内部内容,并通过PyUFunc_*
函数来操作。该结构的大小可能会因 NumPy 的不同版本而发生变化。为了确保兼容性:
sizeof(PyUFuncObject)
它具有以下结构:
typedef struct {
PyObject_HEAD
int nin;
int nout;
int nargs;
int identity;
PyUFuncGenericFunction *functions;
void **data;
int ntypes;
int reserved1;
const char *name;
char *types;
const char *doc;
void *ptr;
PyObject *obj;
PyObject *userloops;
int core_enabled;
int core_num_dim_ix;
int *core_num_dims;
int *core_dim_ixs;
int *core_offsets;
char *core_signature;
PyUFunc_TypeResolutionFunc *type_resolver;
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector;
void *reserved2;
npy_uint32 *op_flags;
npy_uint32 *iter_flags;
/* new in API version 0x0000000D */
npy_intp *core_dim_sizes;
npy_uint32 *core_dim_flags;
PyObject *identity_value;
/* Further private slots (size depends on the NumPy version) */
} PyUFuncObject;
int nin
输入参数的数量。
int nout
输出参数的数量。
int nargs
参数的总数(nin + nout)。这必须小于NPY_MAXARGS
。
int identity
要么PyUFunc_One
、PyUFunc_Zero
、PyUFunc_MinusOne
、PyUFunc_None
、PyUFunc_ReorderableNone
或PyUFunc_IdentityValue
用于指示该操作的身份。它仅在对空数组进行类似于 reduce 的调用时使用。
void functions(char **args, *dims, *steps, void *extradata)
一个函数指针数组—每种 ufunc 支持的数据类型都有一个。这是被调用以实现底层函数的向量循环,dims [0]次。第一个参数args是一个包含nargs指针的行为内存数组。首先是输入参数的数据指针,然后是输出参数的数据指针。必须跳过多少字节才能到达序列中的下一个元素是由steps数组中的相应条目指定的。最后一个参数允许循环接收额外的信息。这通常用于一个通用的向量循环可以用于多个函数的情况。在这种情况下,要调用的实际标量函数作为extradata传递进来。该函数指针数组的大小为 ntypes。
void **data
要传递给 1-d 向量循环的额外数据,如果不需要额外数据则为 NULL
。这个 C 数组必须与函数数组的大小相同(即 ntypes)。如果不需要额外数据,则使用 NULL
。对于 UFuncs 的几个 C-API 调用只是使用这些额外数据的 1-d 向量循环,以接收指向要调用的实际函数的指针。
int ntypes
ufunc 支持的数据类型数量。这个数字指定了内置数据类型的多少不同的 1-d 循环可用。
int reserved1
未使用。
char *name
ufunc 的字符串名称。这在动态构建 ufuncs 的 __doc__
属性时使用。
char *types
一个 (nargs \times ntypes) 的 8 位 type_numbers 数组,其中包含函数对于每个支持的(内置)数据类型的类型签名。对于每个 ntypes 函数,该数组中相应的类型编号集显示了 1-d 向量循环中 args 参数应该如何解释。这些类型编号不必是相同的类型,支持混合类型的 ufuncs。
char *doc
ufunc 的文档。不应该包含函数签名,因为在检索 __doc__
时会动态生成。
void *ptr
任何动态分配的内存。目前,这用于从 Python 函数创建动态 ufuncs,用于存储类型、数据和名称成员的空间。
*obj
对于从 Python 函数动态创建的 ufuncs,这个成员保存对底层 Python 函数的引用。
*userloops
用户定义的 1-d 向量循环的字典(存储为 CObject ptrs)用于用户定义的类型。用户可以为任何用户定义的类型注册一个循环。它通过类型编号检索。用户定义的类型编号始终大于NPY_USERDEF
。
int core_enabled
标量 ufunc 为 0;广义 ufunc 为 1
int core_num_dim_ix
签名中核心维度名称的不同个数
int *core_num_dims
每个参数的核心维度的数量
int *core_dim_ixs
以扁平化的形式存储的维度索引;第 k
个参数的索引存储在 core_dim_ixs[core_offsets[k] : core_offsets[k] + core_numdims[k]]
int *core_offsets
每个参数中第一个核心维度的位置在 core_dim_ixs
中,等同于累加和(core_num_dims
)
char *core_signature
核心签名字符串
PyUFunc_TypeResolutionFunc *type_resolver
解析类型并填充输入和输出的 dtypes 的函数
PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector
从版本 1.22 起弃用:这个插槽存在某些后备支持,但最终将被移除。依赖于此的通用函数最终将必须迁移。参见NEP 41 和 NEP 43
void *reserved2
用于可能的将来使用具有不同签名的循环选择器。
op_flags
重写每个 ufunc 操作数的默认操作数标志。
iter_flags
重写 ufunc 的默认 nditer 标志。
在 API 版本 0x0000000D 中添加
*core_dim_sizes
对于每个不同的核心尺寸,如果UFUNC_CORE_DIM_SIZE_INFERRED
等于 0
,则是 p签名的可能大小
*core_dim_flags
每个不同的核心维度,一组标志(UFUNC_CORE_DIM_CAN_IGNORE
和 UFUNC_CORE_DIM_SIZE_INFERRED
)
*identity_value
缩减的身份,当PyUFuncObject.identity
等于PyUFunc_IdentityValue
时。
UFUNC_CORE_DIM_CAN_IGNORE
如果维度名称以 ?
结尾
UFUNC_CORE_DIM_SIZE_INFERRED
若维度大小将由操作数确定,并非来自 frozen 签名
PyArrayIter_Type
这是一个迭代器对象,它可以轻松地循环访问 N 维数组。 这是从 ndarray 的 flat 属性返回的对象。 它还广泛用于整体实现内部,用于循环访问 N 维数组。 interface 已实现,使得可以对迭代器对象进行索引(使用 1-d 索引),并且一些方法通过tp_methods
表进行了实现。 此对象实现了next
方法,并且可以在 Python 中使用任何可以使用迭代器的地方。
type PyArrayIterObject
与PyArrayIter_Type
对象对应的 C-结构是PyArrayIterObject
. PyArrayIterObject
用于跟踪指向 N 维数组的指针。 它包含相关信息,用于快速遍历数组。 指针可以通过三种基本方法进行调整:1) 以 C 风格连续的方式前进到数组中的“下一个”位置,2) 前进到数组中的任意 N 维坐标,和 3) 前进到数组中的任意一维索引。 这些计算中使用了PyArrayIterObject
结构的成员。 迭代器对象保留其数组的维度和步幅信息。这可以根据需要进行“广播”,或者仅循环特定维度。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index;
npy_intp size;
npy_intp coordinates[NPY_MAXDIMS];
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
} PyArrayIterObject;
int nd_m1
(N-1) 其中 (N) 是底层数组中的维数。
index
当前数组的 1-d 索引。
size
底层数组的总大小。
*coordinates
一个 (N) -维数组的索引。
*dims_m1
每个维度中数组的大小减 1。
*strides
数组的步幅。在每个维度中,跳到下一个元素需要多少字节。
*backstrides
从维度末尾跳回到其开头需要多少字节。注意 backstrides[k] == strides[k] * dims_m1[k]
,但它存储在这里是一种优化。
*factors
此数组用于计算从 1-d 索引到 N-d 索引的所需乘积。
*ao
创建此迭代器表示的基础 ndarray 的指针。
char *dataptr
此成员指向由索引指示的 ndarray 中的元素。
contiguous
如果底层数组为NPY_ARRAY_C_CONTIGUOUS
,则此标志为真。它用于尽可能简化计算。
如何在 C 级别上使用数组迭代器在后续部分中有更详细的解释。通常情况下,您无需关注迭代器对象的内部结构,而只需通过宏 PyArray_ITER_NEXT
(it)、PyArray_ITER_GOTO
(it, dest) 或 PyArray_ITER_GOTO1D
(it, index) 与之交互即可。所有这些宏都要求参数 it 是一个 PyArrayIterObject*。
PyArrayMultiIter_Type
这种类型提供了一种封装了广播概念的迭代器。它允许将 (N) 个数组一起广播,以便循环按照 C 风格的连续方式在广播的数组上进行。相应的 C 结构是 PyArrayMultiIterObject
,其内存布局必须从传递给 PyArray_Broadcast
(obj) 函数的任何对象 obj 开始。通过调整数组迭代器来执行广播,使得每个迭代器表示广播的形状和大小,但其步幅被调整,以便在每次迭代中使用数组中的正确元素。
type PyArrayMultiIterObject
typedef struct {
PyObject_HEAD
int numiter;
npy_intp size;
npy_intp index;
int nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject *iters[NPY_MAXDIMS];
} PyArrayMultiIterObject;
int numiter
需要广播到相同形状的数组数量。
size
广播的总大小。
index
广播结果中当前的(1-d)索引。
int nd
广播结果中的维数数量。
*dimensions
广播结果的形状(仅使用 nd
个槽)。
**iters
一个包含为一起广播的数组调整过迭代器的迭代器对象数组。返回时,这些迭代器已调整好以进行广播。
PyArrayNeighborhoodIter_Type
这是一个迭代器对象,使得在 N 维邻域上循环变得��易。
type PyArrayNeighborhoodIterObject
与对象 PyArrayNeighborhoodIter_Type
对应的 C 结构是 PyArrayNeighborhoodIterObject
。
typedef struct {
PyObject_HEAD
int nd_m1;
npy_intp index, size;
npy_intp coordinates[NPY_MAXDIMS]
npy_intp dims_m1[NPY_MAXDIMS];
npy_intp strides[NPY_MAXDIMS];
npy_intp backstrides[NPY_MAXDIMS];
npy_intp factors[NPY_MAXDIMS];
PyArrayObject *ao;
char *dataptr;
npy_bool contiguous;
npy_intp bounds[NPY_MAXDIMS][2];
npy_intp limits[NPY_MAXDIMS][2];
npy_intp limits_sizes[NPY_MAXDIMS];
npy_iter_get_dataptr_t translate;
npy_intp nd;
npy_intp dimensions[NPY_MAXDIMS];
PyArrayIterObject* _internal_iter;
char* constant;
int mode;
} PyArrayNeighborhoodIterObject;
PyArrayFlags_Type
当从 Python 检索 flags 属性时,将构造这种特殊的内置对象。这种特殊类型通过将其作为属性访问这些不同的标志,或者通过将其作为字典访问,其中标志名称作为条目,使得更容易处理不同的标志。
type PyArrayFlagsObject
typedef struct PyArrayFlagsObject {
PyObject_HEAD
PyObject *arr;
int flags;
} PyArrayFlagsObject;
对于数组中可能存在的不同内置数据类型,每个都有一个 Python 类型。其中大部分只是对应的 C 中数据类型的简单包装。这些类型的 C 名称为Py{TYPE}ArrType_Type
,其中{TYPE}
可以是
Bool,Byte,Short,Int,Long,LongLong,UByte,UShort,UInt,ULong,ULongLong,Half,Float,Double,LongDouble,CFloat,CDouble,CLongDouble,String,Unicode,Void,和Object。
这些类型名称是 C-API 的一部分,因此可以在扩展 C 代码中创建它们。还有一个PyIntpArrType_Type
和一个PyUIntpArrType_Type
,它们可以简单地替代可以在平台上保存指针的整数类型之一。这些标量对象的结构不暴露给 C 代码。函数PyArray_ScalarAsCtype
(…)可用于从数组标量中提取 C 类型值,而函数PyArray_Scalar
(…)可用于从 C 值构造数组标量。
发现在 NumPy 开发中几个新的 C 结构非常有用。这些 C 结构至少在一个 C-API 调用中被使用,因此在此进行了文档化。定义这些结构的主要原因是为了简化使用 Python ParseTuple C-API,将 Python 对象转换为有用的 C 对象。
type PyArray_Dims
当需要解释形状和/或步幅信息时,此结构非常有用。该结构是:
typedef struct {
npy_intp *ptr;
int len;
} PyArray_Dims;
该结构的成员有
*ptr
指向(npy_intp
)整数列表的指针,通常表示数组形状或数组步幅。
int len
整数列表的长度。假定可以安全地访问ptr[0]至ptr[len-1]。
type PyArray_Chunk
这相当于 Python 中缓冲区对象结构,直到 ptr 成员。在 32 位平台上(即如果NPY_SIZEOF_INT
== NPY_SIZEOF_INTP
),len 成员也与缓冲区对象的等效成员匹配。用于表示通用单段内存块。
typedef struct {
PyObject_HEAD
PyObject *base;
void *ptr;
npy_intp len;
int flags;
} PyArray_Chunk;
其成员有
*base
此内存块来自的 Python 对象。需要这样做以正确计算内存。
void *ptr
指向内存单段块的起始位置的指针。
len
段的长度(以字节为单位)。
int flags
任何应该用于解释内存的数据标志(例如NPY_ARRAY_WRITEABLE
)。
另请参阅
数组接口协议
type PyArrayInterface
PyArrayInterface
结构定义了 NumPy 和其他扩展模块可以使用的快速数组接口协议。支持快速数组接口协议的对象的 __array_struct__
方法应返回一个包含指向 PyArrayInterface
结构的指针的 PyCapsule
,其中包含数组的相关详细信息。创建新数组后,应DECREF
该属性,这将释放 PyArrayInterface
结构的内存。记得INCREF
获取 __array_struct__
属性的对象,并将新 PyArrayObject
的 base 成员指向同一对象。通过这种方式,数组的内存将正确管理。
typedef struct {
int two;
int nd;
char typekind;
int itemsize;
int flags;
npy_intp *shape;
npy_intp *strides;
void *data;
PyObject *descr;
} PyArrayInterface;
int two
作为健全性检查的整数 2。
int nd
数组中的维度数。
char typekind
根据类型字符串约定指示存在什么类型的数组的字符,将 ‘t’ -> 位域,‘b’ -> 布尔值,‘i’ -> 有符号整数,‘u’ -> 无符号整数,‘f’ -> 浮点,‘c’ -> 复数浮点,‘O’ -> 对象,‘S’ -> (字节)字符串,‘U’ -> Unicode,‘V’ -> void。
int itemsize
数组中每个项所需的字节数。
int flags
任何一个位 NPY_ARRAY_C_CONTIGUOUS
(1),NPY_ARRAY_F_CONTIGUOUS
(2),NPY_ARRAY_ALIGNED
(0x100),NPY_ARRAY_NOTSWAPPED
(0x200),或 NPY_ARRAY_WRITEABLE
(0x400),以指示有关数据的一些信息。实际上,可以从其他参数确定标志 NPY_ARRAY_ALIGNED
,NPY_ARRAY_C_CONTIGUOUS
和 NPY_ARRAY_F_CONTIGUOUS
。标志 NPY_ARR_HAS_DESCR
(0x800) 也可以设置,以向消费版本 3 数组接口的对象指示结构的 descr 成员存在(版本 2 的数组接口将忽略它)。
*shape
包含数组每个维度的大小的数组。
*strides
包含在每个维度中跳过下一个元素所需的字节数的数组。
void *data
指向数组第一个元素的指针。
*descr
描述数据类型详细信息的 Python 对象(与__array_interface__
中的descr键相同)。如果typekind和itemsize提供足够的信息,这个字段可以为NULL
。除非在flags中打开了NPY_ARR_HAS_DESCR
标志,否则此字段也会被忽略。
内部,该代码使用了一些额外的 Python 对象,主要用于内存管理。这些类型无法直接从 Python 访问,并且不会暴露给 C-API。它们在此处仅用于完整性和帮助理解代码。
type PyUFunc_Loop1d
简单的 C 结构链表,包含了为用户定义的数据类型的每个定义的签名定义 1-d 循环所需的信息。
PyArrayMapIter_Type
使用此 Python 类型处理高级索引。它只是一个松散的包装器,包装了包含高级数组索引所需变量的 C 结构。
type PyArrayMapIterObject
与PyArrayMapIter_Type
相关联的 C 结构。如果你正在尝试理解高级索引映射代码,这个结构很有用。它在arrayobject.h
头文件中定义。此类型未暴露给 Python,可以用 C 结构替换。作为 Python 类型,它利用引用计数内存管理。
type PyArray_Dims
当形状和/或步长信息需要被解释时,这个结构非常有用。结构是:
typedef struct {
npy_intp *ptr;
int len;
} PyArray_Dims;
这个结构的成员是
*ptr
指向一个(npy_intp
)整数列表的指针,通常表示数组的形状或数组的步长。
int len
整数列表的长度。假定可以安全访问ptr[0]到ptr[len-1]。
type PyArray_Chunk
这与 Python 中的缓冲对象结构相当,直到 ptr 成员。在 32 位平台上(即 if NPY_SIZEOF_INT
== NPY_SIZEOF_INTP
),len 成员也与缓冲对象的等效成员匹配。它用于表示通用单段内存块。
typedef struct {
PyObject_HEAD
PyObject *base;
void *ptr;
npy_intp len;
int flags;
} PyArray_Chunk;
这些成员是
*base
分配这块内存的 Python 对象。需要这样以便能够正确计算内存使用。
void *ptr
指向单个内存块的起始指针。
len
段的字节长度。
int flags
用于解释内存的任何数据标志(例如 NPY_ARRAY_WRITEABLE
)。
另请参见
数组接口协议
type PyArrayInterface
PyArrayInterface
结构被定义为 NumPy 和其他扩展模块可以使用快速数组接口协议。支持快速数组接口协议的对象的__array_struct__
方法应返回包含指向具有数组的相关细节的PyArrayInterface
结构的指针的PyCapsule
。创建新数组后,应DECREF
该属性,这将释放PyArrayInterface
结构。记得INCREF
这个对象(其__array_struct__
属性已被检索)并将新PyArrayObject
的 base 成员指向同一对象。这样数组的内存将得到正确管理。
typedef struct {
int two;
int nd;
char typekind;
int itemsize;
int flags;
npy_intp *shape;
npy_intp *strides;
void *data;
PyObject *descr;
} PyArrayInterface;
int two
整数2
作为健全性检查。
int nd
数组中的维度数。
char typekind
根据类型字符串约定指示出现的数组类型的字符,‘t’ -> 位字段,‘b’ -> 布尔,‘i’ -> 有符号整数,‘u’ -> 无符号整数,‘f’ -> 浮点数,‘c’ -> 复数浮点数,‘O’ -> 对象,‘S’ -> (字节)字符串,‘U’ -> Unicode,‘V’ -> 空。
int itemsize
数组中每个项所需的字节数。
int flags
位之一NPY_ARRAY_C_CONTIGUOUS
(1)、NPY_ARRAY_F_CONTIGUOUS
(2)、NPY_ARRAY_ALIGNED
(0x100)、NPY_ARRAY_NOTSWAPPED
(0x200)或NPY_ARRAY_WRITEABLE
(0x400)用于指示数据的某些内容。NPY_ARRAY_ALIGNED
、NPY_ARRAY_C_CONTIGUOUS
和NPY_ARRAY_F_CONTIGUOUS
标志实际上可以从其他参数中确定。标志NPY_ARR_HAS_DESCR
(0x800)也可以设置为指示消耗版本 3 数组接口的对象,结构的 descr 成员存在(对于消耗版本 2 的数组接口的对象将被忽略)。
*shape
包含每个维度中数组大小的数组。
*strides
包含跳转到每个维度中下一个元素所需字节数的数组。
void *data
指向数组第一个元素的指针。
*descr
描述数据类型的 Python 对象更详细(与__array_interface__
中的descr键相同)。如果typekind和itemsize提供足够信息,则此字段可以是NULL
。除非flags中打开了NPY_ARR_HAS_DESCR
标志,否则此字段也会被忽略。
在内部,代码主要用于内存管理的一些额外的 Python 对象。这些类型不能直接从 Python 访问,也未暴露给 C-API。它们仅在此列出,以完整和帮助理解代码。
type PyUFunc_Loop1d
包含定义用户定义数据类型的每个签名的 1-d 循环所需信息的 C 结构的简单链接列表。
PyArrayMapIter_Type
高级索引使用这种 Python 类型处理。它只是围绕包含高级数组索引所需变量的 C 结构的松散包装。
type PyArrayMapIterObject
与PyArrayMapIter_Type
相关联的 C 结构。如果您试图理解高级索引映射代码,这个结构非常有用。它在arrayobject.h
头文件中定义。这种类型并未暴露给 Python,可以用 C 结构替代。作为 Python 类型,它利用引用计数的内存管理。