“Python/docs/3.9/extending/building”的版本间差异
(autoload) |
小 (Page commit) |
||
第1行: | 第1行: | ||
+ | {{DISPLAYTITLE:4. 构建 C 和 C++ 扩展 — Python 文档}} | ||
<div id="building-c-and-c-extensions" class="section"> | <div id="building-c-and-c-extensions" class="section"> | ||
<span id="building"></span> | <span id="building"></span> | ||
− | = | + | = 4. 构建 C 和 C++ 扩展 = |
− | + | CPython 的 AC 扩展是一个共享库(例如 Linux 上的 <code>.so</code> 文件,Windows 上的 <code>.pyd</code>),它导出 ''初始化函数'' 。 | |
− | <code>.pyd</code> | ||
− | + | 为了可导入,共享库必须在 <span id="index-0" class="target"></span>[[../../using/cmdline#envvar-PYTHONPATH|PYTHONPATH]] 上可用,并且必须以模块名称命名,并带有适当的扩展名。 使用 distutils 时,会自动生成正确的文件名。 | |
− | |||
− | |||
− | + | 初始化函数具有签名: | |
+ | |||
+ | ; [[../../c-api/structures#c|<span class="n"><span class="pre">PyObject</span></span>]]<span class="w"> </span><span class="p"><span class="pre">*</span></span><span class="sig-name descname"><span class="n"><span class="pre">PyInit_modulename</span></span></span><span class="sig-paren">(</span><span class="kt"><span class="pre">void</span></span><span class="sig-paren">)</span><br /> | ||
− | |||
: | : | ||
− | + | 它返回一个完全初始化的模块,或者一个 [[../../c-api/module#c|PyModuleDef]] 实例。 有关详细信息,请参阅 [[../../c-api/module#initializing-modules|初始化 C 模块]] 。 | |
− | |||
− | + | 对于只有 ASCII 名称的模块,函数必须命名为 <code>PyInit_<modulename></code>,用模块名称替换 <code><modulename></code>。 使用 [[../../c-api/module#multi-phase-initialization|多阶段初始化]] 时,允许使用非 ASCII 模块名称。 在这种情况下,初始化函数名称是 <code>PyInitU_<modulename></code>,其中 <code><modulename></code> 使用 Python 的 ''punycode'' 编码,连字符替换为下划线。 在 Python 中: | |
− | <code>PyInit_<modulename></code> | ||
− | |||
− | |||
− | <code>PyInitU_<modulename></code> | ||
− | ''punycode'' | ||
<div class="highlight-python notranslate"> | <div class="highlight-python notranslate"> | ||
第30行: | 第23行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python">def initfunc_name(name): |
try: | try: | ||
suffix = b'_' + name.encode('ascii') | suffix = b'_' + name.encode('ascii') | ||
except UnicodeEncodeError: | except UnicodeEncodeError: | ||
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_') | suffix = b'U_' + name.encode('punycode').replace(b'-', b'_') | ||
− | return b'PyInit' + suffix</ | + | return b'PyInit' + suffix</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 通过定义多个初始化函数,可以从单个共享库中导出多个模块。 但是,导入它们需要使用符号链接或自定义导入器,因为默认情况下只能找到与文件名对应的函数。 有关详细信息,请参阅 <span id="index-1" class="target"></span>[https://www.python.org/dev/peps/pep-0489 PEP 489] 中的 ''“一个库中的多个模块”'' 部分。 | |
− | |||
− | |||
− | |||
− | |||
<div id="building-c-and-c-extensions-with-distutils" class="section"> | <div id="building-c-and-c-extensions-with-distutils" class="section"> | ||
− | == | + | == 4.1. 使用 distutils 构建 C 和 C++ 扩展 == |
− | + | 可以使用 Python 中包含的 distutils 构建扩展模块。 由于 distutils 也支持创建二进制包,用户不一定需要编译器和 distutils 来安装扩展。 | |
− | |||
− | |||
− | + | distutils 包包含一个驱动程序脚本,<code>setup.py</code>。 这是一个普通的 Python 文件,在最简单的情况下,它可能如下所示: | |
− | Python | ||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第61行: | 第47行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from distutils.core import setup, Extension |
module1 = Extension('demo', | module1 = Extension('demo', | ||
第69行: | 第55行: | ||
version = '1.0', | version = '1.0', | ||
description = 'This is a demo package', | description = 'This is a demo package', | ||
− | ext_modules = [module1])</ | + | ext_modules = [module1])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 有了这个 <code>setup.py</code> 和一个文件 <code>demo.c</code>,运行 | |
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第80行: | 第66行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">python setup.py build</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 将编译 <code>demo.c</code>,并在 <code>build</code> 目录中生成名为 <code>demo</code> 的扩展模块。 根据系统的不同,模块文件将在子目录 <code>build/lib.system</code> 中结束,并且可能具有类似 <code>demo.so</code> 或 <code>demo.pyd</code> 的名称。 | |
− | |||
− | |||
− | <code>demo.so</code> | ||
− | + | 在<code>setup.py</code>中,所有的执行都是通过调用<code>setup</code>函数来完成的。 这需要可变数量的关键字参数,上面的示例仅使用其中的一个子集。 具体来说,该示例指定了构建包的元信息,并指定了包的内容。 通常,一个包会包含额外的模块,比如 Python 源模块、文档、子包等。 请参考 [[../../distutils/index#distutils-index|Distributing Python Modules (Legacy version)]] 中的 distutils 文档,了解更多关于 distutils 的特性; 本节仅解释构建扩展模块。 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | distutils | ||
− | + | 通常预先计算 <code>setup()</code> 的参数,以更好地构建驱动程序脚本。 在上面的例子中,[[../../distutils/apiref#distutils.core|setup()]] 的 <code>ext_modules</code> 参数是一个扩展模块列表,每个模块都是 <code>Extension</code> 的一个实例。 在示例中,该实例定义了一个名为 <code>demo</code> 的扩展名,它是通过编译单个源文件 <code>demo.c</code> 来构建的。 | |
− | |||
− | [[../../distutils/apiref#distutils.core|<code> | ||
− | |||
− | |||
− | |||
− | |||
− | + | 在许多情况下,构建扩展更为复杂,因为可能需要额外的预处理器定义和库。 这在下面的示例中进行了演示。 | |
− | |||
− | |||
<div class="highlight-python3 notranslate"> | <div class="highlight-python3 notranslate"> | ||
第115行: | 第83行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="python3">from distutils.core import setup, Extension |
module1 = Extension('demo', | module1 = Extension('demo', | ||
第134行: | 第102行: | ||
This is really just a demo package. | This is really just a demo package. | ||
''', | ''', | ||
− | ext_modules = [module1])</ | + | ext_modules = [module1])</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在这个例子中,[[../../distutils/apiref#distutils.core|setup()]] 被调用时带有额外的元信息,推荐在必须构建分发包时使用。 对于扩展本身,它指定了预处理器定义、包含目录、库目录和库。 根据编译器的不同,distutils 以不同的方式将此信息传递给编译器。 例如,在 Unix 上,这可能会导致编译命令 | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第151行: | 第113行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o |
− | gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so</ | + | gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 这些线路仅用于演示目的; distutils 用户应该相信 distutils 会正确调用。 | |
− | |||
第166行: | 第127行: | ||
<span id="distributing"></span> | <span id="distributing"></span> | ||
− | == | + | == 4.2. 分发扩展模块 == |
− | + | 成功构建扩展后,可以通过三种方式使用它。 | |
− | + | 最终用户通常希望安装该模块,他们通过运行 | |
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第176行: | 第137行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">python setup.py install</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 模块维护者应该制作源码包; 为此,他们运行 | |
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第187行: | 第148行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">python setup.py sdist</syntaxhighlight> |
</div> | </div> | ||
</div> | </div> | ||
− | + | 在某些情况下,源代码分发中需要包含其他文件; 这是通过 <code>MANIFEST.in</code> 文件完成的; 有关详细信息,请参阅 [[../../distutils/sourcedist#manifest|指定要分发的文件]] 。 | |
− | |||
− | + | 如果源分发已成功构建,维护人员还可以创建二进制分发。 根据平台,可以使用以下命令之一来执行此操作。 | |
− | |||
− | |||
<div class="highlight-c notranslate"> | <div class="highlight-c notranslate"> | ||
第203行: | 第161行: | ||
<div class="highlight"> | <div class="highlight"> | ||
− | < | + | <syntaxhighlight lang="c">python setup.py bdist_wininst |
python setup.py bdist_rpm | python setup.py bdist_rpm | ||
− | python setup.py bdist_dumb</ | + | python setup.py bdist_dumb</syntaxhighlight> |
</div> | </div> | ||
第212行: | 第170行: | ||
</div> | </div> | ||
+ | |||
+ | </div> | ||
+ | <div class="clearer"> | ||
+ | |||
+ | |||
</div> | </div> | ||
− | [[Category:Python 3.9 | + | [[Category:Python 3.9 文档]] |
2021年10月31日 (日) 04:50的最新版本
4. 构建 C 和 C++ 扩展
CPython 的 AC 扩展是一个共享库(例如 Linux 上的 .so
文件,Windows 上的 .pyd
),它导出 初始化函数 。
为了可导入,共享库必须在 PYTHONPATH 上可用,并且必须以模块名称命名,并带有适当的扩展名。 使用 distutils 时,会自动生成正确的文件名。
初始化函数具有签名:
- PyObject *PyInit_modulename(void)
它返回一个完全初始化的模块,或者一个 PyModuleDef 实例。 有关详细信息,请参阅 初始化 C 模块 。
对于只有 ASCII 名称的模块,函数必须命名为 PyInit_<modulename>
,用模块名称替换 <modulename>
。 使用 多阶段初始化 时,允许使用非 ASCII 模块名称。 在这种情况下,初始化函数名称是 PyInitU_<modulename>
,其中 <modulename>
使用 Python 的 punycode 编码,连字符替换为下划线。 在 Python 中:
def initfunc_name(name):
try:
suffix = b'_' + name.encode('ascii')
except UnicodeEncodeError:
suffix = b'U_' + name.encode('punycode').replace(b'-', b'_')
return b'PyInit' + suffix
通过定义多个初始化函数,可以从单个共享库中导出多个模块。 但是,导入它们需要使用符号链接或自定义导入器,因为默认情况下只能找到与文件名对应的函数。 有关详细信息,请参阅 PEP 489 中的 “一个库中的多个模块” 部分。
4.1. 使用 distutils 构建 C 和 C++ 扩展
可以使用 Python 中包含的 distutils 构建扩展模块。 由于 distutils 也支持创建二进制包,用户不一定需要编译器和 distutils 来安装扩展。
distutils 包包含一个驱动程序脚本,setup.py
。 这是一个普通的 Python 文件,在最简单的情况下,它可能如下所示:
from distutils.core import setup, Extension
module1 = Extension('demo',
sources = ['demo.c'])
setup (name = 'PackageName',
version = '1.0',
description = 'This is a demo package',
ext_modules = [module1])
有了这个 setup.py
和一个文件 demo.c
,运行
python setup.py build
将编译 demo.c
,并在 build
目录中生成名为 demo
的扩展模块。 根据系统的不同,模块文件将在子目录 build/lib.system
中结束,并且可能具有类似 demo.so
或 demo.pyd
的名称。
在setup.py
中,所有的执行都是通过调用setup
函数来完成的。 这需要可变数量的关键字参数,上面的示例仅使用其中的一个子集。 具体来说,该示例指定了构建包的元信息,并指定了包的内容。 通常,一个包会包含额外的模块,比如 Python 源模块、文档、子包等。 请参考 Distributing Python Modules (Legacy version) 中的 distutils 文档,了解更多关于 distutils 的特性; 本节仅解释构建扩展模块。
通常预先计算 setup()
的参数,以更好地构建驱动程序脚本。 在上面的例子中,setup() 的 ext_modules
参数是一个扩展模块列表,每个模块都是 Extension
的一个实例。 在示例中,该实例定义了一个名为 demo
的扩展名,它是通过编译单个源文件 demo.c
来构建的。
在许多情况下,构建扩展更为复杂,因为可能需要额外的预处理器定义和库。 这在下面的示例中进行了演示。
from distutils.core import setup, Extension
module1 = Extension('demo',
define_macros = [('MAJOR_VERSION', '1'),
('MINOR_VERSION', '0')],
include_dirs = ['/usr/local/include'],
libraries = ['tcl83'],
library_dirs = ['/usr/local/lib'],
sources = ['demo.c'])
setup (name = 'PackageName',
version = '1.0',
description = 'This is a demo package',
author = 'Martin v. Loewis',
author_email = 'martin@v.loewis.de',
url = 'https://docs.python.org/extending/building',
long_description = '''
This is really just a demo package.
''',
ext_modules = [module1])
在这个例子中,setup() 被调用时带有额外的元信息,推荐在必须构建分发包时使用。 对于扩展本身,它指定了预处理器定义、包含目录、库目录和库。 根据编译器的不同,distutils 以不同的方式将此信息传递给编译器。 例如,在 Unix 上,这可能会导致编译命令
gcc -DNDEBUG -g -O3 -Wall -Wstrict-prototypes -fPIC -DMAJOR_VERSION=1 -DMINOR_VERSION=0 -I/usr/local/include -I/usr/local/include/python2.2 -c demo.c -o build/temp.linux-i686-2.2/demo.o
gcc -shared build/temp.linux-i686-2.2/demo.o -L/usr/local/lib -ltcl83 -o build/lib.linux-i686-2.2/demo.so
这些线路仅用于演示目的; distutils 用户应该相信 distutils 会正确调用。
4.2. 分发扩展模块
成功构建扩展后,可以通过三种方式使用它。
最终用户通常希望安装该模块,他们通过运行
python setup.py install
模块维护者应该制作源码包; 为此,他们运行
python setup.py sdist
在某些情况下,源代码分发中需要包含其他文件; 这是通过 MANIFEST.in
文件完成的; 有关详细信息,请参阅 指定要分发的文件 。
如果源分发已成功构建,维护人员还可以创建二进制分发。 根据平台,可以使用以下命令之一来执行此操作。
python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb