# Cython 和 Pyrex 之間的區別
> 原文: [http://docs.cython.org/en/latest/src/userguide/pyrex_differences.html](http://docs.cython.org/en/latest/src/userguide/pyrex_differences.html)
警告
Cython 和 Pyrex 都是移動目標。已經到了這一點,兩個項目之間所有差異的明確列表將很難列出和跟蹤,但希望這個高級列表能夠了解存在的差異。應該注意的是,兩個項目都努力相互兼容,但 Cython 的目標是盡可能接近并完成 Python 的合理性。
## Python 3 支持
Cython 創建了`.c`文件,可以在 Python 2.x 和 Python 3.x 中構建和使用。實際上,使用 Cython 編譯模塊很可能是將代碼移植到 Python 3 的簡單方法。
Cython 還支持 Python 3.0 和后來的主要 Python 版本附帶的各種語法添加。如果它們不與現有的 Python 2.x 語法或語義沖突,它們通常只是被編譯器接受。其他一切都取決于編譯器指令`language_level=3`(參見 [編譯器指令](source_files_and_compilation.html#compiler-directives) )。
### List / Set / Dict 理解
Cython 支持 Python 3 為列表,集和 dicts 定義的不同理解:
```py
[expr(x) for x in A] # list
{expr(x) for x in A} # set
{key(x) : value(x) for x in A} # dict
```
如果`A`是列表,元組或字典,則優化循環。您也可以使用 [`for`](https://docs.python.org/3/reference/compound_stmts.html#for "(in Python v3.7)") ... [`from`](https://docs.python.org/3/reference/simple_stmts.html#from "(in Python v3.7)") 語法,但通常最好使用通常的 [`for`](https://docs.python.org/3/reference/compound_stmts.html#for "(in Python v3.7)") ... [`in`](https://docs.python.org/3/reference/expressions.html#in "(in Python v3.7)") `range(...)`語法與 C 運行變量(例如`cdef int i`)。
注意
見 [自動量程轉換](#automatic-range-conversion)
請注意,Cython 還支持從 Python 2.4 開始的集合文字。
### 僅關鍵字參數
Python 函數可以在`*`參數之后和`**`參數之前列出僅限關鍵字的參數,例如:
```py
def f(a, b, *args, c, d = 42, e, **kwds):
...
```
這里`c`,`d`和`e`不能作為位置參數傳遞,必須作為關鍵字參數傳遞。此外,`c`和`e`是必需的關鍵字參數,因為它們沒有默認值。
如果省略`*`之后的參數名,則該函數將不接受任何額外的位置參數,例如:
```py
def g(a, b, *, c, d):
...
```
采用兩個位置參數,并有兩個必需的關鍵字參數。
## 條件表達式“x if b else y”
[https://www.python.org/dev/peps/pep-0308/](https://www.python.org/dev/peps/pep-0308/) 中描述的條件表達式:
```py
X if C else Y
```
僅評估`X`和`Y`中的一個(取決于 C 的值)。
## cdef inline
模塊級函數現在可以內聯聲明, [`inline`](#inline) 關鍵字傳遞給 C 編譯器。這些可以和宏一樣快:
```py
cdef inline int something_fast(int a, int b):
return a*a + b
```
請注意,類級 [`cdef`](language_basics.html#cdef) 函數是通過虛函數表處理的,因此幾乎在所有情況下編譯器都無法內聯它們。
## 聲明作業(例如“cdef int spam = 5”)
在 Pyrex 中,必須寫:
```py
cdef int i, j, k
i = 2
j = 5
k = 7
```
現在,有了 cython,人們可以寫:
```py
cdef int i = 2, j = 5, k = 7
```
右邊的表達可以任意復雜,例如:
```py
cdef int n = python_call(foo(x,y), a + b + c) - 32
```
## for 循環中的'by'表達式(例如“for i from 0< = i< 10 by 2”)
```py
for i from 0 <= i < 10 by 2:
print i
```
收益率:
```py
0
2
4
6
8
```
Note
不鼓勵使用此語法,因為它對于普通的 Python [`for`](https://docs.python.org/3/reference/compound_stmts.html#for "(in Python v3.7)") 循環來說是多余的。參見 [自動量程轉換](#automatic-range-conversion) 。
## 布爾 int 類型(例如,它的行為類似于 c int,但是作為布爾值強制轉換為/來自 python)
在 C 中,int 用于真值。在 python 中,任何對象都可以用作真值(使用`__nonzero__()`方法),但規范選擇是兩個布爾對象`True`和`False`。 `bint`(用于“boolean int”)類型被編譯為 C int,但是作為布爾值強制進出 Python。比較的返回類型和幾個內置函數也是`bint`。這減少了在`bool()`中包裝物品的需要。例如,人們可以寫:
```py
def is_equal(x):
return x == y
```
它將在 Pyrex 中返回`1`或`0`,但在 Cython 中返回`True`或`False`。可以聲明函數的變量和返回值為`bint`類型。例如:
```py
cdef int i = x
cdef bint b = x
```
第一次轉換將通過`x.__int__()`進行,而第二次轉換將通過`x.__bool__()`(a.k.a。`__nonzero__()`)進行,并對已知的內置類型進行適當的優化。
## 可執行類主體
包括工作 [`classmethod()`](https://docs.python.org/3/library/functions.html#classmethod "(in Python v3.7)") :
```py
cdef class Blah:
def some_method(self):
print self
some_method = classmethod(some_method)
a = 2*3
print "hi", a
```
## cpdef 函數
Cython 在通常的 [`def`](https://docs.python.org/3/reference/compound_stmts.html#def "(in Python v3.7)") 和 [`cdef`](language_basics.html#cdef) 之上添加了第三種功能類型。如果聲明了一個函數 [`cpdef`](language_basics.html#cpdef) ,它可以被擴展和普通的 python 子類調用和覆蓋。您基本上可以將 [`cpdef`](language_basics.html#cpdef) 方法視為 [`cdef`](language_basics.html#cdef) 方法+一些額外的方法。 (這就是它至少實現的方式。)首先,它創建了一個 [`def`](https://docs.python.org/3/reference/compound_stmts.html#def "(in Python v3.7)") 方法,除了調用底層 [`cdef`](language_basics.html#cdef) 方法之外什么都不做(并且如果需要,可以進行參數解包/強制) )。在 [`cdef`](language_basics.html#cdef) 方法的頂部添加了一些代碼以查看它是否被覆蓋,類似于以下偽代碼:
```py
if hasattr(type(self), '__dict__'):
foo = self.foo
if foo is not wrapper_foo:
return foo(args)
[cdef method body]
```
要檢測類型是否具有字典,它只檢查`tp_dictoffset`插槽,對于擴展類型,它是`NULL`(默認情況下),但對于實例類,則為非空。如果字典存在,它會執行單個屬性查找,并且可以(通過比較指針)判斷返回的結果是否實際上是新函數。如果且僅當它是一個新函數時,則參數被打包到元組和方法中。這一切都非常快。設置了一個標志,因此如果直接在類上調用方法,則不會發生此查找,例如:
```py
cdef class A:
cpdef foo(self):
pass
x = A()
x.foo() # will check to see if overridden
A.foo(x) # will call A's implementation whether overridden or not
```
有關說明和使用提示,請參閱 [早期綁定速度](early_binding_for_speed.html#early-binding-for-speed) 。
## 自動量程轉換
當`i`是任何 cdef'd 整數類型時,這將把`for i in range(...)`形式的語句轉換為`for i from ...`,并且可以確定方向(即步驟的符號)。
Warning
如果范圍導致賦值給`i`溢出,這可能會改變語義。具體來說,如果設置了此選項,則在輸入循環之前將引發錯誤,而如果沒有此選項,循環將執行,直到遇到溢出值。如果這會影響你,請更改`Cython/Compiler/Options.py`(最終會有更好的方法來設置它)。
## 更友好的類型鑄造
在 Pyrex 中,如果有一個類型`<int>x`,其中`x`是一個 Python 對象,那么將獲得`x`的內存地址。同樣,如果一個類型`<object>i`,其中`i`是一個 C int,那么將在內存中的`i`位置獲得一個“對象”。這會導致混亂的結果和段錯誤。
在 Cython `<type>x`中,如果其中一個類型是 python 對象,則會嘗試強制執行(如將`x`賦值給類型類型的變量時)。它不會阻止一個沒有轉換的地方(雖然它會發出警告)。如果一個人真的想要這個地址,首先要轉換為`void *`。
如在 Pyrex 中`<MyExtensionType>x`將`x`轉換為類型`MyExtensionType`而不進行任何類型檢查。 Cython 支持使用類型檢查進行轉換的語法`<MyExtensionType?>`(即如果`x`不是`MyExtensionType`的子類,它將拋出錯誤。
## cdef / cpdef 函數中的可選參數
Cython 現在支持 [`cdef`](language_basics.html#cdef) 和 [`cpdef`](language_basics.html#cpdef) 功能的可選參數。
`.pyx`文件中的語法保留在 Python 中,但是通過寫`cdef foo(x=*)`在`.pxd`文件中聲明了這些函數。子類化時參數的數量可能會增加,但參數類型和順序必須保持不變。在某些情況下,如果沒有任何可選項的 cdef / cpdef 函數被一個具有默認參數值的函數覆蓋,則會有輕微的性能損失。
例如,可以有`.pxd`文件:
```py
cdef class A:
cdef foo(self)
cdef class B(A):
cdef foo(self, x=*)
cdef class C(B):
cpdef foo(self, x=*, int k=*)
```
使用相應的`.pyx`文件:
```py
from __future__ import print_function
cdef class A:
cdef foo(self):
print("A")
cdef class B(A):
cdef foo(self, x=None):
print("B", x)
cdef class C(B):
cpdef foo(self, x=True, int k=3):
print("C", x, k)
```
Note
這也證明了 [`cpdef`](language_basics.html#cpdef) 功能如何覆蓋 [`cdef`](language_basics.html#cdef) 功能。
## 結構中的函數指針
為方便起見, [`struct`](language_basics.html#struct) 中聲明的函數會自動轉換為函數指針。
## C ++異常處理
[`cdef`](language_basics.html#cdef) 函數現在可以聲明為:
```py
cdef int foo(...) except +
cdef int foo(...) except +TypeError
cdef int foo(...) except +python_error_raising_function
```
在這種情況下,捕獲 C ++錯誤時將引發 Python 異常。有關詳細信息,請參閱 [在 Cython](wrapping_CPlusPlus.html#wrapping-cplusplus)中使用 C ++。
## 同義詞
`cdef import from`與`cdef extern from`的含義相同
## 源代碼編碼
Cython 支持 [**PEP 3120** ](https://www.python.org/dev/peps/pep-3120)和 [**PEP 263** ](https://www.python.org/dev/peps/pep-0263),即你可以開始你的 Cython 帶有編碼注釋的源文件,通常用 UTF-8 編寫源代碼。這會影響字節字符串的編碼以及將`u'abcd'`等 unicode 字符串文字轉換為 unicode 對象。
## 自動`typecheck`
而不是像 [Pyrex 文檔](https://www.cosc.canterbury.ac.nz/greg.ewing/python/Pyrex/version/Doc/Manual/special_methods.html)中所解釋的那樣引入新關鍵字`typecheck`,只要 [`isinstance()`](https://docs.python.org/3/library/functions.html#isinstance "(in Python v3.7)") 與擴展類型一起使用,Cython 就會發出(非欺騙性和更快速)的類型檢查第二個參數。
## 來自 __future__ 指令
Cython 支持多種`from __future__ import ...`指令,即`absolute_import`,`unicode_literals`,`print_function`和`division`。
始終啟用語句。
## 純 Python 模式
Cython 支持編譯`.py`文件,并使用裝飾器和其他有效的 Python 語法接受類型注釋。這允許將相同的源解釋為直接 Python,或者編譯以獲得優化結果。有關詳細信息,請參閱 [純 Python 模式](../tutorial/pure.html#pure-mode) 。
- 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 后端