# 純 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`:
```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`:
```py
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`,就像它編寫如下:
```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 可見函數簽名必須聲明為 <cite>cpdef</cite> (默認參數替換為 <cite>*</cite> 以避免重復):
```py
cpdef int myfunction(int x, int y=*)
```
* 內部函數的 C 函數簽名可以聲明為 <cite>cdef</cite> :
```py
cdef double _helper(double a)
```
* <cite>cdef</cite> 類(擴展類型)聲明為 <cite>cdef 類</cite>;
* <cite>cdef</cite> 類屬性必須聲明為 <cite>cdef public</cite> 如果需要讀/寫 Python 訪問, <cite>cdef readonly</cite> 用于只讀 Python 訪問,或普通 <cite>cdef</cite> 用于內部 C 級屬性;
* <cite>cdef</cite> 類方法必須聲明為 <cite>cpdef</cite> 用于 Python 可見方法或 <cite>cdef</cite> 用于內部 C 方法。
在上面的例子中, <cite>myfunction()</cite>中局部變量<cite>和</cite>的類型不固定,因此是一個 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 函數的類型,例如覆蓋其局部變量的類型。在大多數情況下,將它們聲明為 <cite>cpdef</cite> 將按預期工作。
## 魔法屬性
magic `cython`模塊提供了特殊裝飾器,可用于在 Python 文件中添加靜態類型,同時被解釋器忽略。
此選項將`cython`模塊依賴項添加到原始代碼,但不需要維護補充`.pxd`文件。 Cython 提供了這個模塊的虛假版本 <cite>Cython.Shadow</cite> ,當安裝 Cython 時可以作為 <cite>cython.py</cite> 使用,但是當 Cython 是 Cython 時可以被復制以供其他模塊使用。未安裝。
### “編譯”開關
* `compiled`是一個特殊變量,在編譯器運行時設置為`True`,在解釋器中設置為`False`。因此,代碼
```py
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]`構造。這有兩種形式,第一種作為賦值(在解釋模式中創建聲明時很有用):
```py
import cython
x = cython.declare(cython.int) # cdef int x
y = cython.declare(cython.double, 0.57721) # cdef double y = 0.57721
```
和第二種模式作為一個簡單的函數調用:
```py
import cython
cython.declare(x=cython.int, y=cython.double) # cdef int x; cdef double y
```
它還可以用于定義擴展類型 private,readonly 和 public 屬性:
```py
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`是一個裝飾器,用于指定函數體中局部變量的類型(包括參數):
```py
import cython
@cython.locals(a=cython.long, b=cython.long, n=cython.longlong)
def foo(a, b, x, y):
n = a * b
# ...
```
* `@cython.returns(<type>)`指定函數的返回類型。
* `@cython.exceptval(value=None, *, check=False)`指定函數的異常返回值和異常檢查語義,如下所示:
```py
@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`禁用此功能。
```py
import cython
def func(foo: dict, bar: cython.int) -> tuple:
foo["hello world"] = 3 + bar
return foo, 5
```
對于非 Python 返回類型,這可以與`@cython.exceptval()`裝飾器結合使用:
```py
import cython
@cython.exceptval(-1)
def func(x: cython.int) -> cython.int:
if x < 0:
raise ValueError("need integer >= 0")
return x + 1
```
從版本 0.27 開始,Cython 還支持 [PEP 526](https://www.python.org/dev/peps/pep-0526/) 中定義的變量注釋。這允許以 Python 3.6 兼容的方式聲明變量類型,如下所示:
```py
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) 功能的示例:
```py
@cython.cfunc
@cython.returns(cython.bint)
@cython.locals(a=cython.int, b=cython.int)
def c_compare(a,b):
return a == b
```
### 進一步的 Cython 函數和聲明
* `address`用于代替`&`運算符:
```py
cython.declare(x=cython.int, x_ptr=cython.p_int)
x_ptr = cython.address(x)
```
* `sizeof`模擬運算符的<cite>大小。它可以采用兩種類型和表達方式。</cite>
```py
cython.declare(n=cython.longlong)
print(cython.sizeof(cython.longlong))
print(cython.sizeof(n))
```
* `struct`可用于創建結構類型:
```py
MyStruct = cython.struct(x=cython.int, y=cython.int, data=cython.double)
a = cython.declare(MyStruct)
```
相當于代碼:
```py
cdef struct MyStruct:
int x
int y
double data
cdef MyStruct a
```
* `union`使用與`struct`完全相同的語法創建聯合類型。
* `typedef`定義給定名稱下的類型:
```py
T = cython.typedef(cython.p_int) # ctypedef int* T
```
* `cast`將(不安全地)重新解釋表達式類型。 `cython.cast(T, t)`相當于`<T>t`。第一個屬性必須是類型,第二個屬性是要轉換的表達式。指定可選關鍵字參數`typecheck=True`具有`<T?>t`的語義。
```py
t1 = cython.cast(T, t)
t2 = cython.cast(T, t, typecheck=True)
```
### .pxd
特殊的 <cite>cython</cite> 模塊也可以在擴充`.pxd`文件中導入和使用。例如,以下 Python 文件`dostuff.py`:
```py
def dostuff(n):
t = 0
for i in range(n):
t += i
return t
```
可以使用以下`.pxd`文件`dostuff.pxd`進行擴充:
```py
import cython
@cython.locals(t=cython.int, i=cython.int)
cpdef int dostuff(int n)
```
`cython.declare()`函數可用于在擴充`.pxd`文件中指定全局變量的類型。
## 提示與技巧
### 調用 C 函數
通常,不可能在純 Python 模式下調用 C 函數,因為在普通(未編譯)Python 中沒有通用的方法來支持它。但是,在存在等效 Python 函數的情況下,可以通過將 C 函數強制與條件導入相結合來實現,如下所示:
```py
# mymodule.pxd
# declare a C function as "cpdef" to export it to the module
cdef extern from "math.h":
cpdef double sin(double x)
```
```py
# 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”,如下所示:
```py
cdef extern from "math.h":
cpdef double _sin "sin" (double x)
```
然后,您還可以將 Python 導入更改為`from math import sin as _sin`以使名稱再次匹配。
### 將 C 數組用于固定大小的列表
C 數組可以自動強制轉換為 Python 列表或元組。這可以被利用來在編譯時用 C 數組替換 Python 代碼中的固定大小的 Python 列表。一個例子:
```py
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 代碼。
- Cython 3.0 中文文檔
- 入門
- Cython - 概述
- 安裝 Cython
- 構建 Cython 代碼
- 通過靜態類型更快的代碼
- Tutorials
- 基礎教程
- 調用 C 函數
- 使用 C 庫
- 擴展類型(又名.cdef 類)
- pxd 文件
- Caveats
- Profiling
- Unicode 和傳遞字符串
- 內存分配
- 純 Python 模式
- 使用 NumPy
- 使用 Python 數組
- 進一步閱讀
- 相關工作
- 附錄:在 Windows 上安裝 MinGW
- 用戶指南
- 語言基礎
- 擴展類型
- 擴展類型的特殊方法
- 在 Cython 模塊之間共享聲明
- 與外部 C 代碼連接
- 源文件和編譯
- 早期綁定速度
- 在 Cython 中使用 C ++
- 融合類型(模板)
- 將 Cython 代碼移植到 PyPy
- Limitations
- Cython 和 Pyrex 之間的區別
- 鍵入的內存視圖
- 實現緩沖協議
- 使用并行性
- 調試你的 Cython 程序
- 用于 NumPy 用戶的 Cython
- Pythran 作為 Numpy 后端