目前,Scipy项目包括两个包:
numpy---它提供如下包:
numpy.distutils-python distutils的扩展
numpy.f2py-将Fortran/C代码绑定到python的工具
numpy.core-将来替换numeric和numarray包
numpy.lib-额外的实用程序函数
numpy.testing-用于单元测试的numpy样式工具
等
scipy——一组用于Python的科学工具。
本文的目的是描述如何向scipy添加新工具。
scipy由python包组成,称为scipy包,通过 scipy 命名空间。每个scipy包可能包含其他scipy包。等等。因此,scipy目录树是一个任意深度和宽度的包树。任何scipy包可能依赖于numpy包,但对其他scipy包的依赖性应保持最小或零。
scipy
scipy包除了其源文件外,还包含以下文件和目录:
setup.py ---构建脚本 __init__.py ---包初始值设定项 tests/ ---单元测试目录
setup.py ---构建脚本
setup.py
__init__.py ---包初始值设定项
__init__.py
tests/ ---单元测试目录
tests/
其内容如下所述。
为了向scipy添加一个python包,它的构建脚本 (setup.py )必须满足某些要求。最重要的要求是包定义了 configuration(parent_package='',top_path=None) 返回适合传递到的字典的函数 numpy.distutils.core.setup(..) . 为了简化这本词典的结构, numpy.distutils.misc_util 提供 Configuration 类,如下所述。
configuration(parent_package='',top_path=None)
numpy.distutils.core.setup(..)
numpy.distutils.misc_util
Configuration
下面是一个最小值的例子 setup.py 纯scipy包的文件:
#!/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
parent_package
top_path
这个 Configuration 构造函数有第四个可选参数, package_path ,当包文件位于与 setup.py 文件。
package_path
剩下的 Configuration 参数是将用于初始化的属性的所有关键字参数 Configuration 实例。通常,这些关键字与 setup(..) 例如,函数期望 packages , ext_modules , data_files , include_dirs , libraries , headers , scripts , package_dir 等等。但是,不建议直接指定这些关键字,因为这些关键字参数的内容不会被处理或检查scipy建筑系统的一致性。
setup(..)
packages
ext_modules
data_files
include_dirs
libraries
headers
scripts
package_dir
最后, Configuration 有 .todict() 方法,将所有配置数据作为适合传递给 setup(..) 功能。
.todict()
除了可以通过关键字参数指定的属性之外, Configuration 构造函数, Configuration 实例(我们将其表示为 config )具有以下在编写安装脚本时有用的属性:
config
config.name -当前包的全名。父包的名称可以提取为 config.name.split('.') .
config.name
config.name.split('.')
config.local_path -当前位置的路径 setup.py 文件。
config.local_path
config.top_path -主管道位置的路径 setup.py 文件。
config.top_path
config.todict() ---返回适合传递到的配置字典 numpy.distutils.core.setup(..) 功能。
config.todict()
config.paths(*paths) --- applies `` glob.glob(..)``到项目 ``paths 如有必要。修正 paths 与相关的项 config.local_path .
config.paths(*paths) --- applies `` glob.glob(..)``到项目 ``paths
paths
config.get_subpackage(subpackage_name,subpackage_path=None) ---返回子包配置列表。子包在当前目录的名称下查找 subpackage_name 但是路径也可以通过可选的 subpackage_path 参数。如果 subpackage_name 指定为 None 那么子包名称将作为 subpackage_path . 任何 * 用于子包的名称扩展为通配符。
config.get_subpackage(subpackage_name,subpackage_path=None)
subpackage_name
subpackage_path
None
*
config.add_subpackage(subpackage_name,subpackage_path=None) ---将scipy子包配置添加到当前子包配置中。以上解释了参数的含义和用法,请参见 config.get_subpackage() 方法。
config.add_subpackage(subpackage_name,subpackage_path=None)
config.get_subpackage()
config.add_data_files(*files) ---准备 files 到 data_files 名单。如果 files 项是一个元组,然后其第一个元素定义相对于包安装目录复制数据文件的后缀,第二个元素指定数据文件的路径。默认情况下,数据文件复制到包安装目录下。例如,
config.add_data_files(*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 是一个元组,那么它的第一个元素定义数据文件相对于包安装目录的复制位置后缀,第二个元素指定数据目录的路径。默认情况下,数据目录将复制到包安装目录下的basename下 data_path . 例如,
config.add_data_dir(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_include_dirs(*paths)
config.add_headers(*files) ---准备 files 到 headers 名单。默认情况下,标题将安装在 <prefix>/include/pythonX.X/<config.name.replace('.','/')>/ 目录。如果 files 项是元组,然后它的第一个参数指定相对于 <prefix>/include/pythonX.X/ 路径。这是一个python distuils方法;不鼓励将其用于numpy和scipy,而支持 config.add_data_files(*files) .
config.add_headers(*files)
<prefix>/include/pythonX.X/<config.name.replace('.','/')>/
<prefix>/include/pythonX.X/
config.add_scripts(*files) ---准备 files 到 scripts 名单。脚本将安装在 <prefix>/bin/ 目录。
config.add_scripts(*files)
<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.add_extension(name,sources,**kw)
Extension
name
add_extension
define_macros
undef_macros
library_dirs
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 列表比扩展模块更新,然后将重建该模块。
config.paths
源列表可以包含具有模式的函数(“源生成器”)。 def <funcname>(ext, build_dir): return <source(s) or None> .如果 funcname 收益率 None ,不生成源。如果 Extension 实例在处理完所有源生成器后没有源,将不会生成扩展模块。这是有条件地定义扩展模块的推荐方法。源生成器函数由 build_src 子命令 numpy.distutils .
def <funcname>(ext, build_dir): return <source(s) or None>
funcname
build_src
numpy.distutils
例如,这里有一个典型的源生成器函数:
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
第一个参数包含可用于访问其属性的扩展实例,例如 depends , sources 等等。在构建过程中列出并修改它们。第二个参数提供了一个生成目录的路径,在将文件创建到磁盘时必须使用该目录。
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.add_library(name, sources, **build_info)
macros
extra_compiler_args
.add_extension()
config.have_f77c() ---如果Fortran 77编译器可用,则返回true(读取:成功编译的简单Fortran 77代码)。
config.have_f77c()
config.have_f90c() ---如果Fortran 90编译器可用,则返回true(读取:成功编译的简单Fortran 90代码)。
config.have_f90c()
config.get_version() ---返回当前包的版本字符串, None 如果无法检测到版本信息。此方法扫描文件 __version__.py , <packagename>_version.py , version.py , __svn_version__.py 对于字符串变量 version , __version__ , <packagename>_version .
config.get_version()
__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.make_svn_version_py()
config.get_build_temp_dir() ---返回临时目录的路径。这是应该建立临时文件的地方。
config.get_build_temp_dir()
config.get_distribution() ---返回distutils Distribution 实例。
config.get_distribution()
Distribution
config.get_config_cmd() ---返回 numpy.distutils 配置命令实例。
config.get_config_cmd()
config.get_info(*names) ---
config.get_info(*names)
.src
numpy distutils支持名为<somefile>的源文件的自动转换。此工具可用于维护非常相似的代码块,只需要在块之间进行简单的更改。在安装程序的构建阶段,如果遇到名为<somefile>.src的模板文件,则从模板构建名为<somefile>的新文件,并将其放置在要使用的构建目录中。支持两种形式的模板转换。第一种形式出现在名为<file>ext.src的文件中,其中ext是可识别的Fortran扩展名(f,f90,f95,f77,for,ftn,pyf)。第二种形式用于所有其他情况。
此模板转换器将复制所有 功能 和 子程序 根据“<…>”中的规则,文件中名为“<…>”的块。“<…>”中逗号分隔的单词数决定了块重复的次数。这些词表示在每个块中应该用什么替换重复规则“<…>”。一个块中的所有重复规则必须包含相同数量的逗号分隔词,指示该块应重复的次数。如果重复规则中的单词需要逗号、左箭头或右箭头,则在其前面加上反斜杠''。如果重复规则中的一个词与'<index>'匹配,则它将替换为同一重复规范中的第<index>个词。重复规则有两种形式:命名和简短。
当同一组重复必须在一个块中多次使用时,命名重复规则非常有用。它是使用<rule1=item1,item2,item3,…,itemn>指定的,其中n是块应重复的次数。在块的每个重复上,整个表达式“<…>”将首先替换为item1,然后替换为item2,依此类推,直到完成n次重复。一旦引入了一个命名的重复规范,就可以使用相同的重复规则。 在当前块中 仅引用名称(即<rule1>)。
一个简短的重复规则看起来像<item1,item2,item3,…,itemn>。规则指定整个表达式“<…>”应首先替换为item1,然后替换为item2,以此类推,直到完成n次重复。
以下预定义的命名重复规则可用:
<prefix=s,d,c,z>
<_c=s,d,c,z>
<u t=real,double precision,complex,double complex>
<ftype=real,double precision,complex,double complex>
<ctype=float,double,complex_float,complex_double>
<ftyperial=float,double precision,\0,\1>
<ctyperial=float,double,\0,\1>
非Fortran文件使用单独的语法来定义模板块,这些模板块应使用变量展开式重复,类似于Fortran特定重复的命名重复规则。
numpy distuils预处理C源文件(扩展名: .c.src )用自定义模板语言编写以生成C代码。这个 @ 符号用于包装宏样式变量,以支持字符串替换机制,该机制可以描述(例如)一组数据类型。
.c.src
@
模板语言块由分隔 /**begin repeat 和 /**end repeat**/ 行,也可以使用连续编号的分隔行嵌套,例如 /**begin repeat1 和 /**end repeat1**/ :
/**begin repeat
/**end repeat**/
/**begin repeat1
/**end repeat1**/
/**begin repeat 在一条直线上,它本身标志着应该重复的一段的开始。
命名变量展开式是使用 #name=item1, item2, item3, ..., itemN# 并放置在连续的行上。这些变量在每个重复块中用相应的字替换。同一重复块中的所有命名变量必须定义相同的字数。
#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# .
item*N
item, item, ..., item
*N
#name=(item1, item2)*4#
#name=item1, item2, item1, item2, item1, item2, item1, item2#
*/ 在一行上单独标记变量扩展命名的结束。下一行是将使用命名规则重复的第一行。
*/
在要重复的块中,应展开的变量指定为 @name@ .
@name@
/**end repeat**/ 在一行上,将前一行标记为要重复的块的最后一行。
numpyc源代码中的循环可能有 @TYPE@ 变量,以字符串替换为目标,它预处理为多个具有多个字符串的相同循环,如 INT , LONG , UINT , ULONG . 这个 @TYPE@ 样式语法通过模仿具有泛型类型支持的语言,从而减少代码重复和维护负担。
@TYPE@
INT
LONG
UINT
ULONG
在下面的模板源示例中,上述规则可能更清楚:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/* TIMEDELTA to non-float types */ /**begin repeat * * #TOTYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, * LONGLONG, ULONGLONG, DATETIME, * TIMEDELTA# * #totype = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, * npy_long, npy_ulong, npy_longlong, npy_ulonglong, * npy_datetime, npy_timedelta# */ /**begin repeat1 * * #FROMTYPE = TIMEDELTA# * #fromtype = npy_timedelta# */ static void @FROMTYPE@_to_@TOTYPE@(void *input, void *output, npy_intp n, void *NPY_UNUSED(aip), void *NPY_UNUSED(aop)) { const @fromtype@ *ip = input; @totype@ *op = output; while (n--) { *op++ = (@totype@)*ip++; } } /**end repeat1**/ /**end repeat**/
泛型C源文件的预处理(无论是在NumPy中还是在任何使用NumPy Distutils的第三方包中)由 conv_template.py . 生成的特定于类型的C文件(扩展名: .c )通过这些模块,在构建过程中就可以进行编译了。C头文件也支持这种形式的通用类型(预处理以生成 .h 文件)。
.c
.h
get_numpy_include_dirs() ---返回numpy base include目录的列表。numpy base include目录包含头文件,例如 numpy/arrayobject.h , numpy/funcobject.h 等等。对于installed numpy,返回的列表的长度为1,但是当构建numpy时,列表可能包含更多目录,例如 config.h 文件说明 numpy/base/setup.py 文件生成并由使用 numpy 头文件。
get_numpy_include_dirs()
numpy/arrayobject.h
numpy/funcobject.h
config.h
numpy/base/setup.py
numpy
append_path(prefix,path) ---智能附加 path 到 prefix .
append_path(prefix,path)
path
prefix
gpaths(paths, local_path='') ---将glob应用于路径并提前 local_path 如果需要的话。
gpaths(paths, local_path='')
local_path
njoin(*path) --- join pathname components + convert /-separated path to `` os.sep``-分离路径和解析 .., . 从路径。前任。 njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g') .
njoin(*path)
/
..
.
njoin('a',['b','./c'],'..','g') -> os.path.join('a','b','g')
minrelpath(path) ---解析点 path .
minrelpath(path)
rel_path(path, parent_path) --返回 path 相对 parent_path .
rel_path(path, parent_path)
parent_path
def get_cmd(cmdname,_cache={{}}) ---返回 numpy.distutils 命令实例。
def get_cmd(cmdname,_cache={{}})
all_strings(lst)
has_f_sources(sources)
has_cxx_sources(sources)
filter_sources(sources) --- return c_sources, cxx_sources, f_sources, fmodule_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) ---用点连接非零参数。
dot_join(*args)
get_frame(level=0) ---从具有给定级别的调用堆栈返回帧对象。
get_frame(level=0)
cyg2win32(path)
mingw32() --返回 True 使用mingw32环境时。
mingw32()
True
terminal_has_colors(), red_text(s), green_text(s), yellow_text(s), blue_text(s), cyan_text(s)
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) ---返回模块相对于父路径的路径。把手也 __main__ 和 __builtin__ 模块。
get_path(mod_name,parent_path=None)
__main__
__builtin__
allpath(name) --- replaces / with `` SEP’in name .
allpath(name)
cxx_ext_match, fortran_ext_match, f90_ext_match, f90_module_name_match
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 是::
""" 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.add_library('library',sources=[...], config_fc={'noopt':(__file__,1)})
sources=[...], config_fc={'noopt':(__file__,1)})
将编译 library 没有优化标志的源。
library
建议仅以编译器独立的方式指定那些配置fc选项。
一些旧的Fortran代码需要特殊的编译器选项才能正常工作。为了指定每个源文件的编译器选项, numpy.distutils Fortran编译器查找以下模式:
CF77FLAGS(<fcompiler type>) = <fcompiler f77flags>
在源的前20行中,使用 f77flags 对于指定类型的FCompiler(第一个字符 C 是可选的)。
f77flags
C
TODO:这个特性也可以很容易地扩展到Fortran 90代码中。如果您需要这样的功能,请通知我们。