Cython

官方文档: https://cython.readthedocs.io/en/latest/

中文文档:https://www.bookstack.cn/read/cython-doc-zh/
                   https://cython.apachecn.org/#/

构建 Cython 代码

与 Python 不同,Cython 代码必须编译。这发生在两个阶段:

.pyx文件由 Cython 编译为.c文件,包含 Python 扩展模块的代码。

.c文件由 C 编译器编译为.so文件(或 Windows 上的.pyd),可直接import直接进入 Python 会话.

构建 Cython 代码的几种方法:

  • 写一个 distutils / setuptools setup.py。推荐的方式。
  • 使用 Pyximport,导入 Cython .pyx文件就像它们是.py文件一样(使用 distutils 在后台编译和构建)。这种方法比编写setup.py更容易,但不是很灵活。因此,如果您需要某些编译选项,则需要编写setup.py
  • 手动运行cython命令行实用程序,从.pyx文件生成.c文件,然后手动将.c文件编译成适合从 Python 导入的共享库或 DLL。(这些手动步骤主要用于调试和实验。)
  • 使用 [Jupyter] 笔记本或 [Sage] 笔记本,两者都允许 Cython 代码内联。这是开始编写 Cython 代码并运行它的最简单方法。

手动从命令行编译

有两种从命令行编译的方法:

cython命令获取.py.pyx文件并将其编译为 C / C ++文件。

cythonize命令获取.py.pyx文件并将其编译为 C / C ++文件。然后,它将 C / C ++文件编译为可直接从 Python 导入的扩展模块。

cython命令进行编译

一种方法是使用 Cython 编译器手动编译它,例如:

$ cython primes.pyx

生成一个名为primes.c的文件,然后需要使用适合您平台的任何选项使用 C 编译器编译该文件以生成扩展模块(例如gcc)。

cythonize命令进行编译

cythonize -a -i yourmod.pyx

这将创建一个yourmod.c文件(或 C ++模式下的yourmod.cpp),对其进行编译,并将生成的扩展模块(.so.pyd,具体取决于您的平台)放在源文件旁边以进行直接导入(-i builds “in place””)。 -a 另外生成源代码的带注释的 html 文件。cythonize命令接受多个源文件和类似**/*.pyx的 glob 模式作为参数,并行构建作业的常用-j选项。在没有其他选项的情况下调用时,它只会将源文件转换为.c.cpp文件。传递-h标志以获取支持选项的完整列表。

distutils 构建 Cython 模块

def say_hello_to(name):print("Hello %s!" % name)

相应的setup.py脚本:

from distutils.core import setup
from Cython.Build import cythonize
setup(name='Hello world app',ext_modules=cythonize("hello.pyx"))

运行python setup.py build_ext --inplace。然后只需启动一个 Python 会话并执行from hello import say_hello_to并根据需要使用导入的函数。

如果您使用 setuptools 而不是 distutils,则需要注意,运行python setup.py install时的默认操作是创建一个压缩的egg文件,当您尝试从依赖包中使用它们时,这些文件无法与pxd文件一起用于pxd文件。为防止这种情况,请在setup()的参数中包含zip_safe=False

如果需要指定编译器选项,要链接的库或其他链接器选项,则需要手动创建Extension实例(请注意,glob 语法仍可用于在一行中指定多个扩展名):

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
extensions = [Extension("primes", ["primes.pyx"],include_dirs=[...],libraries=[...],library_dirs=[...]),# Everything but primes.pyx is included here.Extension("*", ["*.pyx"],include_dirs=[...],libraries=[...],library_dirs=[...]),
]
setup(name="My hello app",ext_modules=cythonize(extensions),

使用 setuptools 时,您应该在 Cython 之前导入它,因为 setuptools 可能会替换 distutils 中的Extension

Cythonize参数https://www.bookstack.cn/read/cython-doc-zh/docs-29.md

Cython.Build.cythonize(module_listexclude=Nonenthreads=0aliases=Nonequiet=Falseforce=Falselanguage=Noneexclude_failures=Falseshow_all_warnings=False**options)

  • module_list –作为模块列表,传递全局模式,全局模式列表或扩展对象列表。后者允许您通过常规distutils选项分别配置扩展。您还可以传递具有glob模式作为其源的Extension对象。然后,cythonize将解析该模式并为每个匹配文件创建扩展的副本。
  • exclude –当将glob模式传递作为module_list时,可以通过将某些模块名称传递到exclude选项中来显式排除某些模块名称。
  • nthreads –并行编译的并发构建数(需要multiprocessing模块)。
  • alias
  • quiet –如果为True,则Cython在编译过程中不会打印错误,警告或状态消息。
  • force –强制重新编译Cython模块,即使时间戳不表明需要重新编译也是如此。
  • language –要全局启用C ++模式,可以通过language='c++'。否则,这将基于编译器指令在每个文件级别确定。这仅影响基于文件名找到的模块。传入的扩展实例cythonize()将不会更改。建议使用编译器指令而不是此选项。# distutils: language = c++
  • exclude_failures –对于广泛的“尝试编译”模式,该模式将忽略编译失败并仅排除失败的扩展,请通过exclude_failures=True。请注意,这仅对编译.py文件有意义,这些文件也可以不经编译而使用。
  • show_all_warnings –默认情况下,并非所有Cython警告都会被打印。设置为true以显示所有警告。
  • annotate
  • compiler_directives -允许集合中的编译器指令setup.py是这样的:compiler_directives={'embedsignature': True} 。请参阅编译器指令https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#compiler-directives。

包中的多个 Cython 文件

要自动编译多个 Cython 文件而不显式列出所有这些文件,可以使用 glob 模式:

setup(ext_modules = cythonize("package/*.pyx")
)

cythonize()传递它们,也可以在Extension对象中使用 glob 模式:

extensions = [Extension("*", ["*.pyx"])]
setup(ext_modules = cythonize(extensions)
)

如果您有许多扩展并希望避免声明中的额外复杂性,您可以使用它们的正常 Cython 源声明它们,然后在不使用 Cython 时调用以下函数而不是cythonize()来调整 Extensions 中的源列表:

import os.path
def no_cythonize(extensions, **_ignore):for extension in extensions:sources = []for sfile in extension.sources:path, ext = os.path.splitext(sfile)if ext in ('.pyx', '.py'):if extension.language == 'c++':ext = '.cpp'else:ext = '.c'sfile = path + extsources.append(sfile)extension.sources[:] = sourcesreturn extensions

另一个选择是使 Cython 成为系统的设置依赖项,并使用 Cython 的 build_ext 模块作为构建过程的一部分运行

setup(setup_requires=['cython>=0.x',],extensions = [Extension("*", ["*.pyx"])],cmdclass={'build_ext': Cython.Build.build_ext},...
)

通过cmdclass,build_ext可以自定义,看一个pytorch版本中的fasterrcnn 源码的setup.py

# --------------------------------------------------------
# Fast R-CNN
# Copyright (c) 2015 Microsoft
# Licensed under The MIT License [see LICENSE for details]
# Written by Ross Girshick
# --------------------------------------------------------import os
from os.path import join as pjoin
import numpy as np
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonizedef find_in_path(name, path):"Find a file in a search path"# adapted fom http://code.activestate.com/recipes/52224-find-a-file-given-a-search-path/for dir in path.split(os.pathsep):binpath = pjoin(dir, name)if os.path.exists(binpath):return os.path.abspath(binpath)return Nonedef locate_cuda():"""Locate the CUDA environment on the systemReturns a dict with keys 'home', 'nvcc', 'include', and 'lib64'and values giving the absolute path to each directory.Starts by looking for the CUDAHOME env variable. If not found, everythingis based on finding 'nvcc' in the PATH."""# first check if the CUDAHOME env variable is in useif 'CUDAHOME' in os.environ:home = os.environ['CUDAHOME']nvcc = pjoin(home, 'bin', 'nvcc')else:# otherwise, search the PATH for NVCCdefault_path = pjoin(os.sep, 'usr', 'local', 'cuda', 'bin')nvcc = find_in_path('nvcc', os.environ['PATH'] + os.pathsep + default_path)if nvcc is None:raise EnvironmentError('The nvcc binary could not be ''located in your $PATH. Either add it to your path, or set $CUDAHOME')home = os.path.dirname(os.path.dirname(nvcc))cudaconfig = {'home': home, 'nvcc': nvcc,'include': pjoin(home, 'include'),'lib64': pjoin(home, 'lib64')}for k, v in cudaconfig.iteritems():if not os.path.exists(v):raise EnvironmentError('The CUDA %s path could not be located in %s' % (k, v))return cudaconfigCUDA = locate_cuda()# Obtain the numpy include directory.  This logic works across numpy versions.
try:numpy_include = np.get_include()
except AttributeError:numpy_include = np.get_numpy_include()def customize_compiler_for_nvcc(self):"""inject deep into distutils to customize how the dispatchto gcc/nvcc works.If you subclass UnixCCompiler, it's not trivial to get your subclassinjected in, and still have the right customizations (i.e.distutils.sysconfig.customize_compiler) run on it. So instead of goingthe OO route, I have this. Note, it's kindof like a wierd functionalsubclassing going on."""# tell the compiler it can processes .cuself.src_extensions.append('.cu')# save references to the default compiler_so and _comple methodsdefault_compiler_so = self.compiler_sosuper = self._compile# now redefine the _compile method. This gets executed for each# object but distutils doesn't have the ability to change compilers# based on source extension: we add it.def _compile(obj, src, ext, cc_args, extra_postargs, pp_opts):print extra_postargsif os.path.splitext(src)[1] == '.cu':# use the cuda for .cu filesself.set_executable('compiler_so', CUDA['nvcc'])# use only a subset of the extra_postargs, which are 1-1 translated# from the extra_compile_args in the Extension classpostargs = extra_postargs['nvcc']else:postargs = extra_postargs['gcc']super(obj, src, ext, cc_args, postargs, pp_opts)# reset the default compiler_so, which we might have changed for cudaself.compiler_so = default_compiler_so# inject our redefined _compile method into the classself._compile = _compile# run the customize_compiler
class custom_build_ext(build_ext):def build_extensions(self):customize_compiler_for_nvcc(self.compiler)build_ext.build_extensions(self)ext_modules = [Extension(        "model.utils.cython_bbox",      ["model/utils/bbox.pyx"],       extra_compile_args={'gcc': ["-Wno-cpp", "-Wno-unused-function"]},       include_dirs=[numpy_include]        ),Extension('pycocotools._mask',sources=['pycocotools/maskApi.c', 'pycocotools/_mask.pyx'],include_dirs = [np.get_include(), '../common'],extra_compile_args={'gcc': ['-Wno-cpp', '-Wno-unused-function', '-std=c99']},),
]setup(name='faster_rcnn',ext_modules=ext_modules,# inject our custom triggercmdclass={'build_ext': custom_build_ext},
)setup(name='pycocotools',packages=['pycocotools'],package_dir = {'pycocotools': 'pycocotools'},version='2.0',ext_modules=cythonize(ext_modules))

Cython + distutils/setuptools 构建

关于编写setup来变异cython程序,有两个模块distutils和setuptools,distutils属于python的标准库,setuptools在distutils基础上补充扩展,并且更新维护也比较快,所以setuptools应该替代distutils.在看很多项目源码里还是使用distutils,主要还是因为它是标准库吧,setuptools是三方库还要安装.distutils的官方文档也标注是遗留版本.(https://docs.python.org/zh-cn/3/distutils/index.html#distutils-index)

编写setup文件

,这里不记录怎么分发安装,简单了解下setup文件,用来编译cython

分发一个foo文件中包含的名为的模块foo.py

from distutils.core import setup
setup(name='foo',version='1.0',py_modules=['foo'],)
py_modules 需要的模块列表,模块是通过模块名称而不是文件名指定的,其他的是元数据(名称,版本号)等.

setup的参数

参数名称

类型

name

包的名字

字符串

version

包的版本号;参见 distutils.version

字符串

dcription

单行的包的描述

字符串

long_description

更长的包描述

字符串

author

包的作者

字符串

author_email

包的作者的电子邮件

字符串

maintainer

当前维护者的名称(如果不同于作者)。请注意,如果提供了维护程序,则distutils会将其用作作者PKG-INFO

字符串

maintenanceer_email

当前维护者的电子邮件地址,如果不同与作者

字符串

url

包的URL(主页)

字符串

download_url

包的下载地址

字符串

packages

distutils将操作的Python软件包列表

字符串列表

py_modules

distutils 会操作的 Python 模块列表

字符串列表

scripts

要构建和安装的独立脚本文件的列表

字符串列表

ext_modules

要构建的 Python 扩展的列表

类 distutils.core.Extension 的实例的列表

classifiers

包的类别列表

字符串列表;有效的分类器列在PyPI上。

distclass

要使用的类 Distribution 类

distutils.core.Distribution 的子类

script_name

setup.py 脚本名称 —— 默认为 sys.argv[0]

字符串

script_args

提供给安装脚本的参数

字符串列表

options

安装脚本的默认选项

字典

license

包的许可证

字符串

keywords

描述性元数据,请参阅 PEP 314

字符串列表或逗号分隔的字符串

platform

 

字符串列表或逗号分隔的字符串

cmdclass

命令名称到Command子类的映射

字典

datafile

要安装的数据文件列表

列表

package_dir

包到目录名称的映射

字典

#!/usr/bin/env pythonfrom distutils.core import setupsetup(name='Distutils',version='1.0',description='Python Distribution Utilities',author='Greg Ward',author_email='gward@python.net',url='https://www.python.org/sigs/distutils-sig/',packages=['distutils', 'distutils.command'],)

packages  按包分发,其他的还是元数据,其中name最好是一定要到,如果安装的话生成的文件会是指定name的名字.          Setup函数的packages参数是一个列表,其中包含了Distutils需要处理(构建、发布、安装等)的所有包。要实现此目的,那么包名和目录名必须能够相互对应,比如包名是distutils,则意味着在发布的根目录(setup脚本所在目录)下存在distutils子目录;再比如在setup脚本中packages = ['foo'],意味着要在setup脚本所在目录下存在相应的foo目录和foo/__init__.py文件。
package_dir选项可以改变这种默认的对应规则。package_dir是个字典,其中的key是要安装的包名,如果为空,则表明是root package,value就是该包(key)对应的源码树的目录。

package_dir = {'':'lib'},
packages = ['foo']

则必须在目录中存在lib子目录,lib/foo子目录,以及文件lib/foo/__init__.py。所以源码树如下

setup.py
lib/foo/__init__.pyfoo.py

另外一个例子,foo包对应lib目录,所以,foo.bar包就对应着lib/bar子目录。所以如果在setup.py中这么写:

package_dir = {'foo':'lib'},packages = ['foo','foo.bar']

则必须存在lib/__init__.py,  lib/bar/__init__.py文件。源码树如下:

setup.py
lib/__init__.pyfoo.pybar/__init__.pybar.py

Python扩展模块必须指定扩展名,源文件以及所有编译/链接要求(包括目录,要链接的库等),通过ext_modules

如果不需要对编译器/链接器的附加说明,则描述此扩展非常简单:

Extension('foo', ['foo.c'])
from distutils.core import setup, Extension
setup(name='foo',version='1.0',ext_modules=[Extension('foo', ['foo.c'])],)
Extension('foo', ['src/foo1.c', 'src/foo2.c'])和 Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])区别是生成的扩展名在文件系统中的位置
如果一个包下有多个扩展,而且要把这些扩展都放在统一的目录下,则可以使用ext_package关键字,比如下面的语句
setup(...,ext_package='pkg',ext_modules=[Extension('foo', ['src/foo.c']),Extension('subpkg.bar', ['src/bar.c'])])

如果扩展名include 在分发根目录下的目录中需要头文件,可以使用以下include_dirs选项

Extension('foo', ['foo.c'], include_dirs=['include'])

以使用define_macros和 undef_macros选项定义和取消定义预处理器宏

Extension(...,define_macros=[('NDEBUG', '1'),('HAVE_STRFTIME', None)],undef_macros=['HAVE_FOO', 'HAVE_BAR'])

等于在每个C源文件的顶部都包含此代码:

#define NDEBUG 1
#define HAVE_STRFTIME
#undef HAVE_FOO
#undef HAVE_BAR

指定在构建扩展时要链接到的库,以及用于搜索这些库的目录。该libraries选项是要链接的库列表,library_dirs是在链接时搜索库runtime_library_dirs的目录列表,并且是在运行时搜索共享(动态加载)库的目录列表。

Extension(...,libraries=['gdbm', 'readline'])

Extension参数

参数名称

类型

name

扩展名的全名,包括所有软件包- 不是文件名或路径名,而是Python点分名称

字符串

source

相对于分发根(安装脚本所在的位置)的源文件名列表,以Unix形式(以斜杠分隔)以实现可移植性。源文件可以是C,C ++,SWIG(.i),特定于平台的资源文件,也可以是build_ext命令识别为Python扩展源的任何其他文件 。

字符串列表

include_dirs

搜索C / C ++头文件的目录列表(以Unix形式提供可移植性)

字符串列表

define_macros

要定义的宏列表;每个宏都是使用2元组定义的,其中value是用来定义它的字符串,或者是在没有特定值的情况下定义它的字符串(等效 于source或 在Unix C编译器命令行上)(name, value)None#define FOO-DFOO

元组列表

undef_macros

要明确取消定义的宏列表

字符串列表

library_dirs

在链接时搜索C / C ++库的目录列表

字符串列表

libraries

要链接的库名列表(不是文件名或路径)

字符串列表

runtime_library_dirs

在运行时搜索C / C ++库的目录列表(对于共享扩展,这是在加载扩展时)

字符串列表

extra_objects

要链接的其他文件列表(例如,“源”未暗示的目标文件,必须明确指定的静态库,二进制资源文件等)

字符串列表

extra_compile_args

在“源”中编译源文件时要使用的任何其他特定于平台和编译器的信息。对于需要命令行的平台和编译器,这通常是命令行参数的列表,但是对于其他平台,可以是任何东西。

字符串列表

extra_link_args

将目标文件链接在一起以创建扩展名(或创建新的静态Python解释器)时使用的所有其他特定于平台和编译器的信息。与“ extra_compile_args”类似的解释。

字符串列表

export_symbols

从共享扩展名导出的符号列表。并非在所有平台上都使用,对于Python扩展通常也不是必需的,Python扩展通常只导出一个符号: init+ extension_name。

字符串列表

depends

扩展名依赖的文件列表

字符串列表

language

扩展语言(即 'c''c++', 'objc')。如果未提供,将从源扩展中检测到。

字符串

optional

指定扩展中的构建失败不应中止构建过程,而只是跳过扩展。

布尔

Distutils简单示例 https://docs.python.org/zh-cn/3/distutils/examples.html

扩展 Distutils https://docs.python.org/zh-cn/3/distutils/extending.html

Distutils 可以通过各种方式扩展。 大多数扩展都采用新命令或现有命令的替换形式。 例如,可以编写新命令以支持新的特定于平台的包格式,但是可以修改现有命令的替换,以修改命令在包上的操作细节。

distutils 的大多数扩展都在想要修改现有命令的 setup.py 脚本中编写;其中许多只是简单地在 .py 文件以外添加了一些应当被拷贝到包中的文件后缀以便使用。

大多部 distutils 命令的实现都是 distutils.cmd.Command 类的子类。 新增命令可直接继承自 Command,而替换命令往往间接派生自 Command, 直接子类化它们所替换的命令。 所有命令都要求自 Command 派生。

from distutils.command.build_py import build_py as _build_py
from distutils.core import setupclass build_py(_build_py):"""Specialized Python source builder."""# implement whatever needs to be different...setup(cmdclass={'build_py': build_py},...)

Setuptools文档 https://setuptools.readthedocs.io/en/latest/index.html

cython代码编译和setup.py文件编写相关推荐

  1. python的setup.py文件及其常用命令

    python的setup.py文件及其常用命令 上传者:tingting1718      我也要"分享赚钱" 2014/7/7 关注(286) 评论(0) 声明:此内容仅代表网友 ...

  2. python打不开py文件查看代码,用python打开py文件

    .py文件无法用python打开 刚刚把python更新到python3.7.2 但是发现之前写的.py的文件双击没有任何我去,你的情况和我一模一样,我也是环境变量和注册表按照网上的方法设置了,却还是 ...

  3. java 0000 0001 0002 000a 000b_Java代码编译后的class文件

    还是熟悉的味道,还是最简单的代码. // Hello.java public class Hello { public static void main(String[] args) { System ...

  4. python文件中的代码调用以及“__init__.py“ 文件说明

    python文件中的代码调用以及"__init__.py" 文件说明 如何调用另一个python文件中的代码 同一文件目录下 不同文件目录下 "__init__.py&q ...

  5. python编译:setup.py添加.h头文件或者库的搜索路径

    通过源码安装python第三方库时,经常会出现该库依赖的c/c++头文件.库找不到的情况,特别是自己编译安装的c/c++库时.比如leveldb,mysql等等. 我编译图形学的一个库就出现了问题. ...

  6. python打包安装-setup.py文件的使用(不能用pip安装时推荐)

    编写python的第三方库,最终要的一个工作就是编写setup.py了,其实如果我们下载过一些第三库的源代码文件,打开之后一般就会有一个setup.py,执行python setup.py insta ...

  7. Python 库打包分发(setup.py 编写)

    Python 有非常丰富的第三方库可以使用,很多开发者会向 pypi 上提交自己的 Python 包.要想向 pypi 包仓库提交自己开发的包,首先要将自己的代码打包,才能上传分发. distutil ...

  8. 详解Python中的setup.py

    软硬件环境 ubuntu 18.04 64bit anaconda with python 3.6 setup.py 前言 科技发展到今日,软件开发已经变得越来越复杂,再也不是单单靠一个人的力量就能够 ...

  9. 【python基础知识】调用C++接口(setup.py运行)出现的各种问题

    1.setup.py文件编写 网上下载的C++程序的Python接口,不会使用,全靠摸索.根据最近出现的问题,写下其中的道道,避免下一次抓狂. setup.py文件内容如下: from distuti ...

最新文章

  1. 2019.2.7 区块链论文翻译
  2. 『HTML5制造仿JQuery作用』减速
  3. Drupal7 将到2021年11月结束支持,请注意升级
  4. EL之AdaBoost:集成学习之AdaBoost算法的简介、应用、经典案例之详细攻略
  5. 3.通过现有的PDB创建一个新的PDB
  6. 数字化时代在线教育行业营销解决方案
  7. 云栖日报丨收购中天微,阿里芯了解一下!
  8. 解决idea中找不到程序包和找不到符号的问题
  9. linux随手笔记(Centos为主)
  10. 企业网站内容维护日常工作有哪些
  11. Elasticseach api keys are not enabled
  12. Unity3D 关于材质球自发光_EmissionColor参数,构建后无法动态修改颜色值解决办法
  13. ANDROID_MARS学习笔记_S04_004_用HTTPCLENT发带参数的get和post请求
  14. adb检测不到模拟器的解决方法
  15. Warm-up pytorch代码
  16. 飞书:远程办公更轻松
  17. linux查看告警日志,linux怎么查看硬件告警
  18. 电脑和手机如何将PPT转成PDF?
  19. 【ISO14229_UDS诊断】-11.11-$19服务sub-function = 0x0A reportSupportedDTC
  20. 麦当劳中国内地第4000家餐厅开业;圣培露携手心灵厨房打造更可持续的未来 | 知消...

热门文章

  1. Failing because I am unlikely to write too排查方案
  2. 解决GoPro Quik频繁自动登出的问题
  3. js正则表达式验证字符串只包括大小写字母下划线和-
  4. Jsonviewer2 for Notepad++ 64 bit
  5. hello树先生经典台词
  6. html5首字母大小写,css中如何设置英文首字母大写
  7. 某资讯App signature签名分析 (一)
  8. 用python把图片换成蓝底_详解Python给照片换底色(蓝底换红底)
  9. Win10系统Edge可以上网其他浏览器不能上网怎么回事
  10. qt项目在Linux平台上面发布成可执行程序.run