# 語言基礎
> 原文: [http://docs.cython.org/en/latest/src/userguide/language_basics.html](http://docs.cython.org/en/latest/src/userguide/language_basics.html)
## 聲明數據類型
作為一種動態語言,Python 鼓勵一種編程風格,即在方法和屬性方面考慮類和對象,而不是它們適合類層次結構。
這可以使 Python 成為一種非常輕松和舒適的快速開發語言,但需要付出代價 - 管理數據類型的“繁文縟節”會被轉儲到解釋器上。在運行時,解釋器在搜索命名空間,獲取屬性以及解析參數和關鍵字元組方面做了大量工作。與“早期綁定”語言(如 C ++)相比,這種運行時“后期綁定”是 Python 相對緩慢的主要原因。
然而,使用 Cython,通過使用“早期綁定”編程技術可以獲得顯著的加速。
注意
打字不是必需品
為參數和變量提供靜態類型可以方便地加速代碼,但這不是必需的。優化需要的地點和時間。實際上,在鍵入不允許優化但 Cython 仍然需要檢查某個對象的類型是否與聲明的類型匹配的情況下,鍵入 _ 可以減慢 _ 你的代碼。
## C 變量和類型定義
[`cdef`](#cdef) 語句用于聲明 C 變量,無論是本地變量還是模塊級變量:
```py
cdef int i, j, k
cdef float f, g[42], *h
```
和 [`struct`](#struct) , [`union`](#union) 或 [`enum`](#enum) 類型:
```py
cdef struct Grail:
int age
float volume
cdef union Food:
char *spam
float *eggs
cdef enum CheeseType:
cheddar, edam,
camembert
cdef enum CheeseState:
hard = 1
soft = 2
runny = 3
```
另見 [struct,union 和 enum 聲明的樣式](external_C_code.html#struct-union-enum-styles)
Note
結構可以聲明為`cdef packed struct`,其效果與 C 指令`#pragma pack(1)`相同。
將枚舉聲明為`cpdef`將創建 [**PEP 435** ](https://www.python.org/dev/peps/pep-0435)風格的 Python 包裝器:
```py
cpdef enum CheeseState:
hard = 1
soft = 2
runny = 3
```
目前沒有用于定義常量的特殊語法,但您可以使用匿名 [`enum`](#enum) 聲明來實現此目的,例如:
```py
cdef enum:
tons_of_spam = 3
```
Note
單詞`struct`,`union`和`enum`僅在定義類型時使用,而不是在引用時使用。例如,要聲明一個指向`Grail`的變量,您將編寫:
```py
cdef Grail *gp
```
并不是:
```py
cdef struct Grail *gp # WRONG
```
還有一個`ctypedef`語句用于為類型命名,例如:
```py
ctypedef unsigned long ULong
ctypedef int* IntPtr
```
也可以用 [`cdef`](#cdef) 聲明函數,使它們成為 c 函數。
```py
cdef int eggs(unsigned long l, float f):
...
```
您可以在 [Python 函數與 C 函數](#python-functions-vs-c-functions) 中閱讀更多相關信息。
您可以使用 [`cdef`](#cdef) 聲明類,使它們成為 [擴展類型](extension_types.html#extension-types) 。那些將具有非常接近 python 類的行為,但速度更快,因為它們在內部使用`struct`來存儲屬性。
這是一個簡單的例子:
```py
from __future__ import print_function
cdef class Shrubbery:
cdef int width, height
def __init__(self, w, h):
self.width = w
self.height = h
def describe(self):
print("This shrubbery is", self.width,
"by", self.height, "cubits.")
```
您可以在 [擴展類型](extension_types.html#extension-types) 中閱讀更多相關信息。
### 類型
Cython 使用 C 類型的常規 C 語法,包括指針。它提供所有標準 C 類型,即`char`,`short`,`int`,`long`,`long long`以及它們的`unsigned`版本,例如`unsigned int`。特殊`bint`類型用于 C 布爾值(`int`,其中 0 /非 0 值為 False / True)和`Py_ssize_t`用于(容器)的(簽名)大小。
通過將`*`附加到它們指向的基本類型,例如,在 C 中構造指針類型。 `int**`指向指向 C int 的指針。數組使用普通的 C 數組語法,例如, `int[10]`,并且在編譯時必須知道堆棧分配的數組的大小。 Cython 不支持 C99 的可變長度數組。請注意,Cython 使用數組訪問進行指針解除引用,因為`*x`不是有效的 Python 語法,而`x[0]`是。
此外,Python 類型`list`,`dict`,`tuple`等可用于靜態類型,以及任何用戶定義的 [擴展類型](extension_types.html#extension-types) 。例如:
```py
cdef list foo = []
```
這需要類的 _ 精確 _ 匹配,它不允許子類。這允許 Cython 通過訪問內置類的內部來優化代碼。對于這種類型的輸入,Cython 在內部使用`PyObject*`類型的 C 變量。 Python 類型 int,long 和 float 不能用于靜態類型,而是分別解釋為 C `int`,`long`和`float`,因為使用這些 Python 類型的靜態類型變量沒有任何優勢。
Cython 提供了一個加速和類型化的 Python 元組,即`ctuple`。 `ctuple`由任何有效的 C 類型組裝而成。例如:
```py
cdef (double, int) bar
```
它們編譯成 C 結構,可以用作 Python 元組的有效替代品。
雖然這些 C 類型可以非常快,但它們具有 C 語義。具體來說,整數類型溢出,C `float`類型只有 32 位精度(而不是 Python 浮動包裝的 64 位 C `double`,而且通常是你想要的)。如果要使用這些數字 Python 類型,只需省略類型聲明并將它們作為對象。
也可以聲明 [擴展類型](extension_types.html#extension-types) (用`cdef class`聲明)。這確實允許子類。此類型主要用于訪問`cdef`方法和擴展類型的屬性。 C 代碼使用一個變量,它是指向特定類型結構的指針,類似于`struct MyExtensionTypeObject*`。
### 分組多個 C 聲明
如果您有一系列聲明都以 [`cdef`](#cdef) 開頭,您可以將它們分組為 [`cdef`](#cdef) 塊,如下所示:
```py
from __future__ import print_function
cdef:
struct Spam:
int tons
int i
float a
Spam *p
void f(Spam *s):
print(s.tons, "Tons of spam")
```
## Python 函數與 C 函數
Cython 中有兩種函數定義:
Python 函數是使用 def 語句定義的,就像在 Python 中一樣。它們將 Python 對象作為參數并返回 Python 對象。
C 函數使用新的 [`cdef`](#cdef) 語句定義。它們將 Python 對象或 C 值作為參數,并且可以返回 Python 對象或 C 值。
在 Cython 模塊中,Python 函數和 C 函數可以自由地相互調用,但只能通過解釋的 Python 代碼從模塊外部調用 Python 函數。因此,您希望從 Cython 模塊“導出”的任何函數都必須使用 def 聲明為 Python 函數。還有一種稱為 [`cpdef`](#cpdef) 的混合功能。可以從任何地方調用 [`cpdef`](#cpdef) ,但在從其他 Cython 代碼調用時使用更快的 C 調用約定。 [`cpdef`](#cpdef) 也可以在子類或實例屬性上被 Python 方法覆蓋,即使從 Cython 調用也是如此。如果發生這種情況,大多數性能提升當然都會丟失,即使它沒有,與調用 [`cdef`](#cdef) 相比,從 Cython 調用 [`cpdef`](#cpdef) 方法的開銷很小。 ] 方法。
可以使用常規 C 聲明語法將任一類型的函數的參數聲明為具有 C 數據類型。例如,:
```py
def spam(int i, char *s):
...
cdef int eggs(unsigned long l, float f):
...
```
也可以使用`ctuples`:
```py
cdef (int, float) chips((long, long, double) t):
...
```
當 Python 函數的參數聲明為具有 C 數據類型時,它將作為 Python 對象傳入,并在可能的情況下自動轉換為 C 值。換句話說,上面`spam`的定義等同于寫作:
```py
def spam(python_i, python_s):
cdef int i = python_i
cdef char* s = python_s
...
```
目前,只能對數字類型,字符串類型和結構進行自動轉換(以遞歸方式組合任何這些類型);嘗試將任何其他類型用于 Python 函數的參數將導致編譯時錯誤。如果要在調用后使用指針,必須小心使用字符串以確保引用。可以從 Python 映射中獲取結構,如果要在函數返回后使用它們,則必須再次使用字符串屬性。
另一方面,C 函數可以具有任何類型的參數,因為它們是使用普通的 C 函數調用直接傳遞的。
使用 [`cdef`](#cdef) 與 Python 對象返回類型聲明的函數(如 Python 函數)將在執行離開函數體時返回`None`值而沒有顯式返回值。這與 C / C ++形成對比,后者使返回值未定義。在非 Python 對象返回類型的情況下,返回等效于零的值,例如,對于`int`為 0,對于`bint`為`False`,對于指針類型為`NULL`。
可以在 [早期結合速度](early_binding_for_speed.html#early-binding-for-speed) 中找到這些不同方法類型的優缺點的更完整比較。
### Python 對象作為參數和返回值
如果沒有為參數或返回值指定類型,則假定它是 Python 對象。 (請注意,這與 C 約定不同,它默認為 int。)例如,下面定義了一個 C 函數,它將兩個 Python 對象作為參數并返回一個 Python 對象:
```py
cdef spamobjs(x, y):
...
```
根據標準 Python / C API 規則自動執行這些對象的引用計數(即,借用的引用被視為參數并返回新引用)。
> 警告
>
> 這僅適用于 Cython 代碼。在 C 中實現的其他 Python 包(如 NumPy)可能不遵循這些約定。
name 對象也可用于顯式聲明某些內容為 Python 對象。如果聲明的名稱將被視為類型的名稱,這可能很有用,例如:
```py
cdef ftang(object int):
...
```
聲明一個名為 int 的參數,它是一個 Python 對象。您還可以使用 object 作為函數的顯式返回類型,例如:
```py
cdef object ftang(object int):
...
```
為了清楚起見,始終明確 C 函數中的對象參數可能是個好主意。
要創建借用引用,請將參數類型指定為`PyObject*`。 Cython 不會執行自動`Py_INCREF`或`Py_DECREF`,例如:
將顯示:
```py
Initial refcount: 2
Inside owned_reference: 3
Inside borrowed_reference: 2
```
### 可選參數
與 C 不同,可以在`cdef`和`cpdef`函數中使用可選參數。但是,無論是在`.pyx`文件還是相應的`.pxd`文件中聲明它們,都存在差異。
為避免重復(以及潛在的未來不一致),默認參數值在聲明中(在`.pxd`文件中)不可見,但僅在實現中(在`.pyx`文件中)。
在`.pyx`文件中,簽名與 Python 本身的簽名相同:
```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)
```
在`.pxd`文件中,簽名與此示例不同:`cdef foo(x=*)`。這是因為調用函數的程序只需知道 C 中可能的簽名,但不需要知道默認參數的值:
```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=*)
```
Note
子類化時參數的數量可能會增加,但 arg 類型和順序必須相同,如上例所示。
當使用沒有默認值的可選 arg 覆蓋可選 arg 時,可能會有輕微的性能損失。
### 僅關鍵字參數
與在 Python 3 中一樣,`def`函數可以在`"*"`參數之后和`"**"`參數之前列出僅限關鍵字的參數(如果有):
```py
def f(a, b, *args, c, d = 42, e, **kwds):
...
# We cannot call f with less verbosity than this.
foo = f(4, "bar", c=68, e=1.0)
```
如上所示,`c`,`d`和`e`參數不能作為位置參數傳遞,必須作為關鍵字參數傳遞。此外,`c`和`e`是**必需**關鍵字參數,因為它們沒有默認值。
沒有參數名稱的單個`"*"`可用于終止位置參數列表:
```py
def g(a, b, *, c, d):
...
# We cannot call g with less verbosity than this.
foo = g(4.0, "something", c=68, d="other")
```
如上所示,簽名只需要兩個位置參數,并且有兩個必需的關鍵字參數。
### 功能指針
在`struct`中聲明的函數會自動轉換為函數指針。
有關使用帶有功能指針的錯誤返回值,請參見 [錯誤返回值](#error-return-values) 底部的注釋。
### 錯誤返回值
如果你沒有做任何特殊的事情,用 [`cdef`](#cdef) 聲明的不返回 Python 對象的函數無法向其調用者報告 Python 異常。如果在此類函數中檢測到異常,則會打印警告消息并忽略該異常。
如果你想要一個不返回 Python 對象的 C 函數能夠將異常傳播給它的調用者,你需要為它聲明一個異常值。這是一個例子:
```py
cdef int spam() except -1:
...
```
使用此聲明,每當垃圾郵件內發生異常時,它將立即返回值`-1`。此外,每當對垃圾郵件的調用返回`-1`時,將假定已發生異常并將進行傳播。
聲明函數的異常值時,不應顯式或隱式返回該值。特別是,如果異常返回值是`False`值,那么您應該確保該函數永遠不會通過隱式或空返回終止。
如果所有可能的返回值都是合法的,并且您不能完全為信號錯誤保留一個,則可以使用另一種形式的異常值聲明:
```py
cdef int spam() except? -1:
...
```
“?”表示值`-1`僅表示可能的錯誤。在這種情況下,如果返回異常值,Cython 會生成對 [`PyErr_Occurred()`](https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred "(in Python v3.7)") 的調用,以確保它確實是一個錯誤。
還有第三種形式的例外價值聲明:
```py
cdef int spam() except *:
...
```
這種形式導致 Cython 在每次調用垃圾郵件后生成對 [`PyErr_Occurred()`](https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred "(in Python v3.7)") 的調用,無論它返回什么值。如果您有一個返回 void 的函數需要傳播錯誤,則必須使用此表單,因為沒有任何要測試的返回值。否則,此表格幾乎沒有用處。
可以使用以下命令聲明可能引發異常的外部 C ++函數:
```py
cdef int spam() except +
```
有關詳細信息,請參閱 [在 Cython](wrapping_CPlusPlus.html#wrapping-cplusplus)中使用 C ++。
有些事情需要注意:
* 異常值只能為返回整數,枚舉,浮點或指針類型的函數聲明,并且值必須是常量表達式。 Void 函數只能使用`except *`表單。
* 異常值規范是函數簽名的一部分。如果您將指向函數的指針作為參數傳遞或將其指定給變量,則聲明的參數或變量類型必須具有相同的異常值規范(或缺少該規范)。以下是帶有異常值的指針到函數聲明的示例:
```py
int (*grail)(int, char*) except -1
```
* 您不需要(也不應該)為返回 Python 對象的函數聲明異常值。請記住,沒有聲明返回類型的函數會隱式返回 Python 對象。 (通過返回 NULL 隱式傳播此類函數的異常。)
### 檢查非 Cython 函數的返回值
重要的是要理解,當返回指定的值時,except 子句不會引發錯誤。例如,你不能寫像:
```py
cdef extern FILE *fopen(char *filename, char *mode) except NULL # WRONG!
```
并且如果對`fopen()`的調用返回`NULL`,則期望自動引發異常。 except 子句不起作用;它的唯一目的是傳播已經引發的 Python 異常,可以通過 Cython 函數或調用 Python / C API 例程的 C 函數來傳播。要從諸如`fopen()`的非 Python 感知函數中獲取異常,您必須檢查返回值并自行引發它,例如:
```py
from libc.stdio cimport FILE, fopen
from libc.stdlib cimport malloc, free
from cpython.exc cimport PyErr_SetFromErrnoWithFilenameObject
def open_file():
cdef FILE* p
p = fopen("spam.txt", "r")
if p is NULL:
PyErr_SetFromErrnoWithFilenameObject(OSError, "spam.txt")
...
def allocating_memory(number=10):
cdef double *my_array = <double *> malloc(number * sizeof(double))
if not my_array: # same as 'is NULL' above
raise MemoryError()
...
free(my_array)
```
### 覆蓋擴展類型
`cpdef`方法可以覆蓋`cdef`方法:
```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)
```
當使用 Python 類繼承擴展類型時,`def`方法可以覆蓋`cpdef`方法但不能覆蓋`cdef`方法:
```py
from __future__ import print_function
cdef class A:
cdef foo(self):
print("A")
cdef class B(A):
cpdef foo(self):
print("B")
class C(B): # NOTE: not cdef class
def foo(self):
print("C")
```
如果上面的`C`是擴展類型(`cdef class`),這將無法正常工作。在這種情況下,Cython 編譯器將發出警告。
## 自動類型轉換
在大多數情況下,當在需要 C 值的上下文中使用 Python 對象時,將對基本數字和字符串類型執行自動轉換,反之亦然。下表總結了轉換的可能性。
<colgroup><col width="42%"> <col width="30%"> <col width="27%"></colgroup>
| C 類型 | 從 Python 類型 | 到 Python 類型 |
| --- | --- | --- |
| [unsigned] char,[unsigned] short,int,long | int,long | INT |
| unsigned int,unsigned long,[unsigned] long long | int, long | 長 |
| 浮,雙,長雙 | int,long,float | 浮動 |
| 字符* | STR /字節 | str / bytes [[3]](#id13) |
| C 數組 | 迭代 | 清單 [[5]](#id15) |
| 結構,聯合 | | 字典 [[4]](#id14) |
<colgroup><col class="label"><col></colgroup>
| [[3]](#id10) | 對于 Python 2.x,轉換為/來自 str,對于 Python 3.x,轉換為字節。 |
<colgroup><col class="label"><col></colgroup>
| [[4]](#id12) | 從 C union 類型到 Python dict 的轉換將為每個 union 字段添加一個值。但是,Cython 0.23 及更高版本將拒絕自動轉換具有不安全類型組合的聯合。一個例子是`int`和`char*`的并集,在這種情況下,指針值可能是也可能不是有效指針。 |
<colgroup><col class="label"><col></colgroup>
| [[5]](#id11) | 除了 signed / unsigned char []。如果在編譯時未知 C 數組的長度,并且使用 C 數組的切片,則轉換將失敗。 |
### 在 C 語境中使用 Python 字符串時的注意事項
在期望`char*`的上下文中使用 Python 字符串時需要小心。在這種情況下,使用指向 Python 字符串內容的指針,只有 Python 字符串存在時才有效。因此,只要需要 C 字符串,就需要確保保留對原始 Python 字符串的引用。如果您不能保證 Python 字符串的存活時間足夠長,則需要復制 C 字符串。
Cython 檢測并防止這種錯誤。例如,如果您嘗試以下內容:
```py
cdef char *s
s = pystring1 + pystring2
```
然后 Cython 將產生錯誤消息`Obtaining char* from temporary Python value`。原因是連接兩個 Python 字符串會產生一個新的 Python 字符串對象,該對象僅由 Cython 生成的臨時內部變量引用。語句完成后,臨時變量將被刪除,Python 字符串被釋放,`s`懸空。由于此代碼無法工作,因此 Cython 拒絕編譯它。
解決方案是將串聯的結果賦給 Python 變量,然后從中獲取`char*`,即:
```py
cdef char *s
p = pystring1 + pystring2
s = p
```
然后,您有責任在必要時保留參考 p。
請記住,用于檢測此類錯誤的規則僅是啟發式。有時 Cython 會不必要地抱怨,有時它會無法檢測到存在的問題。最終,您需要了解問題并小心您的工作。
### 類型轉換
C 使用`"("`和`")"`,Cython 使用`"<"`和`">"`。例如:
```py
cdef char *p
cdef float *q
p = <char*>q
```
將 C 值轉換為 Python 對象類型或反之時,Cython 將嘗試強制。簡單示例是類似`<int>pyobj`的轉換,它將 Python 編號轉換為普通的 C `int`值,或者`<bytes>charptr`,它將 C `char*`字符串復制到新的 Python 字節對象中。
> Note
>
> Cython 不會阻止冗余演員,但會發出警告。
要獲取某些 Python 對象的地址,請使用強制轉換為`<void*>`或`<PyObject*>`等指針類型。您還可以使用`<object>`或更具體的內置或擴展類型(例如`<MyExtType>ptr`)將 C 指針強制轉換回 Python 對象引用。這將使對象的引用計數增加 1,即轉換返回擁有的引用。這是一個例子:
```py
from cpython.ref cimport PyObject
cdef extern from *:
ctypedef Py_ssize_t Py_intptr_t
python_string = "foo"
cdef void* ptr = <void*>python_string
cdef Py_intptr_t adress_in_c = <Py_intptr_t>ptr
address_from_void = adress_in_c # address_from_void is a python int
cdef PyObject* ptr2 = <PyObject*>python_string
cdef Py_intptr_t address_in_c2 = <Py_intptr_t>ptr2
address_from_PyObject = address_in_c2 # address_from_PyObject is a python int
assert address_from_void == address_from_PyObject == id(python_string)
print(<object>ptr) # Prints "foo"
print(<object>ptr2) # prints "foo"
```
`<...>`的優先級是`<type>a.b.c`被解釋為`<type>(a.b.c)`。
轉換為`<object>`會創建一個自有引用。 Cython 將自動執行`Py_INCREF`和`Py_DECREF`操作。轉換為`<PyObject *>`會創建一個借用的引用,保持引用不變。
### 檢查類型轉換
像`<MyExtensionType>x`這樣的強制轉換會將 x 轉換為類`MyExtensionType`而不進行任何檢查。
要檢查強制轉換,請使用如下語法:`<MyExtensionType?>x`。在這種情況下,如果`x`不是`MyExtensionType`的實例,Cython 將應用運行時檢查,該檢查會引發`TypeError`。這將測試內置類型的確切類,但允許 [擴展類型](extension_types.html#extension-types) 的子類。
## 陳述和表達
控件結構和表達式大部分都遵循 Python 語法。當應用于 Python 對象時,它們具有與 Python 相同的語義(除非另有說明)。大多數 Python 運算符也可以應用于 C 值,具有明顯的語義。
如果在表達式中混合使用 Python 對象和 C 值,則會在 Python 對象和 C 數字或字符串類型之間自動執行轉換。
為所有 Python 對象自動維護引用計數,并自動檢查所有 Python 操作是否有錯誤,并采取適當的操作。
### C 和 Cython 表達式之間的差異
C 表達式和 Cython 表達式之間在語法和語義上存在一些差異,特別是在 C 語言結構領域,它們在 Python 中沒有直接的等價物。
* 整數文字被視為 C 常量,并將被截斷為 C 編譯器認為合適的任何大小。獲取 Python 整數(任意精度)立即轉換為對象(例如`<object>100000000000000000000`)。 `L`,`LL`和`U`后綴與 C 中的含義相同。
* Cython 中沒有`->`運算符。而不是`p->x`,使用`p.x`
* 在 Cython 中沒有一元`*`運算符。而不是`*p`,使用`p[0]`
* 有一個`&`運算符,其語義與 C 中相同。
* 空 C 指針稱為`NULL`,而不是`0`(`NULL`是保留字)。
* 類型轉換被寫為`<type>value`,例如:
```py
cdef char* p, float* q
p = <char*>q
```
### 范圍規則
Cython 完全靜態地確定變量是屬于本地范圍,模塊范圍還是內置范圍。與 Python 一樣,分配給未以其他方式聲明隱式聲明的變量將其聲明為駐留在分配范圍內的變量。變量的類型取決于類型推斷,但全局模塊范圍除外,它始終是 Python 對象。
### 內置函數
Cython 將對大多數內置函數的調用編譯為對相應 Python / C API 例程的直接調用,使得它們特別快。
僅優化使用這些名稱的直接函數調用。如果你使用其中一個名稱假設它是 Python 對象,比如將其分配給 Python 變量,然后調用它,那么調用將作為 Python 函數調用。
<colgroup><col width="42%"> <col width="18%"> <col width="39%"></colgroup>
| 功能和參數 | 返回類型 | Python / C API 等效 |
| --- | --- | --- |
| ABS(OBJ) | 對象,雙,...... | PyNumber_Absolute,fabs,fabsf,...... |
| 可調用(OBJ) | 賓特 | PyObject_Callable |
| delattr(obj,名字) | 沒有 | PyObject_DelAttr |
| exec(代碼,[glob,[loc]]) | 賓語 | |
| DIR(OBJ) | 名單 | PyObject_Dir |
| divmod(a,b) | 元組 | PyNumber_Divmod |
| getattr(obj,name,[default])(注 1) | object | PyObject_GetAttr |
| hasattr(obj,name) | bint | PyObject_HasAttr |
| 哈希(OBJ) | int / long | PyObject_Hash |
| 實習生(OBJ) | object | PY * _InternFromString |
| isinstance(obj,type) | bint | PyObject_IsInstance |
| issubclass(obj,type) | bint | PyObject_IsSubclass |
| iter(obj,[sentinel]) | object | PyObject_GetIter |
| LEN(OBJ) | Py_ssize_t | PyObject_Length |
| pow(x,y,[z]) | object | PyNumber_Power |
| 重裝(OBJ) | object | PyImport_ReloadModule |
| 再版(OBJ) | object | PyObject_Repr |
| setattr(obj,name) | 空虛 | PyObject_SetAttr |
注 1:Pyrex 最初提供了一個函數`getattr3(obj, name, default)()`,對應于 Python 內置 [`getattr()`](https://docs.python.org/3/library/functions.html#getattr "(in Python v3.7)") 的三參數形式。 Cython 仍然支持這個功能,但不贊成使用普通的內置,而 Cython 可以在兩種形式中進行優化。
### 運算符優先級
請記住,Python 和 C 之間的運算符優先級存在一些差異,并且 Cython 使用 Python 優先級,而不是 C 優先級。
### 整數 for 循環
Cython 識別通常的 Python for-in-range 整數循環模式:
```py
for i in range(n):
...
```
如果`i`被聲明為 [`cdef`](#cdef) 整數類型,它會將其優化為純 C 循環。需要此限制,否則由于目標體系結構上的潛在整數溢出,生成的代碼將不正確。如果您擔心循環未正確轉換,請使用 cython 命令行(`-a`)的 annotate 功能輕松查看生成的 C 代碼。見 [自動量程轉換](pyrex_differences.html#automatic-range-conversion)
為了向后兼容 Pyrex,Cython 還支持更詳細的 for 循環形式,您可以在遺留代碼中找到它:
```py
for i from 0 <= i < n:
...
```
要么:
```py
for i from 0 <= i < n by s:
...
```
其中`s`是一個整數步長。
Note
不推薦使用此語法,不應在新代碼中使用。請使用普通的 Python for 循環。
有關 for-from 循環的一些注意事項:
* 目標表達式必須是普通變量名稱。
* 下限和上限之間的名稱必須與目標名稱相同。
* 迭代的方向由關系決定。如果它們都來自集合{`<`,`<=`}則它是向上的;如果他們都來自集合{`>`,`>=`}那么它是向下的。 (不允許任何其他組合。)
與其他 Python 循環語句一樣,break 和 continue 可以在 body 中使用,循環可以有 else 子句。
## Cython 文件類型
Cython 中有三種文件類型:
* 實現文件,帶有`.py`或`.pyx`后綴。
* 定義文件,帶有`.pxd`后綴。
* 包含文件,帶有`.pxi`后綴。
### 實現文件
顧名思義,實現文件包含函數,類,擴展類型等的實現。此文件幾乎支持所有 python 語法。大多數情況下,`.py`文件可以在不更改任何代碼的情況下重命名為`.pyx`文件,Cython 將保留 python 行為。
Cython 可以編譯`.py`和`.pyx`文件。如果只想使用 Python 語法,則文件名不重要,Cython 不會根據使用的后綴更改生成的代碼。但是,如果想要使用 Cython 語法,則需要使用`.pyx`文件。
除了 Python 語法之外,用戶還可以利用 Cython 語法(例如`cdef`)來使用 C 變量,可以將函數聲明為`cdef`或`cpdef`,并可以使用 [`cimport`導入 C 定義](sharing_declarations.html#cimport)。在本頁和 Cython 文檔的其余部分中可以找到許多其他可用于實現文件的 Cython 功能。
如果相應的定義文件也定義了該類型,則對某些 [擴展類型](extension_types.html#extension-types) 的實現部分有一些限制。
Note
編譯`.pyx`文件時,Cython 首先檢查相應的`.pxd`文件是否存在并首先處理它。它就像一個 Cython `.pyx`文件的頭文件。您可以放入其他 Cython 模塊將使用的內部函數。這允許不同的 Cython 模塊在沒有 Python 開銷的情況下使用彼此的函數和類。要了解更多有關如何操作的信息,可以看 [pxd 文件](../tutorial/pxd_files.html#pxd-files) 。
### 定義文件
定義文件用于聲明各種事物。
可以進行任何 C 聲明,它也可以是 C / C ++文件中實現的 C 變量或函數的聲明。這可以通過`cdef extern from`完成。有時,`.pxd`文件用作 C / C ++頭文件到 Cython 可以理解的語法的轉換。這允許 C / C ++變量和函數直接用于 [`cimport`](sharing_declarations.html#cimport) 的實現文件中。您可以在 [與外部 C 代碼](external_C_code.html#external-c-code) 和 [的接口中使用 Cython](wrapping_CPlusPlus.html#wrapping-cplusplus)中的 C ++閱讀更多相關信息。
它還可以包含擴展類型的定義部分和外部庫的函數聲明。
它不能包含任何 C 或 Python 函數的實現,也不能包含任何 Python 類定義或任何可執行語句。當想要訪問 [`cdef`](#cdef) 屬性和方法,或從本模塊中定義的 [`cdef`](#cdef) 類繼承時,需要它。
Note
您不需要(也不應該)在聲明文件 [`public`](extension_types.html#public) 中聲明任何內容,以使其可用于其他 Cython 模塊;它只是存在于定義文件中。如果要為外部 C 代碼提供某些內容,則只需要公開聲明。
### include 語句和包含文件
Warning
歷史上,`include`語句用于共享聲明。使用 [在 Cython 模塊](sharing_declarations.html#sharing-declarations) 之間共享聲明。
Cython 源文件可以使用 include 語句包含來自其他文件的材料,例如:
```py
include "spamstuff.pxi"
```
指定文件的內容在文本上包含在該點。包含的文件可以包含在 include 語句出現的上下文中有效的任何完整語句或聲明,包括其他 include 語句。包含文件的內容應該以縮進級別零開始,并且將被視為縮進到包含該文件的 include 語句的級別。但是,include 語句不能在模塊范圍之外使用,例如在函數或類主體內部。
Note
還有其他機制可用于將 Cython 代碼拆分為單獨的部分,在許多情況下可能更合適。參見 [在 Cython 模塊](sharing_declarations.html#sharing-declarations) 之間共享聲明。
## 條件編譯
某些功能可用于 Cython 源文件中的條件編譯和編譯時常量。
### 編譯時定義
可以使用 DEF 語句定義編譯時常量:
```py
DEF FavouriteFood = u"spam"
DEF ArraySize = 42
DEF OtherArraySize = 2 * ArraySize + 17
```
`DEF`的右側必須是有效的編譯時表達式。這些表達式由使用`DEF`語句定義的文字值和名稱組成,使用任何 Python 表達式語法進行組合。
預定義了以下編譯時名稱,對應于 [`os.uname()`](https://docs.python.org/3/library/os.html#os.uname "(in Python v3.7)") 返回的值。
> UNAME_SYSNAME,UNAME_NODENAME,UNAME_RELEASE,UNAME_VERSION,UNAME_MACHINE
還提供以下內置常量和函數選擇:
> None,True,False,abs,all,any,ascii,bin,bool,bytearray,bytes,chr,cmp,complex,dict,divmod,enumerate,filter,float,format,frozenset,hash,hex,int ,len,list,long,map,max,min,oct,ord,pow,range,reduce,repr,reverse,round,set,slice,sorted,str,sum,tuple,xrange,zip
請注意,在 Python 2.x 或 3.x 下編譯時,其中一些內置函數可能不可用,或者兩者中的行為可能不同。
使用`DEF`定義的名稱可以在標識符出現的任何地方使用,并且用其編譯時值替換,就好像它在那時作為文字寫入源中一樣。為此,編譯時表達式必須求值為`int`,`long`,`float`,`bytes`或`unicode`(Py3 中的`str`)類型的 Python 值。
```py
from __future__ import print_function
DEF FavouriteFood = u"spam"
DEF ArraySize = 42
DEF OtherArraySize = 2 * ArraySize + 17
cdef int a1[ArraySize]
cdef int a2[OtherArraySize]
print("I like", FavouriteFood)
```
### 條件語句
`IF`語句可用于在編譯時有條件地包含或排除代碼段。它的工作方式與 C 中的`#if`預處理程序指令類似:
```py
IF UNAME_SYSNAME == "Windows":
include "icky_definitions.pxi"
ELIF UNAME_SYSNAME == "Darwin":
include "nice_definitions.pxi"
ELIF UNAME_SYSNAME == "Linux":
include "penguin_definitions.pxi"
ELSE:
include "other_definitions.pxi"
```
`ELIF`和`ELSE`子句是可選的。 `IF`語句可以出現在正常語句或聲明可以出現的任何地方,并且它可以包含在該上下文中有效的任何語句或聲明,包括`DEF`語句和其他`IF`語句。
`IF`和`ELIF`子句中的表達式必須是`DEF`語句的有效編譯時表達式,盡管它們可以計算任何 Python 值,并且結果的真實性以通常的 Python 方式確定。
- 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 后端