On this page
4. 构建 C 和 C 扩展
CPython 的 C 扩展是共享库(例如 Linux 上的.so
文件,Windows 上的.pyd
),该库导出初始化函数。
要导入,共享库必须在 PYTHONPATH上可用,并且必须以模块名称命名,并带有适当的 extensions。使用 distutils 时,会自动生成正确的文件名。
初始化函数具有签名:
- PyObject *
PyInit_modulename
(无效)
它返回完全初始化的模块或PyModuleDef实例。有关详情,请参见初始化 C 模块。
对于仅使用 ASCII 名称的模块,该函数必须命名为PyInit_<modulename>
,其中<modulename>
替换为模块名称。使用Multi-phase initialization时,允许使用非 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
pass定义多个初始化函数,可以从单个共享库中导出多个模块。但是,导入它们需要使用符号链接或自定义导入器,因为默认情况下,仅找到与文件名相对应的函数。有关详细信息,请参见 PEP 489中的*“一个库中有多个模块” *部分。
4.1. 使用 distutils 构建 C 和 C 扩展
扩展模块可以使用 distutils 构建,该工具包含在 Python 中。由于 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
的扩展模块。根据系统的不同,模块文件finally将位于子目录build/lib.system
中,并且名称可能类似于demo.so
或demo.pyd
。
在setup.py
中,所有执行都pass调用setup
函数执行。这需要可变数量的关键字参数,上面的示例仅使用其中一个子集。具体而言,该示例指定了用于构建程序包的元信息,并指定了程序包的内容。通常,一个软件包将包含其他模块,例如 Python 源模块,文档,子软件包等。请参阅分发 Python 模块(旧版)中的 distutils 文档,以了解有关 distutils Function的更多信息;本节仅说明构建扩展模块。
通常,预先计算setup()
的参数以更好地构造驱动程序脚本。在上面的示例中,setup()的ext_modules
参数是扩展模块的列表,每个扩展模块都是Extension
的实例。在示例中,实例定义了一个名为demo
的 extensions,该 extensions 是pass编译单个源文件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. 分发扩展模块
成功构建扩展后,有三种使用方式。
finally用户通常希望pass运行以下命令来安装模块
python setup.py install
模块维护者应产生源程序包;为此,他们运行
python setup.py sdist
在某些情况下,其他文件需要包含在源分发中。这是passMANIFEST.in
文件完成的;有关详情,请参见指定要分发的文件。
如果已成功构建源分发,则维护者还可以创建二进制分发。根据平台的不同,可以使用以下命令之一。
python setup.py bdist_wininst
python setup.py bdist_rpm
python setup.py bdist_dumb