提交 d28d5832 编写于 作者: W wizardforcel

init

上级
# Cython 3.0 中文文档
来源: [http://docs.cython.org/en/latest/](http://docs.cython.org/en/latest/)
\ No newline at end of file
+ [Cython 3.0 中文文档](README.md)
+ [入门](docs/2.md)
+ [Cython - 概述](docs/3.md)
+ [安装 Cython](docs/4.md)
+ [构建 Cython 代码](docs/5.md)
+ [通过静态类型更快的代码](docs/6.md)
+ [Tutorials](docs/7.md)
+ [基础教程](docs/8.md)
+ [调用 C 函数](docs/9.md)
+ [使用 C 库](docs/10.md)
+ [扩展类型(又名.cdef 类)](docs/11.md)
+ [pxd 文件](docs/12.md)
+ [Caveats](docs/13.md)
+ [Profiling](docs/14.md)
+ [Unicode 和传递字符串](docs/15.md)
+ [内存分配](docs/16.md)
+ [纯 Python 模式](docs/17.md)
+ [使用 NumPy](docs/18.md)
+ [使用 Python 数组](docs/19.md)
+ [进一步阅读](docs/20.md)
+ [相关工作](docs/21.md)
+ [附录:在 Windows 上安装 MinGW](docs/22.md)
+ [用户指南](docs/23.md)
+ [语言基础](docs/24.md)
+ [扩展类型](docs/25.md)
+ [扩展类型的特殊方法](docs/26.md)
+ [在 Cython 模块之间共享声明](docs/27.md)
+ [与外部 C 代码连接](docs/28.md)
+ [源文件和编译](docs/29.md)
+ [早期绑定速度](docs/30.md)
+ [在 Cython 中使用 C ++](docs/31.md)
+ [融合类型(模板)](docs/32.md)
+ [将 Cython 代码移植到 PyPy](docs/33.md)
+ [Limitations](docs/34.md)
+ [Cython 和 Pyrex 之间的区别](docs/35.md)
+ [键入的内存视图](docs/36.md)
+ [实现缓冲协议](docs/37.md)
+ [使用并行性](docs/38.md)
+ [调试你的 Cython 程序](docs/39.md)
+ [用于 NumPy 用户的 Cython](docs/40.md)
+ [Pythran 作为 Numpy 后端](docs/41.md)
# Cython 3.0 文档
来源: [http://docs.cython.org/en/latest/](http://docs.cython.org/en/latest/)
\ No newline at end of file
此差异已折叠。
# 扩展类型(又名.cdef 类)
> 原文: [http://docs.cython.org/en/latest/src/tutorial/cdef_classes.html](http://docs.cython.org/en/latest/src/tutorial/cdef_classes.html)
为了支持面向对象的编程,Cython 支持编写与 Python 完全相同的普通 Python 类:
```
class MathFunction(object):
def __init__(self, name, operator):
self.name = name
self.operator = operator
def __call__(self, *operands):
return self.operator(*operands)
```
然而,基于 Python 所谓的“内置类型”,Cython 支持第二种类:_ 扩展类型 _,由于用于声明的关键字,有时也称为“cdef 类”。与 Python 类相比,它们受到一定限制,但通常比通用 Python 类更高效,速度更快。主要区别在于它们使用 C 结构来存储它们的字段和方法而不是 Python 字典。这允许他们在他们的字段中存储任意 C 类型,而不需要 Python 包装器,并且可以直接在 C 级访问字段和方法,而无需通过 Python 字典查找。
普通的 Python 类可以从 cdef 类继承,但不能从其他方面继承。 Cython 需要知道完整的继承层次结构,以便布局它们的 C 结构,并将其限制为单继承。另一方面,普通的 Python 类可以从 Cython 代码和纯 Python 代码中继承任意数量的 Python 类和扩展类型。
到目前为止,我们的集成示例并不是非常有用,因为它只集成了一个硬编码函数。为了解决这个问题,我们将使用 cdef 类来表示浮点数上的函数:
```
cdef class Function:
cpdef double evaluate(self, double x) except *:
return 0
```
指令 cpdef 提供了两种版本的方法;一个快速使用从 Cython 和一个较慢的使用从 Python。然后:
```
from libc.math cimport sin
cdef class Function:
cpdef double evaluate(self, double x) except *:
return 0
cdef class SinOfSquareFunction(Function):
cpdef double evaluate(self, double x) except *:
return sin(x ** 2)
```
这比为 cdef 方法提供 python 包装稍微多一点:与 cdef 方法不同,cpdef 方法可以被 Python 子类中的方法和实例属性完全覆盖。与 cdef 方法相比,它增加了一点调用开销。
为了使类定义对其他模块可见,从而允许在实现它们的模块之外进行有效的 C 级使用和继承,我们在`sin_of_square.pxd`文件中定义它们:
```
cdef class Function:
cpdef double evaluate(self, double x) except *
cdef class SinOfSquareFunction(Function):
cpdef double evaluate(self, double x) except *
```
使用它,我们现在可以更改我们的集成示例:
```
from sin_of_square cimport Function, SinOfSquareFunction
def integrate(Function f, double a, double b, int N):
cdef int i
cdef double s, dx
if f is None:
raise ValueError("f cannot be None")
s = 0
dx = (b - a) / N
for i in range(N):
s += f.evaluate(a + i * dx)
return s * dx
print(integrate(SinOfSquareFunction(), 0, 1, 10000))
```
这几乎与前面的代码一样快,但是由于可以更改集成功能,因此它更加灵活。我们甚至可以传入 Python 空间中定义的新函数:
```
>>> import integrate
>>> class MyPolynomial(integrate.Function):
... def evaluate(self, x):
... return 2*x*x + 3*x - 10
...
>>> integrate(MyPolynomial(), 0, 1, 10000)
-7.8335833300000077
```
这比原始的仅使用 Python 的集成代码慢大约 20 倍,但仍然快 10 倍。这显示了当整个循环从 Python 代码移动到 Cython 模块时,加速可以很容易地变大。
关于`evaluate`新实施的一些注意事项:
> * 此处的快速方法调度仅起作用,因为`Function`中声明了`evaluate`。如果在`SinOfSquareFunction`中引入`evaluate`,代码仍然可以工作,但 Cython 会使用较慢的 Python 方法调度机制。
> * 以同样的方式,如果参数`f`没有被输入,但只是作为 Python 对象传递,那么将使用较慢的 Python 调度。
> * 由于参数是打字的,我们需要检查它是否是`None`。在 Python 中,当查找`evaluate`方法时,这会导致`AttributeError`,但 Cython 会尝试访问`None`的(不兼容的)内部结构,就像它是`Function`一样,导致崩溃或数据损坏。
有一个 _ 编译器指令 _ `nonecheck`,它会以降低速度为代价启用此检查。以下是编译器指令用于动态打开或关闭`nonecheck`的方法:
```
# cython: nonecheck=True
# ^^^ Turns on nonecheck globally
import cython
cdef class MyClass:
pass
# Turn off nonecheck locally for the function
@cython.nonecheck(False)
def func():
cdef MyClass obj = None
try:
# Turn nonecheck on again for a block
with cython.nonecheck(True):
print(obj.myfunc()) # Raises exception
except AttributeError:
pass
print(obj.myfunc()) # Hope for a crash!
```
cdef 类中的属性与常规类中的属性的行为不同:
> * 所有属性必须在编译时预先声明
> * 属性默认只能从用 Cython 访问(类型化访问)
> * 属性可以声明为暴露的 Python 空间的动态属性
```
from sin_of_square cimport Function
cdef class WaveFunction(Function):
# Not available in Python-space:
cdef double offset
# Available in Python-space:
cdef public double freq
# Available in Python-space, but only for reading:
cdef readonly double scale
# Available in Python-space:
@property
def period(self):
return 1.0 / self.freq
@period.setter
def period(self, value):
self.freq = 1.0 / value
```
\ No newline at end of file
# pxd 文件
> 原文: [http://docs.cython.org/en/latest/src/tutorial/pxd_files.html](http://docs.cython.org/en/latest/src/tutorial/pxd_files.html)
除了`.pyx`源文件之外,Cython 还使用`.pxd`文件,它们的工作方式类似于 C 头文件 - 它们包含 Cython 声明(有时是代码部分),仅供 Cython 模块使用。使用`cimport`关键字将`pxd`文件导入`pyx`模块。
`pxd`文件有很多用例:
> 1. 它们可用于共享外部 C 声明。
>
>
> 2. 它们可以包含非常适合 C 编译器内联的函数。这些功能应标记为`inline`,例如:
>
>
>
> ```
> cdef inline int int_min(int a, int b):
> return b if b < a else a
>
> ```
>
>
> 3. 当附带同名的`pyx`文件时,它们为 Cython 模块提供了一个 Cython 接口,以便其他 Cython 模块可以使用比 Python 更高效的协议与之通信。
在我们的集成示例中,我们可能会将其分解为`pxd`文件,如下所示:
> 1. 添加`cmath.pxd`功能,定义 C `math.h`头文件中可用的 C 功能,如`sin`。然后人们只需在`integrate.pyx`中做`from cmath cimport sin`。
>
>
> 2. 添加`integrate.pxd`,以便用 Cython 编写的其他模块可以定义要集成的快速自定义函数。
>
>
>
> ```
> cdef class Function:
> cpdef evaluate(self, double x)
> cpdef integrate(Function f, double a,
> double b, int N)
>
> ```
>
>
>
> 请注意,如果您的 cdef 类具有属性,则必须在类声明`pxd`文件(如果使用)中声明属性,而不是`pyx`文件。编译器会告诉你这个。
\ No newline at end of file
# Caveats
> 原文: [http://docs.cython.org/en/latest/src/tutorial/caveats.html](http://docs.cython.org/en/latest/src/tutorial/caveats.html)
由于 Cython 混合了 C 语言和 Python 语义,因此有些事情可能会有点令人惊讶或不直观。对于 Python 用户来说,工作总是让 Cython 更自然,因此这个列表将来可能会发生变化。
> * 给定两个类型`int`变量`a`和`b`,`a % b`与第二个参数(遵循 Python 语义)具有相同的符号,而不是与第一个符号相同(如在 C)。通过启用 cdivision 指令(Cython 0.12 之前的版本始终遵循 C 语义),可以在某种速度增益下获得 C 行为。
> * 无条件类型需要小心。`cdef unsigned n = 10; print(range(-n, n))`将打印一个空列表,因为`-n`在传递给`range`函数之前回绕到一个大的正整数。
> * Python 的`float`类型实际上包含了 C `double`值,而 Python 2.x 中的`int`类型包含了 C `long`值。
\ No newline at end of file
# Profiling
> 原文: [http://docs.cython.org/en/latest/src/tutorial/profiling_tutorial.html](http://docs.cython.org/en/latest/src/tutorial/profiling_tutorial.html)
这部分描述了 Cython 的性能分析能力。如果您熟悉纯 Python 代码的分析,则只能阅读第一部分( [Cython Profiling Basics](#profiling-basics))。如果您不熟悉 Python 分析,您还应该阅读教程( [分析教程](#profiling-tutorial) ),它将逐步引导您完成一个完整的示例。
## Cython Profiling Basics
Cython 中的分析由编译器指令控制。它可以通过 Cython 装饰器设置为整个文件或基于每个功能。
### 启用完整源文件的分析
通过全局指令将完整源文件的分析启用到文件顶部的 Cython 编译器:
```
# cython: profile=True
```
请注意,分析会给每个函数调用带来轻微的开销,因此会使程序变慢(或者很多,如果你经常调用一些小函数)。
启用后,从 cProfile 模块调用时,您的 Cython 代码将像 Python 代码一样运行。这意味着您可以使用与仅 Python 代码相同的工具,将您的 Cython 代码与 Python 代码一起分析。
### 禁用分析功能
如果您的分析因为某些小功能的调用开销而变得混乱,而您希望在您的配置文件中看不到这些功能 - 无论是因为您计划内联它们还是因为您确定不能让它们更快 - 你可以使用一个特殊的装饰器来禁用一个函数的分析(无论它是否全局启用):
```
cimport cython
@cython.profile(False)
def my_often_called_function():
pass
```
### 启用行跟踪
要获得更详细的跟踪信息(对于可以使用它的工具),您可以启用行跟踪:
```
# cython: linetrace=True
```
这也将启用分析支持,因此不需要上面的`profile=True`选项。例如,覆盖率分析需要线跟踪。
请注意,即使通过编译器指令启用了行跟踪,默认情况下也不会使用它。由于运行时减速可能很大,因此必须由 C 编译器通过设置 C 宏定义`CYTHON_TRACE=1`进行编译。要在跟踪中包含 nogil 函数,请设置`CYTHON_TRACE_NOGIL=1`(表示`CYTHON_TRACE=1`)。可以在`setup.py`脚本的扩展定义中定义 C 宏,也可以使用以下文件头注释设置源文件中的相应 distutils 选项(如果`cythonize()`用于编译):
```
# distutils: define_macros=CYTHON_TRACE_NOGIL=1
```
### 启用覆盖率分析
从 Cython 0.23 开始,线跟踪(见上文)也支持使用 [coverage.py](https://coverage.readthedocs.io/) 工具报告覆盖率报告。要使覆盖率分析了解 Cython 模块,还需要在`.coveragerc`文件中启用 Cython 的 coverage 插件,如下所示:
```
[run]
plugins = Cython.Coverage
```
使用此插件,您的 Cython 源文件应该在 coverage 报告中正常显示。
要将覆盖率报告包含在 Cython 带注释的 HTML 文件中,您需要首先运行 coverage.py 工具以生成 XML 结果文件。将此文件传递到`cython`命令,如下所示:
```
$ cython --annotate-coverage coverage.xml package/mymodule.pyx
```
这将重新编译 Cython 模块并在其处理的每个 Cython 源文件旁边生成一个 HTML 输出文件,其中包含 coverage 报告中包含的行的颜色标记。
## 分析教程
这将是一个完整的教程,从头到尾,分析 Python 代码,将其转换为 Cython 代码并保持分析直到它足够快。
作为一个玩具示例,我们想要评估平方倒数的总和,直到某个整数![n](img/8d2295f5f8a692b61ccf0b86f3676a21.jpg)来评估![\pi](img/5ff0026ef5efcc1b4ba5ac568d19d36e.jpg)。我们想要使用的关系已经由欧拉在 1735 年证明并被称为[巴塞尔问题](https://en.wikipedia.org/wiki/Basel_problem)
![\pi^2 = 6 \sum_{k=1}^{\infty} \frac{1}{k^2} =
6 \lim_{k \to \infty} \big( \frac{1}{1^2} +
\frac{1}{2^2} + \dots + \frac{1}{k^2} \big) \approx
6 \big( \frac{1}{1^2} + \frac{1}{2^2} + \dots + \frac{1}{n^2} \big)](img/44708a158fc789b494439489536fcd11.jpg)
用于评估截断总和的简单 Python 代码如下所示:
```
# calc_pi.py
def recip_square(i):
return 1. / i ** 2
def approx_pi(n=10000000):
val = 0.
for k in range(1, n + 1):
val += recip_square(k)
return (6 * val) ** .5
```
在我的盒子上,这需要大约 4 秒来运行默认 n 的函数。我们选择 n 越高,![\pi](img/5ff0026ef5efcc1b4ba5ac568d19d36e.jpg)的近似值越好。经验丰富的 Python 程序员已经看到很多地方可以优化这段代码。但请记住优化的黄金法则:永不优化而不进行分析。让我重复一遍:**从不**优化而不分析您的代码。您对代码的哪一部分花费太多时间的想法是错误的。至少,我的总是错的。所以让我们编写一个简短的脚本来分析我们的代码:
```
# profile.py
import pstats, cProfile
import calc_pi
cProfile.runctx("calc_pi.approx_pi()", globals(), locals(), "Profile.prof")
s = pstats.Stats("Profile.prof")
s.strip_dirs().sort_stats("time").print_stats()
```
在我的盒子上运行它给出以下输出:
```
Sat Nov 7 17:40:54 2009 Profile.prof
10000004 function calls in 6.211 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 3.243 3.243 6.211 6.211 calc_pi.py:7(approx_pi)
10000000 2.526 0.000 2.526 0.000 calc_pi.py:4(recip_square)
1 0.442 0.442 0.442 0.442 {range}
1 0.000 0.000 6.211 6.211 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
它包含代码在 6.2 CPU 秒内运行的信息。请注意,代码慢了 2 秒,因为它在 cProfile 模块中运行。该表包含真正有价值的信息。您可能需要查看 Python [分析文档](https://docs.python.org/library/profile.html)以获取详细信息。这里最重要的列是 totime(在此函数中花费的总时间**而不是**计算由此函数调用的函数)和 cumtime(在此函数中花费的总时间**也计算所谓的函数**通过这个功能)。查看 tottime 列,我们看到大约一半的时间花在 approx_pi 上,另一半花在 recip_square 上。还有半秒钟在范围内度过......当然我们应该使用 xrange 进行如此大的迭代。实际上,只需将范围更改为 xrange 就可以在 5.8 秒内运行代码。
我们可以在纯 Python 版本中进行优化,但由于我们对 Cython 感兴趣,让我们继续前进并将此模块带到 Cython。我们无论如何都会这样做,以使循环运行得更快。这是我们的第一个 Cython 版本:
```
# cython: profile=True
# calc_pi.pyx
def recip_square(int i):
return 1. / i ** 2
def approx_pi(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square(k)
return (6 * val) ** .5
```
注意第一行:我们必须告诉 Cython 应该启用性能分析。这使得 Cython 代码稍慢,但如果没有这个,我们将无法从 cProfile 模块获得有意义的输出。其余代码大部分都没有改变,我只输入了一些可能会加快速度的变量。
我们还需要修改我们的分析脚本以直接导入 Cython 模块。这是添加 [Pyximport](../userguide/source_files_and_compilation.html#pyximport)模块导入的完整版本:
```
# profile.py
import pstats, cProfile
import pyximport
pyximport.install()
import calc_pi
cProfile.runctx("calc_pi.approx_pi()", globals(), locals(), "Profile.prof")
s = pstats.Stats("Profile.prof")
s.strip_dirs().sort_stats("time").print_stats()
```
我们只添加了两行,其余的完全相同。或者,我们也可以手动将代码编译成扩展名;我们根本不需要更改配置文件脚本。该脚本现在输出以下内容:
```
Sat Nov 7 18:02:33 2009 Profile.prof
10000004 function calls in 4.406 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 3.305 3.305 4.406 4.406 calc_pi.pyx:7(approx_pi)
10000000 1.101 0.000 1.101 0.000 calc_pi.pyx:4(recip_square)
1 0.000 0.000 4.406 4.406 {calc_pi.approx_pi}
1 0.000 0.000 4.406 4.406 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
我们增加了 1.8 秒。不是太寒酸。将输出与前一个进行比较,我们看到 recip_square 函数变得更快,而 approx_pi 函数没有发生很大变化。让我们更专注于 recip_square 函数。首先请注意,不要从我们模块之外的代码调用此函数;所以将它变成 cdef 以减少呼叫开销是明智的。我们也应该摆脱幂运算符:它变成了 Cython 的 pow(i,2)函数调用,但我们可以改为编写 i * i,这可能更快。整个功能也是内联的良好候选者。让我们看看这些想法的必要变化:
```
# cython: profile=True
# calc_pi.pyx
cdef inline double recip_square(int i):
return 1. / (i * i)
def approx_pi(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square(k)
return (6 * val) ** .5
```
现在运行配置文件脚本会产生:
```
Sat Nov 7 18:10:11 2009 Profile.prof
10000004 function calls in 2.622 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.782 1.782 2.622 2.622 calc_pi.pyx:7(approx_pi)
10000000 0.840 0.000 0.840 0.000 calc_pi.pyx:4(recip_square)
1 0.000 0.000 2.622 2.622 {calc_pi.approx_pi}
1 0.000 0.000 2.622 2.622 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
那又给我们买了 1.8 秒。不是我们可以期待的戏剧性变化。为什么 recip_square 仍然在这个表中;应该是内联的,不是吗?这样做的原因是,即使消除了函数调用,Cython 仍会生成分析代码。让我们告诉它不再介绍 recip_square;无论如何,我们无法使功能更快:
```
# cython: profile=True
# calc_pi.pyx
cimport cython
@cython.profile(False)
cdef inline double recip_square(int i):
return 1. / (i * i)
def approx_pi(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square(k)
return (6 * val) ** .5
```
运行它显示了一个有趣的结果:
```
Sat Nov 7 18:15:02 2009 Profile.prof
4 function calls in 0.089 CPU seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.089 0.089 0.089 0.089 calc_pi.pyx:10(approx_pi)
1 0.000 0.000 0.089 0.089 {calc_pi.approx_pi}
1 0.000 0.000 0.089 0.089 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
首先要注意的是速度的巨大提升:这个版本只占我们第一个 Cython 版本的 1/50。另请注意,recip_square 已经从我们想要的表中消失了。但最奇特和重要的变化是,about_pi 也变得更快。这是所有分析的问题:在配置文件运行中调用函数会给函数调用增加一定的开销。这个开销是**而不是**添加到被调用函数所花费的时间,而是加到**调用**函数所花费的时间。在这个例子中,在最后一次运行中,approx_pi 不需要 2.622 秒;但它调用了 recip_square 10000000 次,每次都需要稍微设置一下它的分析。这相当于大约 2.6 秒的大量时间损失。禁用经常调用的函数的分析现在揭示了 approx_pi 的实际时间;如果需要,我们现在可以继续优化它。
这个分析教程到此结束。此代码仍有一些改进空间。我们可以尝试用 C stdlib 中的 sqrt 调用替换 approx_pi 中的幂运算符;但这并不一定比调用 pow(x,0.5)更快。
即便如此,我们在这里取得的成果非常令人满意:我们提出了一个比原始 Python 版本快得多的解决方案,同时保留了功能和可读性。
\ No newline at end of file
此差异已折叠。
# 内存分配
> 原文: [http://docs.cython.org/en/latest/src/tutorial/memory_allocation.html](http://docs.cython.org/en/latest/src/tutorial/memory_allocation.html)
动态内存分配在 Python 中大多不是问题。一切都是对象,引用计数系统和垃圾收集器在不再使用时自动将内存返回给系统。
当谈到更多低级数据缓冲区时,Cython 通过 NumPy,内存视图或 Python 的 stdlib 数组类型特别支持简单类型的(多维)数组。它们是全功能的,垃圾收集的,比 C 中的裸指针更容易使用,同时仍然保持速度和静态类型的好处。参见 [使用 Python 数组](array.html#array-array)[类型记忆视图](../userguide/memoryviews.html#memoryviews)
但是,在某些情况下,这些对象仍然会产生不可接受的开销,这可能会导致在 C 中进行手动内存管理。
简单的 C 值和结构(例如局部变量`cdef double x`)通常在堆栈上分配并通过值传递,但对于更大和更复杂的对象(例如动态大小的双精度列表),必须手动请求内存并释放。 C 为此提供函数`malloc()``realloc()``free()`,可以从`clibc.stdlib`导入到 cython 中。他们的签名是:
```
void* malloc(size_t size)
void* realloc(void* ptr, size_t size)
void free(void* ptr)
```
malloc 使用的一个非常简单的示例如下:
|
```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
```
|
```
import random
from libc.stdlib cimport malloc, free
def random_noise(int number=1):
cdef int i
# allocate number * sizeof(double) bytes of memory
cdef double *my_array = &lt;double *&gt; malloc(number * sizeof(double))
if not my_array:
raise MemoryError()
try:
ran = random.normalvariate
for i in range(number):
my_array[i] = ran(0, 1)
# ... let's just assume we do some more heavy C calculations here to make up
# for the work that it takes to pack the C double values into Python float
# objects below, right after throwing away the existing objects above.
return [x for x in my_array[:number]]
finally:
# return the previously allocated memory to the system
free(my_array)
```
|
请注意,用于在 Python 堆上分配内存的 C-API 函数通常比上面的低级 C 函数更受欢迎,因为它们提供的内存实际上是在 Python 的内部内存管理系统中考虑的。它们还对较小的内存块进行了特殊优化,通过避免代价高昂的操作系统调用来加速其分配。
可以在`cpython.mem`标准声明文件中找到 C-API 函数:
```
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
```
它们的接口和用法与相应的低级 C 函数相同。
需要记住的一件重要事情是,`malloc()`[`PyMem_Malloc()`](https://docs.python.org/3/c-api/memory.html#c.PyMem_Malloc "(in Python v3.7)") _ 获得的内存块必须手动释放 _,并相应调用`free()`[`PyMem_Free()`](https://docs.python.org/3/c-api/memory.html#c.PyMem_Free "(in Python v3.7)") 当它们不再使用时(_ 必须 _ 总是使用匹配类型的自由函数)。否则,在 python 进程退出之前,它们不会被回收。这称为内存泄漏。
如果一块内存需要比`try..finally`块可以管理的更长的生命周期,另一个有用的习惯是将其生命周期与 Python 对象联系起来以利用 Python 运行时的内存管理,例如:
```
from cpython.mem cimport PyMem_Malloc, PyMem_Realloc, PyMem_Free
cdef class SomeMemory:
cdef double* data
def __cinit__(self, size_t number):
# allocate some memory (uninitialised, may contain arbitrary data)
self.data = <double*> PyMem_Malloc(number * sizeof(double))
if not self.data:
raise MemoryError()
def resize(self, size_t new_number):
# Allocates new_number * sizeof(double) bytes,
# preserving the current content and making a best-effort to
# re-use the original data location.
mem = <double*> PyMem_Realloc(self.data, new_number * sizeof(double))
if not mem:
raise MemoryError()
# Only overwrite the pointer if the memory was really reallocated.
# On error (mem is NULL), the originally memory has not been freed.
self.data = mem
def __dealloc__(self):
PyMem_Free(self.data) # no-op if self.data is NULL
```
\ No newline at end of file
# 纯 Python 模式
> 原文: [http://docs.cython.org/en/latest/src/tutorial/pure.html](http://docs.cython.org/en/latest/src/tutorial/pure.html)
在某些情况下,需要加速 Python 代码而不会失去使用 Python 解释器运行它的能力。虽然可以使用 Cython 编译纯 Python 脚本,但通常只能获得大约 20%-50%的速度增益。
为了超越这一点,Cython 提供了语言结构,为 Python 模块添加静态类型和 cythonic 功能,使其在编译时运行得更快,同时仍允许对其进行解释。这是通过增加`.pxd`文件,通过 Python 类型注释(在 [PEP 484](https://www.python.org/dev/peps/pep-0484/)[PEP 526](https://www.python.org/dev/peps/pep-0526/) 之后)和/或通过导入魔法后可用的特殊函数和装饰器来实现的`cython`模块。尽管项目通常会决定使静态类型信息易于管理的特定方式,但所有这三种方式都可以根据需要进行组合。
虽然通常不建议在`.pyx`文件中编写直接的 Cython 代码,但有正当理由这样做 - 更容易测试和调试,与纯 Python 开发人员协作等。在纯模式下,您或多或少地受限于可以在 Python 中表达(或至少模拟)的代码,以及静态类型声明。除此之外的任何事情都只能在扩展语言语法的.pyx 文件中完成,因为它取决于 Cython 编译器的功能。
## 增加.pxd
使用扩充`.pxd`可以让原始`.py`文件完全不受影响。另一方面,需要保持`.pxd``.py`以使它们保持同步。
虽然`.pyx`文件中的声明必须与具有相同名称的`.pxd`文件的声明完全对应(并且任何矛盾导致编译时错误,请参阅 [pxd 文件](pxd_files.html) ) ,`.py`文件中的无类型定义可以通过`.pxd`中存在的更具体的类型覆盖并使用静态类型进行扩充。
如果找到与正在编译的`.py`文件同名的`.pxd`文件,将搜索 [`cdef`](../userguide/language_basics.html#cdef) 类和 [`cdef`](../userguide/language_basics.html#cdef) / [`cpdef`](../userguide/language_basics.html#cpdef) 的功能和方法。然后,编译器将`.py`文件中的相应类/函数/方法转换为声明的类型。因此,如果有一个文件`A.py`
```
def myfunction(x, y=2):
a = x - y
return a + x * y
def _helper(a):
return a + 1
class A:
def __init__(self, b=0):
self.a = 3
self.b = b
def foo(self, x):
print(x + _helper(1.0))
```
并添加`A.pxd`
```
cpdef int myfunction(int x, int y=*)
cdef double _helper(double a)
cdef class A:
cdef public int a, b
cpdef foo(self, double x)
```
然后 Cython 将编译`A.py`,就像它编写如下:
```
cpdef int myfunction(int x, int y=2):
a = x - y
return a + x * y
cdef double _helper(double a):
return a + 1
cdef class A:
cdef public int a, b
def __init__(self, b=0):
self.a = 3
self.b = b
cpdef foo(self, double x):
print(x + _helper(1.0))
```
注意为了向`.pxd`中的定义提供 Python 包装器,即可以从 Python 访问,
* Python 可见函数签名必须声明为 &lt;cite&gt;cpdef&lt;/cite&gt; (默认参数替换为 &lt;cite&gt;*&lt;/cite&gt; 以避免重复):
```
cpdef int myfunction(int x, int y=*)
```
* 内部函数的 C 函数签名可以声明为 &lt;cite&gt;cdef&lt;/cite&gt;
```
cdef double _helper(double a)
```
* &lt;cite&gt;cdef&lt;/cite&gt; 类(扩展类型)声明为 &lt;cite&gt;cdef 类&lt;/cite&gt;;
* &lt;cite&gt;cdef&lt;/cite&gt; 类属性必须声明为 &lt;cite&gt;cdef public&lt;/cite&gt; 如果需要读/写 Python 访问, &lt;cite&gt;cdef readonly&lt;/cite&gt; 用于只读 Python 访问,或普通 &lt;cite&gt;cdef&lt;/cite&gt; 用于内部 C 级属性;
* &lt;cite&gt;cdef&lt;/cite&gt; 类方法必须声明为 &lt;cite&gt;cpdef&lt;/cite&gt; 用于 Python 可见方法或 &lt;cite&gt;cdef&lt;/cite&gt; 用于内部 C 方法。
在上面的例子中, &lt;cite&gt;myfunction()&lt;/cite&gt;中局部变量&lt;cite&gt;&lt;/cite&gt;的类型不固定,因此是一个 Python 对象。要静态输入,可以使用 Cython 的`@cython.locals`装饰器(参见 [魔法属性](#magic-attributes)[魔法属性.pxd](#magic-attributes-pxd)) 。
普通 Python( [`def`](https://docs.python.org/3/reference/compound_stmts.html#def "(in Python v3.7)") )函数不能在`.pxd`文件中声明。因此,目前不可能在`.pxd`文件中覆盖普通 Python 函数的类型,例如覆盖其局部变量的类型。在大多数情况下,将它们声明为 &lt;cite&gt;cpdef&lt;/cite&gt; 将按预期工作。
## 魔法属性
magic `cython`模块提供了特殊装饰器,可用于在 Python 文件中添加静态类型,同时被解释器忽略。
此选项将`cython`模块依赖项添加到原始代码,但不需要维护补充`.pxd`文件。 Cython 提供了这个模块的虚假版本 &lt;cite&gt;Cython.Shadow&lt;/cite&gt; ,当安装 Cython 时可以作为 &lt;cite&gt;cython.py&lt;/cite&gt; 使用,但是当 Cython 是 Cython 时可以被复制以供其他模块使用。未安装。
### “编译”开关
* `compiled`是一个特殊变量,在编译器运行时设置为`True`,在解释器中设置为`False`。因此,代码
```
import cython
if cython.compiled:
print("Yep, I'm compiled.")
else:
print("Just a lowly interpreted script.")
```
根据代码是作为编译扩展名(`.so` / `.pyd`)模块还是普通`.py`文件执行,将表现不同。
### 静态打字
* `cython.declare`在当前作用域中声明一个类型变量,可用于代替`cdef type var [= value]`构造。这有两种形式,第一种作为赋值(在解释模式中创建声明时很有用):
```
import cython
x = cython.declare(cython.int) # cdef int x
y = cython.declare(cython.double, 0.57721) # cdef double y = 0.57721
```
和第二种模式作为一个简单的函数调用:
```
import cython
cython.declare(x=cython.int, y=cython.double) # cdef int x; cdef double y
```
它还可以用于定义扩展类型 private,readonly 和 public 属性:
```
import cython
@cython.cclass
class A:
cython.declare(a=cython.int, b=cython.int)
c = cython.declare(cython.int, visibility='public')
d = cython.declare(cython.int) # private by default.
e = cython.declare(cython.int, visibility='readonly')
def __init__(self, a, b, c, d=5, e=3):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
```
* `@cython.locals`是一个装饰器,用于指定函数体中局部变量的类型(包括参数):
```
import cython
@cython.locals(a=cython.long, b=cython.long, n=cython.longlong)
def foo(a, b, x, y):
n = a * b
# ...
```
* `@cython.returns(&lt;type&gt;)`指定函数的返回类型。
* `@cython.exceptval(value=None, *, check=False)`指定函数的异常返回值和异常检查语义,如下所示:
```
@exceptval(-1) # cdef int func() except -1:
@exceptval(-1, check=False) # cdef int func() except -1:
@exceptval(check=True) # cdef int func() except *:
@exceptval(-1, check=True) # cdef int func() except? -1:
```
* Python 注释可用于声明参数类型,如以下示例所示。为避免与其他类型的注释使用冲突,可以使用指令`annotation_typing=False`禁用此功能。
```
import cython
def func(foo: dict, bar: cython.int) -&gt; tuple:
foo["hello world"] = 3 + bar
return foo, 5
```
对于非 Python 返回类型,这可以与`@cython.exceptval()`装饰器结合使用:
```
import cython
@cython.exceptval(-1)
def func(x: cython.int) -&gt; cython.int:
if x &lt; 0:
raise ValueError("need integer &gt;= 0")
return x + 1
```
从版本 0.27 开始,Cython 还支持 [PEP 526](https://www.python.org/dev/peps/pep-0526/) 中定义的变量注释。这允许以 Python 3.6 兼容的方式声明变量类型,如下所示:
```
import cython
def func():
# Cython types are evaluated as for cdef declarations
x: cython.int # cdef int x
y: cython.double = 0.57721 # cdef double y = 0.57721
z: cython.float = 0.57721 # cdef float z = 0.57721
# Python types shadow Cython types for compatibility reasons
a: float = 0.54321 # cdef double a = 0.54321
b: int = 5 # cdef object b = 5
c: long = 6 # cdef object c = 6
pass
@cython.cclass
class A:
a: cython.int
b: cython.int
def __init__(self, b=0):
self.a = 3
self.b = b
```
目前无法表达对象属性的可见性。
### C 类型
Cython 模块内置了许多类型。它提供所有标准 C 类型,即`char``short``int``long``longlong`以及它们的无符号版本`uchar``ushort``uint``ulong``ulonglong`。特殊的`bint`类型用于 C 布尔值,`Py_ssize_t`用于(容器)的(签名)大小。
对于每种类型,都有指针类型`p_int``pp_int`等,在解释模式下最多三级,在编译模式下无限深。可以使用`cython.pointer(cython.int)`构建更多指针类型,将数组构造为`cython.int[10]`。有限的尝试是模拟这些更复杂的类型,但只能通过 Python 语言完成。
Python 类型 int,long 和 bool 分别被解释为 C `int``long``bint`。此外,可以使用 Python 内置类型`list``dict``tuple`等,以及任何用户定义的类型。
键入的 C 元组可以声明为 C 类型的元组。
### 扩展类型和 cdef 函数
* 类装饰器`@cython.cclass`创建`cdef class`
* 函数/方法装饰器`@cython.cfunc`创建 [`cdef`](../userguide/language_basics.html#cdef) 函数。
* `@cython.ccall`创建 [`cpdef`](../userguide/language_basics.html#cpdef) 函数,即 Cython 代码可以在 C 级调用的函数。
* `@cython.locals`声明局部变量(见上文)。它还可用于声明参数的类型,即签名中使用的局部变量。
* `@cython.inline`相当于 C `inline`修饰符。
* `@cython.final`通过阻止将类型用作基类来终止继承链,或者通过在子类型中重写方法来终止继承链。这可以实现某些优化,例如内联方法调用。
以下是 [`cdef`](../userguide/language_basics.html#cdef) 功能的示例:
```
@cython.cfunc
@cython.returns(cython.bint)
@cython.locals(a=cython.int, b=cython.int)
def c_compare(a,b):
return a == b
```
### 进一步的 Cython 函数和声明
* `address`用于代替`&`运算符:
```
cython.declare(x=cython.int, x_ptr=cython.p_int)
x_ptr = cython.address(x)
```
* `sizeof`模拟运算符的&lt;cite&gt;大小。它可以采用两种类型和表达方式。&lt;/cite&gt;
```
cython.declare(n=cython.longlong)
print(cython.sizeof(cython.longlong))
print(cython.sizeof(n))
```
* `struct`可用于创建结构类型:
```
MyStruct = cython.struct(x=cython.int, y=cython.int, data=cython.double)
a = cython.declare(MyStruct)
```
相当于代码:
```
cdef struct MyStruct:
int x
int y
double data
cdef MyStruct a
```
* `union`使用与`struct`完全相同的语法创建联合类型。
* `typedef`定义给定名称下的类型:
```
T = cython.typedef(cython.p_int) # ctypedef int* T
```
* `cast`将(不安全地)重新解释表达式类型。 `cython.cast(T, t)`相当于`&lt;T&gt;t`。第一个属性必须是类型,第二个属性是要转换的表达式。指定可选关键字参数`typecheck=True`具有`&lt;T?&gt;t`的语义。
```
t1 = cython.cast(T, t)
t2 = cython.cast(T, t, typecheck=True)
```
### .pxd
特殊的 &lt;cite&gt;cython&lt;/cite&gt; 模块也可以在扩充`.pxd`文件中导入和使用。例如,以下 Python 文件`dostuff.py`
```
def dostuff(n):
t = 0
for i in range(n):
t += i
return t
```
可以使用以下`.pxd`文件`dostuff.pxd`进行扩充:
```
import cython
@cython.locals(t=cython.int, i=cython.int)
cpdef int dostuff(int n)
```
`cython.declare()`函数可用于在扩充`.pxd`文件中指定全局变量的类型。
## 提示与技巧
### 调用 C 函数
通常,不可能在纯 Python 模式下调用 C 函数,因为在普通(未编译)Python 中没有通用的方法来支持它。但是,在存在等效 Python 函数的情况下,可以通过将 C 函数强制与条件导入相结合来实现,如下所示:
```
# mymodule.pxd
# declare a C function as "cpdef" to export it to the module
cdef extern from "math.h":
cpdef double sin(double x)
```
```
# mymodule.py
import cython
# override with Python import if not in compiled code
if not cython.compiled:
from math import sin
# calls sin() from math.h when compiled with Cython and math.sin() in Python
print(sin(0))
```
请注意,“sin”函数将在此处显示在“mymodule”的模块命名空间中(即,将存在`mymodule.sin()`函数)。您可以根据 Python 惯例将其标记为内部名称,方法是将其重命名为`.pxd`文件中的“_sin”,如下所示:
```
cdef extern from "math.h":
cpdef double _sin "sin" (double x)
```
然后,您还可以将 Python 导入更改为`from math import sin as _sin`以使名称再次匹配。
### 将 C 数组用于固定大小的列表
C 数组可以自动强制转换为 Python 列表或元组。这可以被利用来在编译时用 C 数组替换 Python 代码中的固定大小的 Python 列表。一个例子:
```
import cython
@cython.locals(counts=cython.int[10], digit=cython.int)
def count_digits(digits):
"""
>>> digits = '01112222333334445667788899'
>>> count_digits(map(int, digits))
[1, 3, 4, 5, 3, 1, 2, 2, 3, 2]
"""
counts = [0] * 10
for digit in digits:
assert 0 <= digit <= 9
counts[digit] += 1
return counts
```
在普通的 Python 中,这将使用 Python 列表来收集计数,而 Cython 将生成使用 C int 的 C 数组的 C 代码。
\ No newline at end of file
# 使用 NumPy
> 原文: [http://docs.cython.org/en/latest/src/tutorial/numpy.html](http://docs.cython.org/en/latest/src/tutorial/numpy.html)
注意
Cython 0.16 引入了类型化的内存视图作为此处描述的 NumPy 集成的后续版本。它们比下面的缓冲区语法更容易使用,开销更少,并且可以在不需要 GIL 的情况下传递。它们应该优先于本页面中提供的语法。有关 NumPy 用户 ,请参阅 [Cython。](../userguide/numpy_tutorial.html#numpy-tutorial)
您可以使用 Cython 中的 NumPy 与常规 Python 中的 NumPy 完全相同,但这样做会导致潜在的高速加速,因为 Cython 支持快速访问 NumPy 数组。让我们看一下如何使用一个简单的例子。
下面的代码使用过滤器对图像进行 2D 离散卷积(我相信你可以做得更好!,让它用于演示目的)。它既是有效的 Python 又是有效的 Cython 代码。我将它称为 Python 版本的`convolve_py.py`和 Cython 版本的`convolve1.pyx` - Cython 使用“.pyx”作为其文件后缀。
```
import numpy as np
def naive_convolve(f, g):
# f is an image and is indexed by (v, w)
# g is a filter kernel and is indexed by (s, t),
# it needs odd dimensions
# h is the output image and is indexed by (x, y),
# it is not cropped
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
# smid and tmid are number of pixels between the center pixel
# and the edge, ie for a 5x5 filter they will be 2.
#
# The output size is calculated by adding smid, tmid to each
# side of the dimensions of the input image.
vmax = f.shape[0]
wmax = f.shape[1]
smax = g.shape[0]
tmax = g.shape[1]
smid = smax // 2
tmid = tmax // 2
xmax = vmax + 2 * smid
ymax = wmax + 2 * tmid
# Allocate result image.
h = np.zeros([xmax, ymax], dtype=f.dtype)
# Do convolution
for x in range(xmax):
for y in range(ymax):
# Calculate pixel value for h at (x,y). Sum one component
# for each pixel (s, t) of the filter g.
s_from = max(smid - x, -smid)
s_to = min((xmax - x) - smid, smid + 1)
t_from = max(tmid - y, -tmid)
t_to = min((ymax - y) - tmid, tmid + 1)
value = 0
for s in range(s_from, s_to):
for t in range(t_from, t_to):
v = x - smid + s
w = y - tmid + t
value += g[smid - s, tmid - t] * f[v, w]
h[x, y] = value
return h
```
这应编译为生成`yourmod.so`(对于 Linux 系统,在 Windows 系统上,它将是`yourmod.pyd`)。我们运行 Python 会话来测试 Python 版本(从`.py` -file 导入)和编译的 Cython 模块。
```
In [1]: import numpy as np
In [2]: import convolve_py
In [3]: convolve_py.naive_convolve(np.array([[1, 1, 1]], dtype=np.int),
... np.array([[1],[2],[1]], dtype=np.int))
Out [3]:
array([[1, 1, 1],
[2, 2, 2],
[1, 1, 1]])
In [4]: import convolve1
In [4]: convolve1.naive_convolve(np.array([[1, 1, 1]], dtype=np.int),
... np.array([[1],[2],[1]], dtype=np.int))
Out [4]:
array([[1, 1, 1],
[2, 2, 2],
[1, 1, 1]])
In [11]: N = 100
In [12]: f = np.arange(N*N, dtype=np.int).reshape((N,N))
In [13]: g = np.arange(81, dtype=np.int).reshape((9, 9))
In [19]: %timeit -n2 -r3 convolve_py.naive_convolve(f, g)
2 loops, best of 3: 1.86 s per loop
In [20]: %timeit -n2 -r3 convolve1.naive_convolve(f, g)
2 loops, best of 3: 1.41 s per loop
```
还没有那么大的差别;因为 C 代码仍然完全符合 Python 解释器的作用(例如,意味着为每个使用的数字分配了一个新对象)。查看生成的 html 文件,看看即使是最简单的语句,您需要快速得到什么。我们需要给 Cython 更多信息;我们需要添加类型。
## 添加类型
要添加类型,我们使用自定义 Cython 语法,因此我们现在正在破坏 Python 源兼容性。考虑一下这段代码(_ 阅读评论!_):
```
# tag: numpy_old
# You can ignore the previous line.
# It's for internal testing of the cython documentation.
import numpy as np
# "cimport" is used to import special compile-time information
# about the numpy module (this is stored in a file numpy.pxd which is
# currently part of the Cython distribution).
cimport numpy as np
# We now need to fix a datatype for our arrays. I've used the variable
# DTYPE for this, which is assigned to the usual NumPy runtime
# type info object.
DTYPE = np.int
# "ctypedef" assigns a corresponding compile-time type to DTYPE_t. For
# every type in the numpy module there's a corresponding compile-time
# type with a _t-suffix.
ctypedef np.int_t DTYPE_t
# "def" can type its arguments but not have a return type. The type of the
# arguments for a "def" function is checked at run-time when entering the
# function.
#
# The arrays f, g and h is typed as "np.ndarray" instances. The only effect
# this has is to a) insert checks that the function arguments really are
# NumPy arrays, and b) make some attribute access like f.shape[0] much
# more efficient. (In this example this doesn't matter though.)
def naive_convolve(np.ndarray f, np.ndarray g):
if g.shape[0] % 2 != 1 or g.shape[1] % 2 != 1:
raise ValueError("Only odd dimensions on filter supported")
assert f.dtype == DTYPE and g.dtype == DTYPE
# The "cdef" keyword is also used within functions to type variables. It
# can only be used at the top indentation level (there are non-trivial
# problems with allowing them in other places, though we'd love to see
# good and thought out proposals for it).
#
# For the indices, the "int" type is used. This corresponds to a C int,
# other C types (like "unsigned int") could have been used instead.
# Purists could use "Py_ssize_t" which is the proper Python type for
# array indices.
cdef int vmax = f.shape[0]
cdef int wmax = f.shape[1]
cdef int smax = g.shape[0]
cdef int tmax = g.shape[1]
cdef int smid = smax // 2
cdef int tmid = tmax // 2
cdef int xmax = vmax + 2 * smid
cdef int ymax = wmax + 2 * tmid
cdef np.ndarray h = np.zeros([xmax, ymax], dtype=DTYPE)
cdef int x, y, s, t, v, w
# It is very important to type ALL your variables. You do not get any
# warnings if not, only much slower code (they are implicitly typed as
# Python objects).
cdef int s_from, s_to, t_from, t_to
# For the value variable, we want to use the same data type as is
# stored in the array, so we use "DTYPE_t" as defined above.
# NB! An important side-effect of this is that if "value" overflows its
# datatype size, it will simply wrap around like in C, rather than raise
# an error like in Python.
cdef DTYPE_t value
for x in range(xmax):
for y in range(ymax):
s_from = max(smid - x, -smid)
s_to = min((xmax - x) - smid, smid + 1)
t_from = max(tmid - y, -tmid)
t_to = min((ymax - y) - tmid, tmid + 1)
value = 0
for s in range(s_from, s_to):
for t in range(t_from, t_to):
v = x - smid + s
w = y - tmid + t
value += g[smid - s, tmid - t] * f[v, w]
h[x, y] = value
return h
```
在构建完这个并继续我的(非正式)基准测试后,我得到:
```
In [21]: import convolve2
In [22]: %timeit -n2 -r3 convolve2.naive_convolve(f, g)
2 loops, best of 3: 828 ms per loop
```
## 高效索引
仍有瓶颈杀戮性能,那就是数组查找和分配。 `[]` -operator 仍然使用完整的 Python 操作 - 我们想要做的是直接以 C 速度访问数据缓冲区。
我们需要做的是输入`ndarray`对象的内容。我们使用特殊的“缓冲区”语法来执行此操作,必须告诉数据类型(第一个参数)和维度数量(“ndim”仅限关键字参数,如果未提供,则假定为一维)。
这些是必要的变化:
```
...
def naive_convolve(np.ndarray[DTYPE_t, ndim=2] f, np.ndarray[DTYPE_t, ndim=2] g):
...
cdef np.ndarray[DTYPE_t, ndim=2] h = ...
```
用法:
```
In [18]: import convolve3
In [19]: %timeit -n3 -r100 convolve3.naive_convolve(f, g)
3 loops, best of 100: 11.6 ms per loop
```
请注意这种变化的重要性。
_Gotcha_ :这种有效的索引仅影响某些索引操作,即那些具有完全`ndim`数量的类型化整数索引的索引操作。因此,如果没有输入`v`,则查找`f[v, w]`不会被优化。另一方面,这意味着您可以继续使用 Python 对象进行复杂的动态切片等,就像未键入数组时一样。
## 进一步调整索引
数组查找仍然受到两个因素的影响:
1. 进行边界检查。
2. 检查负指数并正确处理。上面的代码是明确编码的,因此它不使用负索引,并且(希望)总是在边界内访问。我们可以添加一个装饰器来禁用边界检查:
```
...
cimport cython
@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False) # turn off negative index wrapping for entire function
def naive_convolve(np.ndarray[DTYPE_t, ndim=2] f, np.ndarray[DTYPE_t, ndim=2] g):
...
```
现在没有执行边界检查(并且,作为一个副作用,如果你'碰巧'访问越界,你将在最好的情况下崩溃你的程序,在最坏的情况下会损坏数据)。可以通过多种方式切换边界检查模式,有关详细信息,请参阅 [编译器指令](../userguide/source_files_and_compilation.html#compiler-directives)
此外,我们已禁用检查以包装负指数(例如 g [-1]给出最后一个值)。与禁用边界检查一样,如果我们尝试实际使用带有此禁用的负索引,则会发生错误。
函数调用开销现在开始发挥作用,因此我们将后两个示例与较大的 N 进行比较:
```
In [11]: %timeit -n3 -r100 convolve4.naive_convolve(f, g)
3 loops, best of 100: 5.97 ms per loop
In [12]: N = 1000
In [13]: f = np.arange(N*N, dtype=np.int).reshape((N,N))
In [14]: g = np.arange(81, dtype=np.int).reshape((9, 9))
In [17]: %timeit -n1 -r10 convolve3.naive_convolve(f, g)
1 loops, best of 10: 1.16 s per loop
In [18]: %timeit -n1 -r10 convolve4.naive_convolve(f, g)
1 loops, best of 10: 597 ms per loop
```
(这也是一个混合基准,因为结果数组是在函数调用中分配的。)
警告
速度需要一些成本。特别是将类型对象(如我们的示例代码中的`f``g``h`)设置为`None`会很危险。将这些对象设置为`None`是完全合法的,但您可以使用它们检查它们是否为 None。所有其他用途(属性查找或索引)都可能会破坏或损坏数据(而不是像在 Python 中那样引发异常)。
实际规则有点复杂但主要信息很明确:不要使用类型化对象而不知道它们没有设置为 None。
## 更通用的代码
有可能做到:
```
def naive_convolve(object[DTYPE_t, ndim=2] f, ...):
```
即使用 [`object`](https://docs.python.org/3/library/functions.html#object "(in Python v3.7)") 而不是`np.ndarray`。在 Python 3.0 下,这可以允许您的算法使用任何支持缓冲区接口的库;并支持例如如果有人对 Python 2.x 感兴趣,可以轻松添加 Python Imaging Library。
但是这有一些速度损失(如果类型设置为`np.ndarray`,则编译时假设编译时间更多,特别是假设数据以纯步进模式而不是间接模式存储)。
\ No newline at end of file
# 使用 Python 数组
> 原文: [http://docs.cython.org/en/latest/src/tutorial/array.html](http://docs.cython.org/en/latest/src/tutorial/array.html)
Python 有一个内置数组模块,支持原始类型的动态一维数组。可以从 Cython 中访问 Python 数组的底层 C 数组。同时它们是普通的 Python 对象,当使用 [`multiprocessing`](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing "(in Python v3.7)") 时,它们可以存储在列表中并在进程之间进行序列化。
与使用`malloc()``free()`的手动方法相比,这提供了 Python 的安全和自动内存管理,并且与 Numpy 数组相比,不需要安装依赖项,如 [`array`](https://docs.python.org/3/library/array.html#module-array "(in Python v3.7)") 模块内置于 Python 和 Cython 中。
## 内存视图的安全使用
```
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef int[:] ca = a
print(ca[0])
```
注意:导入会将常规 Python 数组对象带入命名空间,而 cimport 会添加可从 Cython 访问的函数。
Python 数组使用类型签名和初始值序列构造。有关可能的类型签名,请参阅[阵列模块](https://docs.python.org/library/array.html)的 Python 文档。
请注意,当将 Python 数组分配给类型为内存视图的变量时,构造内存视图会有轻微的开销。但是,从那时起,变量可以无需开销即可传递给其他函数,只要输入即可:
```
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef int[:] ca = a
cdef int overhead(object a):
cdef int[:] ca = a
return ca[0]
cdef int no_overhead(int[:] ca):
return ca[0]
print(overhead(a)) # new memory view will be constructed, overhead
print(no_overhead(ca)) # ca is already a memory view, so no overhead
```
## 零开销,不安全访问原始 C 指针
为了避免任何开销并且能够将 C 指针传递给其他函数,可以将底层连续数组作为指针进行访问。没有类型或边界检查,因此请小心使用正确的类型和签名。
```
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
# access underlying pointer:
print(a.data.as_ints[0])
from libc.string cimport memset
memset(a.data.as_voidptr, 0, len(a) * sizeof(int))
```
请注意,对数组对象的任何长度更改操作都可能使指针无效。
## 克隆,扩展数组
为了避免必须使用 Python 模块中的数组构造函数,可以创建一个与模板具有相同类型的新数组,并预分配给定数量的元素。请求时,数组初始化为零。
```
from cpython cimport array
import array
cdef array.array int_array_template = array.array('i', [])
cdef array.array newarray
# create an array with 3 elements with same type as template
newarray = array.clone(int_array_template, 3, zero=False)
```
阵列也可以扩展和调整大小;这避免了重复的内存重新分配,如果元素将被逐个追加或删除。
```
from cpython cimport array
import array
cdef array.array a = array.array('i', [1, 2, 3])
cdef array.array b = array.array('i', [4, 5, 6])
# extend a with b, resize as needed
array.extend(a, b)
# resize a, leaving just original three elements
array.resize(a, len(a) - len(b))
```
## API 参考
### 数据字段
```
data.as_voidptr
data.as_chars
data.as_schars
data.as_uchars
data.as_shorts
data.as_ushorts
data.as_ints
data.as_uints
data.as_longs
data.as_ulongs
data.as_longlongs # requires Python >=3
data.as_ulonglongs # requires Python >=3
data.as_floats
data.as_doubles
data.as_pyunicodes
```
使用给定类型直接访问基础连续 C 数组;例如,`myarray.data.as_ints`
### 功能
以下函数可用于阵列模块中的 Cython:
```
int resize(array self, Py_ssize_t n) except -1
```
快速调整大小/重新分配。不适合重复的小增量;将基础数组的大小调整为所请求的数量。
```
int resize_smart(array self, Py_ssize_t n) except -1
```
高效率的小增量;使用增长模式,提供摊销的线性时间附加。
```
cdef inline array clone(array template, Py_ssize_t length, bint zero)
```
给定模板数组,快速创建新数组。类型与`template`相同。如果零是`True`,则新数组将用零初始化。
```
cdef inline array copy(array self)
```
制作一个数组的副本。
```
cdef inline int extend_buffer(array self, char* stuff, Py_ssize_t n) except -1
```
有效附加相同类型的新数据(例如相同数组类型)`n`:元素数量(不是字节数!)
```
cdef inline int extend(array self, array other) except -1
```
使用来自另一个数组的数据扩展数组;类型必须匹配。
```
cdef inline void zero(array self)
```
将数组的所有元素设置为零。
\ No newline at end of file
# 入门
> 原文: [http://docs.cython.org/en/latest/src/quickstart/index.html](http://docs.cython.org/en/latest/src/quickstart/index.html)
* [Cython - 概述](overview.html)
* [安装 Cython](install.html)
* [构建 Cython 代码](build.html)
* [使用 distutils 构建一个 Cython 模块](build.html#building-a-cython-module-using-distutils)
* [使用 Jupyter 笔记本](build.html#using-the-jupyter-notebook)
* [使用 Sage 笔记本](build.html#using-the-sage-notebook)
* [通过静态输入加快代码](cythonize.html)
* [打字变量](cythonize.html#typing-variables)
* [打字功能](cythonize.html#typing-functions)
* [确定添加类型的位置](cythonize.html#determining-where-to-add-types)
\ No newline at end of file
# 进一步阅读
> 原文: [http://docs.cython.org/en/latest/src/tutorial/readings.html](http://docs.cython.org/en/latest/src/tutorial/readings.html)
主要文件位于 [http://docs.cython.org/](http://docs.cython.org/) 。最近的一些功能可能还没有编写文档,在这种情况下,通常可以在 [https://github.com/cython/cython/wiki/enhancements 上以 Cython 增强建议(CEP)的形式找到一些注释](https://github.com/cython/cython/wiki/enhancements)
[[Seljebotn09]](#seljebotn09) 包含有关 Cython 和 NumPy 数组的更多信息。如果您打算在多线程设置中使用 Cython 代码,则必须阅读 Cython 用于管理全局解释器锁(GIL)的功能。同一篇论文包含对 GIL 的解释,主要文档解释了用于管理它的 Cython 功能。
最后,请在 Cython 用户邮件列表 [[UserList]](#userlist) 上提出问题(或发布成功报告!)。 Cython 开发人员邮件列表 [[DevList]](#devlist) 也向所有人开放,但侧重于核心开发问题。如果您有时间开发 Cython,或者您对未来的开发有建议,请随意使用它来报告明确的错误,寻求指导。
<colgroup><col class="label"><col></colgroup>
| [[DevList]](#id3) | Cython 开发人员邮件列表: [https://mail.python.org/mailman/listinfo/cython-devel](https://mail.python.org/mailman/listinfo/cython-devel) |
<colgroup><col class="label"><col></colgroup>
| [[Seljebotn09]](#id1) | D. S. Seljebotn,Cython 的快速数值计算,第 8 届 Python 科学会议论文集,2009 年。 |
<colgroup><col class="label"><col></colgroup>
| [[用户列表]](#id2) | Cython 用户邮件列表: [https://groups.google.com/group/cython-users](https://groups.google.com/group/cython-users) |
\ No newline at end of file
# 相关工作
> 原文: [http://docs.cython.org/en/latest/src/tutorial/related_work.html](http://docs.cython.org/en/latest/src/tutorial/related_work.html)
Pyrex [[Pyrex]](../quickstart/overview.html#pyrex) 是 Cython 最初基于的编译器项目。 Cython 语言的许多功能和主要设计决策由 Greg Ewing 开发,作为该项目的一部分。今天,Cython 通过提供与 Python 代码和 Python 语义的更高兼容性,以及优秀的优化和与 NumPy 等科学 Python 扩展的更好集成,取代了 Pyrex 的功能。
ctypes [[ctypes]](#ctypes) 是 Python 的外部函数接口(FFI)。它提供 C 兼容的数据类型,并允许在 DLL 或共享库中调用函数。它可以用于在纯 Python 代码中包装这些库。与 Cython 相比,它具有主要优势,即可以在标准库中直接从 Python 代码中使用,而无需任何其他依赖项。主要缺点是它的性能受到 Python 调用开销的影响,因为所有操作必须首先通过 Python 代码。 Cython 作为一种编译语言,可以通过将更多功能和长时间运行的循环转换为快速 C 代码来避免大量此类开销。
SWIG [[SWIG]](#swig) 是一个包装器代码生成器。它使得在 C / C ++头文件中解析大型 API 定义变得非常容易,并为大量编程语言生成直接的包装器代码。然而,与 Cython 相反,它本身并不是一种编程语言。薄包装器很容易生成,但包装器需要提供的功能越多,使用 SWIG 实现它就越困难。另一方面,Cython 使得为 Python 语言编写非常精细的包装代码变得非常容易,并且可以根据需要在任何给定的位置使其变薄或变厚。此外,存在用于解析 C 头文件并使用它来生成 Cython 定义和模块骨架的第三方代码。
ShedSkin [[ShedSkin]](#shedskin) 是一个实验性的 Python-to-C ++编译器。它使用非常强大的整个模块类型推理引擎从(受限制的)Python 源代码生成 C ++程序。主要缺点是它不支持为本机不支持的操作调用 Python / C API,并且支持很少的标准 Python 模块。
<colgroup><col class="label"><col></colgroup>
| [[ctypes]](#id2) | [https://docs.python.org/library/ctypes.html](https://docs.python.org/library/ctypes.html) 。 |
<colgroup><col class="label"><col></colgroup>
| [[ShedSkin]](#id4) | M. Dufour,J。Coughlan,ShedSkin, [https://github.com/shedskin/shedskin](https://github.com/shedskin/shedskin) |
<colgroup><col class="label"><col></colgroup>
| [[SWIG]](#id3) | David M. Beazley 等,SWIG:一种易于使用的工具,用于将脚本语言与 C 和 C ++集成, [http://www.swig.org](http://www.swig.org) 。 |
\ No newline at end of file
# 附录:在 Windows 上安装 MinGW
> 原文: [http://docs.cython.org/en/latest/src/tutorial/appendix.html](http://docs.cython.org/en/latest/src/tutorial/appendix.html)
> 1. 从 [http://www.mingw.org/wiki/HOWTO_Install_the_MinGW_GCC_Compiler_Suite](http://www.mingw.org/wiki/HOWTO_Install_the_MinGW_GCC_Compiler_Suite) 下载 MinGW 安装程序。 (截至撰写本文时,下载链接有点难以找到;它位于左侧菜单中的“关于”下)。您需要名为“Automated MinGW Installer”(当前版本为 5.1.4)的文件。
>
>
> 2. 运行它并安装 MinGW。 Cython 只需要基本的包,尽管你可能也想要至少抓住 C ++编译器。
>
>
> 3. 您需要设置 Windows 的“PATH”环境变量,以便包括例如“c:\ mingw \ bin”(如果您将 MinGW 安装到“c:\ mingw”)。以下 Web 页面描述了 Windows XP 中的过程(Vista 过程类似): [https://support.microsoft.com/kb/310519](https://support.microsoft.com/kb/310519)
>
>
> 4. 最后,告诉 Python 使用 MinGW 作为默认编译器(否则它将尝试 Visual C)。如果将 Python 安装到“c:\ Python27”,则创建一个名为“c:\ Python27 \ Lib \ distutils \ distutils.cfg”的文件,其中包含:
>
>
>
> ```
> [build]
> compiler = mingw32
>
> ```
[[WinInst]](#wininst) wiki 页面包含有关此过程的更新信息。欢迎任何有助于使 Windows 安装过程更顺畅的贡献;一个不幸的事实是,没有一个普通的 Cython 开发人员可以方便地访问 Windows。
<colgroup><col class="label"><col></colgroup>
| [[WinInst]](#id1) | [https://github.com/cython/cython/wiki/CythonExtensionsOnWindows](https://github.com/cython/cython/wiki/CythonExtensionsOnWindows) |
\ No newline at end of file
# 用户指南
> 原文: [http://docs.cython.org/en/latest/src/userguide/index.html](http://docs.cython.org/en/latest/src/userguide/index.html)
内容:
* [语言基础知识](language_basics.html)
* [声明数据类型](language_basics.html#declaring-data-types)
* [C 变量和类型定义](language_basics.html#c-variable-and-type-definitions)
* [Python 函数与 C 函数](language_basics.html#python-functions-vs-c-functions)
* [自动类型转换](language_basics.html#automatic-type-conversions)
* [陈述和表达](language_basics.html#statements-and-expressions)
* [Cython 文件类型](language_basics.html#cython-file-types)
* [条件编译](language_basics.html#conditional-compilation)
* [扩展类型](extension_types.html)
* [简介](extension_types.html#introduction)
* [静态属性](extension_types.html#static-attributes)
* [动态属性](extension_types.html#dynamic-attributes)
* [类型声明](extension_types.html#type-declarations)
* [扩展类型和无](extension_types.html#extension-types-and-none)
* [特殊方法](extension_types.html#special-methods)
* [属性](extension_types.html#properties)
* [子类](extension_types.html#subclassing)
* [C 方法](extension_types.html#c-methods)
* [前向声明扩展类型](extension_types.html#forward-declaring-extension-types)
* [快速实例化](extension_types.html#fast-instantiation)
* [来自现有 C / C ++指针的实例化](extension_types.html#instantiation-from-existing-c-c-pointers)
* [使扩展类型弱引用](extension_types.html#making-extension-types-weak-referenceable)
* [控制 CPython](extension_types.html#controlling-deallocation-and-garbage-collection-in-cpython) 中的释放和垃圾收集
* [控制酸洗](extension_types.html#controlling-pickling)
* [公共和外部扩展类型](extension_types.html#public-and-external-extension-types)
* [公共扩展类型](extension_types.html#public-extension-types)
* [扩展类型的特殊方法](special_methods.html)
* [声明](special_methods.html#declaration)
* [Docstrings](special_methods.html#docstrings)
* [初始化方法:`__cinit__()`和`__init__()`](special_methods.html#initialisation-methods-cinit-and-init)
* [终结方法:`__dealloc__()`](special_methods.html#finalization-method-dealloc)
* [算术方法](special_methods.html#arithmetic-methods)
* [丰富的比较](special_methods.html#rich-comparisons)
* [`__next__()`方法](special_methods.html#the-next-method)
* [特殊方法表](special_methods.html#special-method-table)
* [在 Cython 模块之间共享声明](sharing_declarations.html)
* [定义和实施文件](sharing_declarations.html#definition-and-implementation-files)
* [定义文件包含什么](sharing_declarations.html#what-a-definition-file-contains)
* [实施文件包含什么](sharing_declarations.html#what-an-implementation-file-contains)
* [cimport 声明](sharing_declarations.html#the-cimport-statement)
* [分享 C 函数](sharing_declarations.html#sharing-c-functions)
* [分享扩展类型](sharing_declarations.html#sharing-extension-types)
* [与外部 C 代码接口](external_C_code.html)
* [外部声明](external_C_code.html#external-declarations)
* [使用 C](external_C_code.html#using-cython-declarations-from-c) 的 Cython 声明
* [源文件和编译](source_files_and_compilation.html)
* [从命令行编译](source_files_and_compilation.html#compiling-from-the-command-line)
* [基本 setup.py](source_files_and_compilation.html#basic-setup-py)
* [包中的多个 Cython 文件](source_files_and_compilation.html#multiple-cython-files-in-a-package)
* [集成多个模块](source_files_and_compilation.html#integrating-multiple-modules)
* [使用`pyximport`](source_files_and_compilation.html#compiling-with-pyximport) 进行编译
* [使用`cython.inline`](source_files_and_compilation.html#compiling-with-cython-inline) 进行编译
* [与 Sage](source_files_and_compilation.html#compiling-with-sage) 一起编译
* [使用 Jupyter 笔记本进行编译](source_files_and_compilation.html#compiling-with-a-jupyter-notebook)
* [编译器指令](source_files_and_compilation.html#compiler-directives)
* [早期绑定速度](early_binding_for_speed.html)
* [在 Cython 中使用 C ++](wrapping_CPlusPlus.html)
* [概述](wrapping_CPlusPlus.html#overview)
* [一个简单的教程](wrapping_CPlusPlus.html#a-simple-tutorial)
* [编制和导入](wrapping_CPlusPlus.html#compilation-and-importing)
* [高级 C ++功能](wrapping_CPlusPlus.html#advanced-c-features)
* [RTTI 和 typeid()](wrapping_CPlusPlus.html#rtti-and-typeid)
* [在 setup.py](wrapping_CPlusPlus.html#specify-c-language-in-setup-py) 中指定 C ++语言
* [警告和限制](wrapping_CPlusPlus.html#caveats-and-limitations)
* [融合类型(模板)](fusedtypes.html)
* [快速入门](fusedtypes.html#quickstart)
* [声明融合类型](fusedtypes.html#declaring-fused-types)
* [使用融合类型](fusedtypes.html#using-fused-types)
* [选择专业化](fusedtypes.html#selecting-specializations)
* [内置熔断类型](fusedtypes.html#built-in-fused-types)
* [熔铸函数](fusedtypes.html#casting-fused-functions)
* [类型检查专业化](fusedtypes.html#type-checking-specializations)
* [条件 GIL 获取/释放](fusedtypes.html#conditional-gil-acquiring-releasing)
* [__signatures__](fusedtypes.html#signatures)
* [将 Cython 代码移植到 PyPy](pypy.html)
* [参考计数](pypy.html#reference-counts)
* [物件寿命](pypy.html#object-lifetime)
* [借用参考和数据指针](pypy.html#borrowed-references-and-data-pointers)
* [内置类型,插槽和字段](pypy.html#builtin-types-slots-and-fields)
* [GIL 处理](pypy.html#gil-handling)
* [效率](pypy.html#efficiency)
* [已知问题](pypy.html#known-problems)
* [错误和崩溃](pypy.html#bugs-and-crashes)
* [限制](limitations.html)
* [嵌套元组参数解包](limitations.html#nested-tuple-argument-unpacking)
* [检查支持](limitations.html#inspect-support)
* [堆叠帧](limitations.html#stack-frames)
* [推断文字的身份与平等](limitations.html#identity-vs-equality-for-inferred-literals)
* [Cython 和派热克斯之间的差异](pyrex_differences.html)
* [Python 3 支持](pyrex_differences.html#python-3-support)
* [条件表达式“x if b else y”](pyrex_differences.html#conditional-expressions-x-if-b-else-y)
* [cdef inline](pyrex_differences.html#cdef-inline)
* [声明转让(例如“cdef int spam = 5”)](pyrex_differences.html#assignment-on-declaration-e-g-cdef-int-spam-5)
* for 循环中的['by'表达式(例如“for i from 0&lt; = i&lt; 10 by 2”)](pyrex_differences.html#by-expression-in-for-loop-e-g-for-i-from-0-i-10-by-2)
* [Boolean int 类型(例如,它的行为类似于 c int,但是作为布尔值强制转换为/来自 python)](pyrex_differences.html#boolean-int-type-e-g-it-acts-like-a-c-int-but-coerces-to-from-python-as-a-boolean)
* [可执行类主体](pyrex_differences.html#executable-class-bodies)
* [cpdef 函数](pyrex_differences.html#cpdef-functions)
* [自动量程转换](pyrex_differences.html#automatic-range-conversion)
* [更友好型铸造](pyrex_differences.html#more-friendly-type-casting)
* [cdef / cpdef 函数中的可选参数](pyrex_differences.html#optional-arguments-in-cdef-cpdef-functions)
* [结构中的函数指针](pyrex_differences.html#function-pointers-in-structs)
* [C ++异常处理](pyrex_differences.html#c-exception-handling)
* [同义词](pyrex_differences.html#synonyms)
* [源代码编码](pyrex_differences.html#source-code-encoding)
* [自动`typecheck`](pyrex_differences.html#automatic-typecheck)
* [来自 __future__ 指令](pyrex_differences.html#from-future-directives)
* [纯 Python 模式](pyrex_differences.html#pure-python-mode)
* [类型记忆视图](memoryviews.html)
* [快速入门](memoryviews.html#quickstart)
* [使用记忆库视图](memoryviews.html#using-memoryviews)
* [与旧缓冲支持](memoryviews.html#comparison-to-the-old-buffer-support)的比较
* [Python 缓冲支持](memoryviews.html#python-buffer-support)
* [内存布局](memoryviews.html#memory-layout)
* [Memoryviews 和 GIL](memoryviews.html#memoryviews-and-the-gil)
* [Memoryview 对象和 Cython 阵列](memoryviews.html#memoryview-objects-and-cython-arrays)
* [Cython 阵列](memoryviews.html#cython-arrays)
* [Python 数组模块](memoryviews.html#cpython-array-module)
* [强制对 NumPy](memoryviews.html#coercion-to-numpy)
* [无切片](memoryviews.html#none-slices)
* [通过指针](memoryviews.html#pass-data-from-a-c-function-via-pointer)从 C 函数传递数据
* [实施缓冲协议](buffer.html)
* [矩阵类](buffer.html#a-matrix-class)
* [内存安全和引用计数](buffer.html#memory-safety-and-reference-counting)
* [旗帜](buffer.html#flags)
* [参考文献](buffer.html#references)
* [使用并行](parallelism.html)
* [编制](parallelism.html#compiling)
* [突破循环](parallelism.html#breaking-out-of-loops)
* [使用 OpenMP 函数](parallelism.html#using-openmp-functions)
* [调试你的 Cython 程序](debugging.html)
* [运行调试器](debugging.html#running-the-debugger)
* [使用调试器](debugging.html#using-the-debugger)
* [便利功能](debugging.html#convenience-functions)
* [配置调试器](debugging.html#configuring-the-debugger)
* [Cython for NumPy 用户](numpy_tutorial.html)
* [Cython 一览](numpy_tutorial.html#cython-at-a-glance)
* [您的 Cython 环境](numpy_tutorial.html#your-cython-environment)
* [安装](numpy_tutorial.html#installation)
* [手动编译](numpy_tutorial.html#manual-compilation)
* [第一个 Cython 程序](numpy_tutorial.html#the-first-cython-program)
* [添加类型](numpy_tutorial.html#adding-types)
* [内存视图的高效索引](numpy_tutorial.html#efficient-indexing-with-memoryviews)
* [进一步调整索引](numpy_tutorial.html#tuning-indexing-further)
* [将 NumPy 数组声明为连续](numpy_tutorial.html#declaring-the-numpy-arrays-as-contiguous)
* [使功能更清晰](numpy_tutorial.html#making-the-function-cleaner)
* [更通用的代码](numpy_tutorial.html#more-generic-code)
* [使用多线程](numpy_tutorial.html#using-multiple-threads)
* [从哪里开始?](numpy_tutorial.html#where-to-go-from-here)
* [Pythran 作为 Numpy 后端](numpy_pythran.html)
* [distutils](numpy_pythran.html#usage-example-with-distutils) 的用法示例
## 指数和表格
* [指数](../../genindex.html)
* [模块索引](../../py-modindex.html)
* [搜索页](../../search.html)
\ No newline at end of file
此差异已折叠。
此差异已折叠。
# 扩展类型的特殊方法
> 原文: [http://docs.cython.org/en/latest/src/userguide/special_methods.html](http://docs.cython.org/en/latest/src/userguide/special_methods.html)
本页介绍了 Cython 扩展类型当前支持的特殊方法。所有特殊方法的完整列表显示在底部的表格中。其中一些方法的行为与 Python 对应方式不同,或者没有直接的 Python 对应方式,需要特别提及。
注意
此页面上说的所有内容仅适用于使用`cdef class`语句定义的扩展类型。它不适用于使用 Python [`class`](https://docs.python.org/3/reference/compound_stmts.html#class "(in Python v3.7)") 语句定义的类,其中适用普通的 Python 规则。
## 声明
必须使用 [`def`](https://docs.python.org/3/reference/compound_stmts.html#def "(in Python v3.7)") 而不是 [`cdef`](language_basics.html#cdef) 声明扩展类型的特殊方法。这不会影响它们的性能 - Python 使用不同的调用约定来调用这些特殊方法。
## Docstrings
目前,某些扩展类型的特殊方法并未完全支持 docstrings。您可以在源中放置 docstring 作为注释,但在运行时它不会显示在相应的`__doc__`属性中。 (这似乎是一个 Python 限制 - 在 &lt;cite&gt;PyTypeObject&lt;/cite&gt; 数据结构中没有任何地方放置这样的文档字符串。)
## 初始化方法:`__cinit__()`和`__init__()`
初始化对象有两种方法。
您应该在`__cinit__()`方法中执行对象的基本 C 级初始化,包括分配您的对象将拥有的任何 C 数据结构。您需要注意在`__cinit__()`方法中执行的操作,因为在调用对象时,该对象可能还不是完全有效的 Python 对象。因此,您应该小心地调用可能触及该对象的任何 Python 操作;特别是它的方法和任何可以被子类型覆盖的东西(因此依赖于它们已经初始化的子类型状态)。
在调用`__cinit__()`方法时,已为该对象分配了内存,并且已将其初始化为 0 或 null 的任何 C 属性。 (任何 Python 属性也已初始化为 None,但您可能不应该依赖它。)保证您的`__cinit__()`方法只能被调用一次。
如果扩展类型具有基类型,则在`__cinit__()`方法之前会自动调用基类型层次结构中的任何现有`__cinit__()`方法。您无法显式调用继承的`__cinit__()`方法,基类型可以自由选择是否实现`__cinit__()`。如果需要将修改后的参数列表传递给基类型,则必须在`__init__()`方法中执行初始化的相关部分,而不是调用继承方法的常规规则。
任何无法在`__cinit__()`方法中安全完成的初始化都应在`__init__()`方法中完成。到`__init__()`被调用时,该对象是一个完全有效的 Python 对象,所有操作都是安全的。在某些情况下,可能会多次调用`__init__()`或根本不调用`__init__()`,因此在这种情况下,您的其他方法应该设计得很稳健。
传递给构造函数的任何参数都将传递给`__cinit__()`方法和`__init__()`方法。如果你期望在 Python 中继承你的扩展类型,你可能会发现给`__cinit__()`方法 &lt;cite&gt;*&lt;/cite&gt;&lt;cite&gt;**&lt;/cite&gt; 参数是有用的,这样它就可以接受并忽略额外的参数。否则,任何具有不同签名的`__init__()`的 Python 子类都必须覆盖`__new__()` [[1]](#id4) 以及`__init__()`,Python 类的编写者不会期望得做。或者,为方便起见,如果您声明`__cinit__`()`方法不带参数(除了 self),它将忽略传递给构造函数的任何额外参数,而不会抱怨签名不匹配。
Note
所有构造函数参数都将作为 Python 对象传递。这意味着不可转换的 C 类型(如指针或 C ++对象)无法从 Cython 代码传递到构造函数中。如果需要,请使用工厂函数来处理对象初始化。直接调用此函数中的`__new__()`以绕过对`__init__()`构造函数的调用通常会有所帮助。
有关示例,请参阅现有 C / C ++指针 中的 [实例化。](extension_types.html#existing-pointers-instantiation)
<colgroup><col class="label"><col></colgroup>
| [[1]](#id3) | [https://docs.python.org/reference/datamodel.html#object.__new__](https://docs.python.org/reference/datamodel.html#object.__new__) |
## 定稿方法:`__dealloc__()`
`__cinit__()`方法的对应物是`__dealloc__()`方法,它应该执行`__cinit__()`方法的反转。您`__cinit__()`方法中明确分配的任何 C 数据(例如通过 malloc)都应该在`__dealloc__()`方法中释放。
您需要注意在`__dealloc__()`方法中执行的操作。在调用`__dealloc__()`方法时,对象可能已经被部分破坏,并且就 Python 而言可能不处于有效状态,因此您应该避免调用可能触及该对象的任何 Python 操作。特别是,不要调用对象的任何其他方法或做任何可能导致对象复活的事情。如果你坚持只是解除分配 C 数据是最好的。
您无需担心释放对象的 Python 属性,因为在`__dealloc__()`方法返回后,Cython 将为您完成此操作。
在对扩展类型进行子类化时,请注意,即使重写了超类的`__dealloc__()`方法,也始终会调用它。这与典型的 Python 行为形成对比,在这种行为中,除非子类显式调用超类方法,否则不会执行超类方法。
Note
扩展类型没有`__del__()`方法。
## 算术方法
算术运算符方法(如`__add__()`)的行为与 Python 对应方法不同。这些方法没有单独的“反向”版本(`__radd__()`等)。相反,如果第一个操作数不能执行操作,则调用第二个操作数的相同方法,操作数的顺序相同。
这意味着您不能依赖这些方法的第一个参数是“自”或正确的类型,并且您应该在决定做什么之前测试两个操作数的类型。如果您无法处理已经给出的类型组合,则应返回 &lt;cite&gt;NotImplemented&lt;/cite&gt; 。
这也适用于就地算术方法`__ipow__()`。它不适用于任何其他始终将 &lt;cite&gt;self&lt;/cite&gt; 作为第一个参数的原位方法(`__iadd__()`等)。
## 丰富的比较
有两种方法可以实现比较方法。根据应用程序的不同,这种方式可能更好:
* 第一种方法使用 6 Python [特殊方法](https://docs.python.org/3/reference/datamodel.html#basic-customization) `__eq__()`,`__lt__()`等。这是 Cython 0.27 以来的新功能,与普通 Python 类完全一样。
* 第二种方法使用单一特殊方法`__richcmp__()`。这在一个方法中实现了所有丰富的比较操作。签名是`def __richcmp__(self, other, int op)`。整数参数`op`表示要执行的操作,如下表所示:
&lt;colgroup&gt;&lt;col width="42%"&gt; &lt;col width="58%"&gt;&lt;/colgroup&gt;
| &LT; | Py_LT |
| == | Py_EQ |
| &GT; | Py_GT |
| &LT = | Py_LE |
| != | Py_NE |
| &GT; = | Py_GE |
这些常量可以从`cpython.object`模块中导入。
## `__next__()`方法
希望实现迭代器接口的扩展类型应该定义一个名为`__next__()`的方法,而不是下一个方法。 Python 系统将自动提供调用`__next__()`的下一个方法。 _NOT_ 是否明确地给你的类型一个`next()`方法,否则可能会发生坏事。
## 特殊方法表
此表列出了所有特殊方法及其参数和返回类型。在下表中,self 的参数名称用于指示参数具有方法所属的类型。表中未指定类型的其他参数是通用 Python 对象。
您不必将方法声明为采用这些参数类型。如果您声明了不同的类型,则会根据需要执行转换。
### 一般
[https://docs.python.org/3/reference/datamodel.html#special-method-names](https://docs.python.org/3/reference/datamodel.html#special-method-names)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| 名称 | 参数 | 返回类型 | 描述 |
| --- | --- | --- | --- |
| __cinit__ | 自我,...... | | 基本初始化(没有直接的 Python 等价物) |
| __ 在里面 __ | self, … |   | 进一步初始化 |
| __dealloc__ | 自 |   | 基本的释放(没有直接的 Python 等价物) |
| __cmp__ | x,y | INT | 3 路比较(仅限 Python 2) |
| __str__ | self | 宾语 | STR(个体经营) |
| __repr__ | self | object | 再版(个体经营) |
| __hash__ | self | Py_hash_t | 散列函数(返回 32/64 位整数) |
| __ 呼叫 __ | self, … | object | 自(…) |
| __iter__ | self | object | 返回序列的迭代器 |
| __getattr__ | 自我,名字 | object | 获取属性 |
| __getattribute__ | self, name | object | 无条件地获取属性 |
| __setattr__ | 自我,名字,val |   | 设置属性 |
| __delattr__ | self, name |   | 删除属性 |
### 丰富的比较运算符
[https://docs.python.org/3/reference/datamodel.html#basic-customization](https://docs.python.org/3/reference/datamodel.html#basic-customization)
您可以选择实现标准的 Python 特殊方法,如`__eq__()`或单个特殊方法`__richcmp__()`。根据应用,一种方式或另一种方式可能更好。
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="43%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __eq__ | 自我,是的 | object | 自我== y |
| __ne__ | self, y | object | self!= y(如果没有,则回退到`__eq__`) |
| __lt__ | self, y | object | 自我&lt; ÿ |
| __gt__ | self, y | object | 自我&gt; ÿ |
| __le__ | self, y | object | 自我&lt; = y |
| __ge__ | self, y | object | 自我&gt; = y |
| __richcmp__ | self,y,int op | object | 为上述所有内容加入了丰富的比较方法(没有直接的 Python 等价物) |
### 算术运算符
[https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __ 加 __ | x, y | object | 二进制 &lt;cite&gt;+&lt;/cite&gt; 运算符 |
| __sub__ | x, y | object | 二进制 &lt;cite&gt;-&lt;/cite&gt; 运算符 |
| __mul__ | x, y | object | &lt;cite&gt;*&lt;/cite&gt; 运算符 |
| __div__ | x, y | object | &lt;cite&gt;/&lt;/cite&gt; 运算符用于旧式划分 |
| __floordiv__ | x, y | object | &lt;cite&gt;//&lt;/cite&gt; 运算符 |
| __truediv__ | x, y | object | &lt;cite&gt;/&lt;/cite&gt; 运算符用于新式划分 |
| __mod__ | x, y | object | &lt;cite&gt;&lt;/cite&gt;运算符 |
| __divmod__ | x, y | object | 组合 div 和 mod |
| __pow__ | x,y,z | object | &lt;cite&gt;**&lt;/cite&gt; 运算符或 pow(x,y,z) |
| __neg__ | self | object | 一元 &lt;cite&gt;-&lt;/cite&gt; 运算符 |
| __pos__ | self | object | 一元 &lt;cite&gt;+&lt;/cite&gt; 运算符 |
| __abs__ | self | object | 绝对值 |
| __nonzero__ | self | int | 转换为布尔值 |
| __ 倒置 __ | self | object | &lt;cite&gt;&lt;/cite&gt;运算符 |
| __lshift__ | x, y | object | &lt;cite&gt;&lt;&lt;&lt;/cite&gt; 运营商 |
| __rshift__ | x, y | object | &lt;cite&gt;&gt;&gt;&lt;/cite&gt; 运营商 |
| __ 和 __ | x, y | object | &lt;cite&gt;&amp;&lt;/cite&gt; 运营商 |
| __ 要么 __ | x, y | object | &lt;cite&gt;&#124;&lt;/cite&gt; 运营商 |
| __xor__ | x, y | object | &lt;cite&gt;^&lt;/cite&gt; 运算符 |
### 数字转换
[https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __int__ | self | object | 转换为整数 |
| __ 长 __ | self | object | 转换为长整数 |
| __ 浮动 __ | self | object | 转换为浮动 |
| __oct__ | self | object | 转换为八进制 |
| __hex__ | self | object | 转换为十六进制 |
| __index__(仅限 2.5+) | self | object | 转换为序列索引 |
### 就地算术运算符
[https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types](https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __ 我加 __ | 自我,x | object | &lt;cite&gt;+ =&lt;/cite&gt; 运算符 |
| __isub__ | self, x | object | &lt;cite&gt;- =&lt;/cite&gt; 运算符 |
| __imul__ | self, x | object | &lt;cite&gt;* =&lt;/cite&gt; 运算符 |
| __idiv__ | self, x | object | &lt;cite&gt;/ =&lt;/cite&gt; 运算符用于旧式划分 |
| __ifloordiv__ | self, x | object | &lt;cite&gt;// =&lt;/cite&gt; 运算符 |
| __truediv__ | self, x | object | &lt;cite&gt;/ =&lt;/cite&gt; 运算符用于新式划分 |
| __imod__ | self, x | object | &lt;cite&gt;%=&lt;/cite&gt; 运算符 |
| __pow__ | x, y, z | object | &lt;cite&gt;** =&lt;/cite&gt; 运算符 |
| __ilshift__ | self, x | object | &lt;cite&gt;&lt;&lt; =&lt;/cite&gt; 运算符 |
| __irshift__ | self, x | object | &lt;cite&gt;&gt;&gt; =&lt;/cite&gt; 运算符 |
| __ 我和 __ | self, x | object | &lt;cite&gt;&amp; =&lt;/cite&gt; 运算符 |
| __ior__ | self, x | object | &lt;cite&gt;&#124; =&lt;/cite&gt; 运算符 |
| __ixor__ | self, x | object | &lt;cite&gt;^ =&lt;/cite&gt; 运算符 |
### 序列和映射
[https://docs.python.org/3/reference/datamodel.html#emulating-container-types](https://docs.python.org/3/reference/datamodel.html#emulating-container-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __len__ | self | Py_ssize_t | LEN(个体经营) |
| __getitem__ | self, x | object | 自[X] |
| __setitem__ | 自我,x,y |   | self [x] = y |
| __delitem__ | self, x |   | del self [x] |
| __getslice__ | self,Py_ssize_t i,Py_ssize_t j | object | 自[I:j]的 |
| __setslice__ | self,Py_ssize_t i,Py_ssize_t j,x |   | 自我[i:j] = x |
| __delslice__ | self, Py_ssize_t i, Py_ssize_t j |   | del self [i:j] |
| __contains__ | self, x | int | x 在自我中 |
### 迭代器
[https://docs.python.org/3/reference/datamodel.html#emulating-container-types](https://docs.python.org/3/reference/datamodel.html#emulating-container-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __ 下一个 __ | self | object | 获取下一个项目(在 Python 中称为 next) |
### 缓冲接口[ [**PEP 3118** ](https://www.python.org/dev/peps/pep-3118)](没有 Python 当量 - 见注 1)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __getbuffer__ | self,Py_buffer &lt;cite&gt;* view&lt;/cite&gt; ,int flags |   |   |
| __releasebuffer__ | self,Py_buffer &lt;cite&gt;* view&lt;/cite&gt; |   |   |
### 缓冲接口[遗留](没有 Python 等价物 - 见注 1)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __getreadbuffer__ | self,Py_ssize_t i,void &lt;cite&gt;** p&lt;/cite&gt; |   |   |
| __getwritebuffer__ | self, Py_ssize_t i, void &lt;cite&gt;**p&lt;/cite&gt; |   |   |
| __getsegcount__ | self,Py_ssize_t &lt;cite&gt;* p&lt;/cite&gt; |   |   |
| __getcharbuffer__ | self,Py_ssize_t i,char &lt;cite&gt;** p&lt;/cite&gt; |   |   |
### 描述符对象(见注 2)
[https://docs.python.org/3/reference/datamodel.html#emulating-container-types](https://docs.python.org/3/reference/datamodel.html#emulating-container-types)
<colgroup><col width="18%"> <col width="30%"> <col width="10%"> <col width="41%"></colgroup>
| Name | Parameters | Return type | Description |
| --- | --- | --- | --- |
| __ 得到 __ | 自我,实例,阶级 | object | 获取属性的值 |
| __ 组 __ | 自我,实例,价值 |   | 设置属性值 |
| __ 删除 __ | 自我,实例 |   | Delete attribute |
Note
(1)缓冲区接口旨在供 C 代码使用,不能直接从 Python 访问。它在 Python 2.x 的 Python / C API 参考手册的 6.6 和 10.6 节中描述。它被 Python 2.6 中新的 [**PEP 3118** ](https://www.python.org/dev/peps/pep-3118)缓冲协议取代,并且在 Python 3 中不再可用。有关新 API 的操作指南,见 [实现缓冲协议](buffer.html#buffer)
Note
(2)描述符对象是新式 Python 类的支持机制的一部分。请参阅 Python 文档中对描述符的讨论。另见 [**PEP 252** ](https://www.python.org/dev/peps/pep-0252),“使类型看起来更像类”, [**PEP 253** ](https://www.python.org/dev/peps/pep-0253),“Subtyping Built-In Types”。
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
# Cython - 概述
> 原文: [http://docs.cython.org/en/latest/src/quickstart/overview.html](http://docs.cython.org/en/latest/src/quickstart/overview.html)
[[Cython]](#cython) 是一种编程语言,它使 Python 语言的 C 语言扩展与 Python 本身一样简单。它旨在成为 [[Python]](#python) 语言的超集,为其提供高级,面向对象,功能和动态编程。它的主要功能是支持可选的静态类型声明作为语言的一部分。源代码被转换为优化的 C / C ++代码并编译为 Python 扩展模块。这允许非常快速的程序执行和与外部 C 库的紧密集成,同时保持 Python 语言众所周知的高程序员生产力。
主要的 Python 执行环境通常被称为 CPython,因为它是用 C 语言编写的。其他主要实现使用 Java(Jython [[Jython]](#jython) ),C#(IronPython [[IronPython]](#ironpython) )和 Python 本身(PyPy [[PyPy]](#pypy) )。用 C 语言编写,CPython 有助于包装许多通过 C 语言接口的外部库。然而,在 C 中编写必要的粘合代码仍然是微不足道的,特别是对于像 Python 这样的高级语言更流利的程序员而不是像 C 这样的接近金属的语言。
最初基于着名的 Pyrex [[Pyrex]](#pyrex) ,Cython 项目通过源代码编译器将 Python 代码转换为等效的 C 代码来解决这个问题。此代码在 CPython 运行时环境中执行,但是以编译的 C 的速度执行,并且能够直接调用 C 库。同时,它保留了 Python 源代码的原始接口,这使得它可以直接从 Python 代码中使用。这些双重特性使 Cython 的两个主要用例成为可能:使用快速二进制模块扩展 CPython 解释器,以及将 Python 代码与外部 C 库连接。
虽然 Cython 可以编译(大多数)常规 Python 代码,但生成的 C 代码通常可以从 Python 和 C 类型的可选静态类型声明中获得主要(并且有时令人印象深刻)的速度改进。这些允许 Cython 将 C 语义分配给代码的一部分,并将它们转换为非常有效的 C 代码。因此,类型声明可用于两个目的:将代码段从动态 Python 语义转换为静态和快速 C 语义,还用于直接操作外部库中定义的类型。因此,Cython 将这两个世界合并为一种非常广泛适用的编程语言。
<colgroup><col class="label"><col></colgroup>
| [[Cython]](#id1) | G. Ewing,R。W. Bradshaw,S。Behnel,D。S. Seljebotn 等人,The Cython 编译器, [https://cython.org/](https://cython.org/) 。 |
<colgroup><col class="label"><col></colgroup>
| [[IronPython]](#id4) | Jim Hugunin 等人, [https://archive.codeplex.com/?p=IronPython](https://archive.codeplex.com/?p=IronPython) 。 |
<colgroup><col class="label"><col></colgroup>
| [[Jython]](#id3) | J. Huginin,B。Warsaw,F.Bock,et al。,Jython:Python for the Java platform, [http://www.jython.org](http://www.jython.org) 。 |
<colgroup><col class="label"><col></colgroup>
| [[PyPy]](#id5) | PyPy Group,PyPy:用 Python 编写的 Python 实现, [https://pypy.org/](https://pypy.org/) 。 |
<colgroup><col class="label"><col></colgroup>
| [[派热克斯]](#id6) | G. Ewing,Pyrex:Python 的 C-Extensions, [https://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/](https://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/) |
<colgroup><col class="label"><col></colgroup>
| [[Python]](#id2) | G. van Rossum 等人,Python 编程语言, [https://www.python.org/](https://www.python.org/) 。 |
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
# 将 Cython 代码移植到 PyPy
> 原文: [http://docs.cython.org/en/latest/src/userguide/pypy.html](http://docs.cython.org/en/latest/src/userguide/pypy.html)
Cython 对 cpyext 有基本支持,cpyext 是 [PyPy](https://pypy.org/) 中模拟 CPython 的 C-API 的层。这是通过使生成的 C 代码在 C 编译时适应来实现的,因此生成的代码将在 CPython 和 PyPy 中编译都不变。
但是,除了 Cython 可以在内部覆盖和调整之外,cpyext C-API 仿真还涉及到 CPython 中对用户代码有明显影响的真实 C-API 的一些差异。此页面列出了主要的差异以及处理它们的方法,以便编写在 CPython 和 PyPy 中都有效的 Cython 代码。
## 参考计数
PyPy 的一般设计差异是运行时内部不使用引用计数,但始终是垃圾收集器。仅通过计算 C 空间中保存的引用,仅在 cpyext 层模拟引用计数。这意味着 PyPy 中的引用计数通常与 CPython 中的引用计数不同,因为它不计算 Python 空间中保存的任何引用。
## 对象寿命
作为不同垃圾收集特征的直接结果,对象可能会在 CPython 之外的其他点看到它们的生命周期结束。因此,当预期物体在 CPython 中死亡但在 PyPy 中可能没有时,必须特别小心。具体来说,扩展类型(`__dealloc__()`)的解除分配器方法可能会在比 CPython 更晚的时间点被调用,而是由内存变得比被死的对象更紧密地触发。
如果代码中的点在某个对象应该死亡时是已知的(例如,当它与另一个对象或某个函数的执行时间相关联时),那么值得考虑它是否可以无效并在此时手动清理而不是依赖于解除分配器。
作为副作用,这有时甚至可以导致更好的代码设计,例如,当上下文管理器可以与`with`语句一起使用时。
## 借用的引用和数据指针
PyPy 中的内存管理允许在内存中移动对象。 C-API 层只是 PyPy 对象的间接视图,通常将数据或状态复制到 C 空间,然后绑定到 C-API 对象的生命周期,而不是底层的 PyPy 对象。重要的是要理解这两个对象在 cpyext 中是不同的东西。
效果可能是当使用数据指针或借用引用,并且不再直接从 C 空间引用拥有对象时,引用或数据指针在某些时候可能变得无效,即使对象本身仍然存在。与 CPython 相反,仅仅在列表(或其他 Python 容器)中保持对对象的引用是不够的,因为它们的内容仅在 Python 空间中管理,因此仅引用 PyPy 对象。 Python 容器中的引用不会使 C-API 视图保持活动状态。 Python 类 dict 中的条目显然也不起作用。
可能发生这种情况的一个更明显的地方是访问字节字符串的`char*`缓冲区。在 PyPy 中,只有在 Cython 代码持有对字节字符串对象本身的直接引用时,这才会起作用。
另一点是当直接使用 CPython C-API 函数返回借用的引用时,例如, [`PyTuple_GET_ITEM()`](https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GET_ITEM "(in Python v3.7)") 和类似的函数,但也有一些函数返回对内置模块或运行时环境的低级对象的借用引用。 PyPy 中的 GIL 只保证借用的引用在下次调用 PyPy(或其 C-API)时保持有效,但不一定更长。
当访问 Python 对象的内部或使用借用的引用时间长于下一次调用 PyPy 时,包括引用计数或释放 GIL 的任何东西,因此需要在 C 空间中另外保持对这些对象的直接拥有引用,例如,在函数中的局部变量或扩展类型的属性中。
如有疑问,请避免使用返回借用引用的 C-API 函数,或者在获取引用和 [`Py_DECREF()`时通过对](https://docs.python.org/3/c-api/refcounting.html#c.Py_DECREF "(in Python v3.7)") [`Py_INCREF()`](https://docs.python.org/3/c-api/refcounting.html#c.Py_INCREF "(in Python v3.7)") 的一对调用显式包围借用引用的使用完成后将其转换为自己的引用。
## 内置类型,插槽和字段
以下内置类型目前在 cpyext 中不以 C 级表示形式提供: [`PyComplexObject`](https://docs.python.org/3/c-api/complex.html#c.PyComplexObject "(in Python v3.7)")[`PyFloatObject`](https://docs.python.org/3/c-api/float.html#c.PyFloatObject "(in Python v3.7)")`PyBoolObject`
内置类型的许多类型槽函数未在 cpyext 中初始化,因此不能直接使用。
类似地,几乎没有内置类型的(实现)特定结构域在 C 级暴露,例如 [`PyLongObject`](https://docs.python.org/3/c-api/long.html#c.PyLongObject "(in Python v3.7)")`ob_digit`字段或[的`allocated`字段。 `PyListObject`](https://docs.python.org/3/c-api/list.html#c.PyListObject "(in Python v3.7)") struct 等虽然容器的`ob_size`字段(`Py_SIZE()`宏使用)可用,但不保证准确。
最好不要访问任何这些结构域和插槽,而是使用普通的 Python 类型以及对象操作的普通 Python 协议。 Cython 会将它们映射到 CPython 和 cpyext 中 C-API 的适当用法。
## GIL 处理
目前,GIL 处理函数 [`PyGILState_Ensure()`](https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure "(in Python v3.7)") 在 PyPy 中不可重入,并且在被调用两次时死锁。这意味着试图获取 GIL“以防万一”的代码,因为它可能在有或没有 GIL 的情况下被调用,但在 PyPy 中不会按预期工作。如果 GIL 已经持有,请参见 [PyGILState_Ensure 不应该死锁。](https://bitbucket.org/pypy/pypy/issues/1778)
## 效率
简单的函数,尤其是用于 CPython 速度的宏,可能在 cpyext 中表现出截然不同的性能特征。
返回借用引用的函数已被提及为需要特别小心,但它们也会导致更多的运行时开销,因为它们经常在 PyPy 中创建弱引用,它们只返回 CPython 中的普通指针。可见的例子是 [`PyTuple_GET_ITEM()`](https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GET_ITEM "(in Python v3.7)")
一些更高级别的功能也可能表现出完全不同的性能特征,例如, [`PyDict_Next()`](https://docs.python.org/3/c-api/dict.html#c.PyDict_Next "(in Python v3.7)") 用于 dict 迭代。虽然它是在 CPython 中迭代 dict 的最快方法,具有线性时间复杂度和低开销,但它目前在 PyPy 中具有二次运行时因为它映射到正常的 dict 迭代,它无法跟踪两个调用之间的当前位置,因此需要在每次调用时重新启动迭代。
这里的一般建议比 CPython 更适用,最好依靠 Cython 为您生成适当的 C-API 处理代码,而不是直接使用 C-API - 除非您真的知道自己在做什么。如果你发现在 PyPy 和 cpyext 中做一些比 Cython 目前做得更好的方法,最好修复 Cython 以获得每个人的好处。
## 已知问题
* 从 PyPy 1.9 开始,在极少数情况下,子类型内置类型会导致方法调用的无限递归。
* 特殊方法的 Docstrings 不会传播到 Python 空间。
* pypy3 中的 Python 3.x 改编只是慢慢开始包含 C-API,因此可以预期更多的不兼容性。
## 错误和崩溃
PyPy 中的 cpyext 实现比经过充分测试的 C-API 及其在 CPython 中的底层本机实现要年轻得多且不太成熟。遇到崩溃时应记住这一点,因为问题可能并不总是存在于您的代码或 Cython 中。此外,PyPy 及其 cpyext 实现在 C 级别比 CPython 和 Cython 更容易调试,仅仅因为它们不是为它而设计的。
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册