ctypes
- 用于Python的外部函數(shù)庫2.5版中的新功能。
ctypes
是Python的外部函數(shù)庫。它提供C兼容的數(shù)據(jù)類型,并允許在DLL或共享庫中調(diào)用函數(shù)。它可以用于在純Python中包裝這些庫。
注意:本教程中的代碼示例doctest
用于確保它們實際工作。由于某些代碼示例在Linux,Windows或Mac OS X下的行為不同,因此它們在注釋中包含doctest指令。
注意:某些代碼示例引用了ctypes c_int
類型。此類型是c_long
32位系統(tǒng)上類型的別名。因此,c_long
如果您打算如果打印,則不應(yīng)該感到困惑c_int
- 它們實際上是相同的類型。
ctypes
導(dǎo)出cdll,以及Windows windll和oledll 對象,用于加載動態(tài)鏈接庫。
您可以通過訪問它們作為這些對象的屬性來加載庫。cdll 加載使用標(biāo)準(zhǔn)cdecl
調(diào)用約定導(dǎo)出函數(shù)的庫,而windll庫使用stdcall
調(diào)用約定調(diào)用函數(shù)。oledll還使用stdcall
調(diào)用約定,并假定函數(shù)返回Windows HRESULT
錯誤代碼。錯誤代碼用于WindowsError
在函數(shù)調(diào)用失敗時自動引發(fā)異常。
以下是Windows的一些示例。注意,它msvcrt
是包含大多數(shù)標(biāo)準(zhǔn)C函數(shù)的MS標(biāo)準(zhǔn)C庫,并使用cdecl調(diào)用約定:
- >>> from ctypes import *
- >>> print windll.kernel32
- <WinDLL 'kernel32', handle ... at ...>
- >>> print cdll.msvcrt
- <CDLL 'msvcrt', handle ... at ...>
- >>> libc = cdll.msvcrt
- >>>
Windows會.dll
自動附加常用的文件后綴。
在Linux上,需要指定包含加載庫的擴展名的文件名,因此不能使用屬性訪問來加載庫。LoadLibrary()
應(yīng)該使用dll加載器的 方法,或者你應(yīng)該通過調(diào)用構(gòu)造函數(shù)創(chuàng)建一個CDLL實例來加載庫:
- >>> cdll.LoadLibrary("libc.so.6")
- <CDLL 'libc.so.6', handle ... at ...>
- >>> libc = CDLL("libc.so.6")
- >>> libc
- <CDLL 'libc.so.6', handle ... at ...>
- >>>
函數(shù)作為dll對象的屬性進行訪問:
- >>> from ctypes import *
- >>> libc.printf
- <_FuncPtr object at 0x...>
- >>> print windll.kernel32.GetModuleHandleA
- <_FuncPtr object at 0x...>
- >>> print windll.kernel32.MyOwnFunction
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "ctypes.py", line 239, in __getattr__
- func = _StdcallFuncPtr(name, self)
- AttributeError: function 'MyOwnFunction' not found
- >>>
請注意,win32系統(tǒng)類似于kernel32
并且user32
經(jīng)常導(dǎo)出ANSI以及函數(shù)的UNICODE版本。UNICODE版本將導(dǎo)出W
并附加到名稱,而ANSI版本將導(dǎo)出A
并附加到名稱。win32 GetModuleHandle
函數(shù)返回給定模塊名稱的 模塊句柄,具有以下C原型,并且GetModuleHandle
根據(jù)是否定義了UNICODE ,使用宏來公開其中一個:
- /* ANSI version */
- HMODULE GetModuleHandleA(LPCSTR lpModuleName);
- /* UNICODE version */
- HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll不會嘗試通過魔法選擇其中一個,您必須通過指定GetModuleHandleA
或GetModuleHandleW
顯式訪問所需的版本,然后分別使用字符串或unicode字符串調(diào)用它。
有時,dll導(dǎo)出的函數(shù)名稱不是有效的Python標(biāo)識符,例如"??2@YAPAXI@Z"
。在這種情況下,您必須使用 getattr()
檢索功能:
- >>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
- <_FuncPtr object at 0x...>
- >>>
在Windows上,某些dll不按名稱導(dǎo)出函數(shù),而是按順序?qū)С龊瘮?shù)??梢酝ㄟ^使用序號索引dll對象來訪問這些函數(shù):
- >>> cdll.kernel32[1]
- <_FuncPtr object at 0x...>
- >>> cdll.kernel32[0]
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "ctypes.py", line 310, in __getitem__
- func = _StdcallFuncPtr(name, self)
- AttributeError: function ordinal 0 not found
- >>>
您可以像調(diào)用任何其他Python一樣調(diào)用這些函數(shù)。此示例使用time()
函數(shù),該函數(shù)返回自Unix紀(jì)元以來以秒為單位的系統(tǒng)時間,以及GetModuleHandleA()
返回win32模塊句柄的函數(shù)。
此示例使用NULL指針調(diào)用這兩個函數(shù)(None
應(yīng)該用作NULL指針):
- >>> print libc.time(None)
- 1150640792
- >>> print hex(windll.kernel32.GetModuleHandleA(None))
- 0x1d000000
- >>>
ctypes
試圖保護您不要使用錯誤的參數(shù)數(shù)量或錯誤的調(diào)用約定來調(diào)用函數(shù)。不幸的是,這僅適用于Windows。它通過在函數(shù)返回后檢查堆棧來完成此操作,因此雖然引發(fā)了錯誤,但函數(shù)已被調(diào)用:
- >>> windll.kernel32.GetModuleHandleA()
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: Procedure probably called with not enough arguments (4 bytes missing)
- >>> windll.kernel32.GetModuleHandleA(0, 0)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: Procedure probably called with too many arguments (4 bytes in excess)
- >>>
stdcall
使用cdecl
調(diào)用約定調(diào)用函數(shù) 時會引發(fā)相同的異常,反之亦然:
- >>> cdll.kernel32.GetModuleHandleA(None)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: Procedure probably called with not enough arguments (4 bytes missing)
- >>>
- >>> windll.msvcrt.printf("spam")
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: Procedure probably called with too many arguments (4 bytes in excess)
- >>>
要找出正確的調(diào)用約定,您必須查看C頭文件或要調(diào)用的函數(shù)的文檔。
在Windows上,ctypes
使用win32結(jié)構(gòu)化異常處理來防止在使用無效參數(shù)值調(diào)用函數(shù)時因常規(guī)保護錯誤而崩潰:
- >>> windll.kernel32.GetModuleHandleA(32)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- WindowsError: exception: access violation reading 0x00000020
- >>>
但是,有足夠的方法可以使Python崩潰ctypes
,所以無論如何你應(yīng)該小心。
None
,整數(shù),長整數(shù),字節(jié)字符串和unicode字符串是唯一可以直接用作這些函數(shù)調(diào)用中的參數(shù)的本機Python對象。 None
作為C NULL
指針傳遞,字節(jié)字符串和unicode字符串作為指針傳遞給包含其數(shù)據(jù)( 或)的內(nèi)存塊。Python整數(shù)和Python long作為平臺默認(rèn)C 類型傳遞,它們的值被屏蔽以適合C類型。char *
wchar_t *
int
在我們繼續(xù)使用其他參數(shù)類型調(diào)用函數(shù)之前,我們必須了解有關(guān)ctypes
數(shù)據(jù)類型的更多信息。
ctypes
定義了許多原始C兼容的數(shù)據(jù)類型:
ctypes類型 | C型 | Python類型 |
---|---|---|
c_bool | _Bool | 布爾(1) |
c_char | char | 1個字符的字符串 |
c_wchar | wchar_t | 1個字符的unicode字符串 |
c_byte | char | INT /長 |
c_ubyte | unsigned char | INT /長 |
c_short | short | INT /長 |
c_ushort | unsigned short | INT /長 |
c_int | int | INT /長 |
c_uint | unsigned int | INT /長 |
c_long | long | INT /長 |
c_ulong | unsigned long | INT /長 |
c_longlong | __int64 要么 long long | INT /長 |
c_ulonglong | unsigned __int64 要么 unsigned long long | INT /長 |
c_float | float | 浮動 |
c_double | double | 浮動 |
c_longdouble | long double | 浮動 |
c_char_p | char * (NUL終止) | 字符串或 None |
c_wchar_p | wchar_t * (NUL終止) | unicode或 None |
c_void_p | void * | int / long或 None |
構(gòu)造函數(shù)接受具有真值的任何對象。
所有這些類型都可以通過使用正確類型和值的可選初始化程序調(diào)用它們來創(chuàng)建:
- >>> c_int()
- c_long(0)
- >>> c_char_p("Hello, World")
- c_char_p('Hello, World')
- >>> c_ushort(-3)
- c_ushort(65533)
- >>>
由于這些類型是可變的,因此它們的值也可以在以后更改:
- >>> i = c_int(42)
- >>> print i
- c_long(42)
- >>> print i.value
- 42
- >>> i.value = -99
- >>> print i.value
- -99
- >>>
分配一個新的值,將指針類型的實例c_char_p
, c_wchar_p
以及c_void_p
改變所述存儲器位置它們指向,而不是內(nèi)容的內(nèi)存塊(當(dāng)然不是,因為Python字符串是不可變的):
- >>> s = "Hello, World"
- >>> c_s = c_char_p(s)
- >>> print c_s
- c_char_p('Hello, World')
- >>> c_s.value = "Hi, there"
- >>> print c_s
- c_char_p('Hi, there')
- >>> print s # first string is unchanged
- Hello, World
- >>>
但是,您應(yīng)該小心,不要將它們傳遞給期望指向可變內(nèi)存的函數(shù)。如果你需要可變的內(nèi)存塊,ctypes有一個create_string_buffer()
以各種方式創(chuàng)建它們的 函數(shù)??梢允褂?code>raw 屬性訪問(或更改)當(dāng)前內(nèi)存塊內(nèi)容; 如果要以NUL終止字符串的形式訪問它,請使用以下value
屬性:
- >>> from ctypes import *
- >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
- >>> print sizeof(p), repr(p.raw)
- 3 '\x00\x00\x00'
- >>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string
- >>> print sizeof(p), repr(p.raw)
- 6 'Hello\x00'
- >>> print repr(p.value)
- 'Hello'
- >>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer
- >>> print sizeof(p), repr(p.raw)
- 10 'Hello\x00\x00\x00\x00\x00'
- >>> p.value = "Hi"
- >>> print sizeof(p), repr(p.raw)
- 10 'Hi\x00lo\x00\x00\x00\x00\x00'
- >>>
該create_string_buffer()
函數(shù)替換了c_buffer()
函數(shù)(仍可作為別名使用),以及c_string()
早期ctypes版本中的函數(shù)。要創(chuàng)建包含C類型的unicode字符的可變內(nèi)存塊,請wchar_t
使用該 create_unicode_buffer()
函數(shù)。
需要注意的是的printf打印到實際的標(biāo)準(zhǔn)輸出通道,不給 sys.stdout
,所以這些例子只是在控制臺提示符下運行,而不是從內(nèi)IDLE或PythonWin的:
- >>> printf = libc.printf
- >>> printf("Hello, %s\n", "World!")
- Hello, World!
- 14
- >>> printf("Hello, %S\n", u"World!")
- Hello, World!
- 14
- >>> printf("%d bottles of beer\n", 42)
- 42 bottles of beer
- 19
- >>> printf("%f bottles of beer\n", 42.5)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
- >>>
如前所述,除了整數(shù),字符串和unicode字符串之外的所有Python類型都必須包裝在相應(yīng)的ctypes
類型中,以便它們可以轉(zhuǎn)換為所需的C數(shù)據(jù)類型:
- >>> printf("An int %d, a double %f\n", 1234, c_double(3.14))
- An int 1234, a double 3.140000
- 31
- >>>
您還可以自定義ctypes
參數(shù)轉(zhuǎn)換,以允許將您自己的類的實例用作函數(shù)參數(shù)。 ctypes
查找 _as_parameter_
屬性并將其用作函數(shù)參數(shù)。當(dāng)然,它必須是整數(shù),字符串或unicode之一:
- >>> class Bottles(object):
- ... def __init__(self, number):
- ... self._as_parameter_ = number
- ...
- >>> bottles = Bottles(42)
- >>> printf("%d bottles of beer\n", bottles)
- 42 bottles of beer
- 19
- >>>
如果您不想將實例的數(shù)據(jù)存儲在_as_parameter_
實例變量中,則可以定義property()
使數(shù)據(jù)可用的數(shù)據(jù)。
可以通過設(shè)置argtypes
屬性來指定從DLL導(dǎo)出的函數(shù)所需的參數(shù)類型。
argtypes
必須是一系列C數(shù)據(jù)類型(這里的printf
函數(shù)可能不是一個很好的例子,因為它取決于格式字符串需要一個可變數(shù)字和不同類型的參數(shù),另一方面,這對于試驗這個特性非常方便) :
- >>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
- >>> printf("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2)
- String 'Hi', Int 10, Double 2.200000
- 37
- >>>
指定格式可以防止不兼容的參數(shù)類型(就像C函數(shù)的原型一樣),并嘗試將參數(shù)轉(zhuǎn)換為有效類型:
- >>> printf("%d %d %d", 1, 2, 3)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ArgumentError: argument 2: exceptions.TypeError: wrong type
- >>> printf("%s %d %f\n", "X", 2, 3)
- X 2 3.000000
- 13
- >>>
如果已經(jīng)定義了自己傳遞給函數(shù)調(diào)用的from_param()
類,則必須為它們實現(xiàn)一個類方法,以便能夠在argtypes
序列中使用它們。本from_param()
類方法接收傳給函數(shù)的Python對象,它做一個類型檢測,或者是需要確保這個對象是可接受的,然后返回對象本身,它_as_parameter_
不管你想傳遞的C函數(shù)屬性,或在這種情況下的論點。同樣,結(jié)果應(yīng)該是整數(shù),字符串,unicode,ctypes
實例或具有_as_parameter_
屬性的對象 。
默認(rèn)情況下,假定函數(shù)返回C int
類型??梢酝ㄟ^設(shè)置restype
函數(shù)對象的屬性來指定其他返回類型。
這是一個更高級的示例,它使用strchr
函數(shù),它需要一個字符串指針和一個char,并返回一個指向字符串的指針:
- >>> strchr = libc.strchr
- >>> strchr("abcdef", ord("d"))
- 8059983
- >>> strchr.restype = c_char_p # c_char_p is a pointer to a string
- >>> strchr("abcdef", ord("d"))
- 'def'
- >>> print strchr("abcdef", ord("x"))
- None
- >>>
如果要避免ord("x")
上面的調(diào)用,可以設(shè)置 argtypes
屬性,第二個參數(shù)將從單個字符Python字符串轉(zhuǎn)換為C字符:
- >>> strchr.restype = c_char_p
- >>> strchr.argtypes = [c_char_p, c_char]
- >>> strchr("abcdef", "d")
- 'def'
- >>> strchr("abcdef", "def")
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ArgumentError: argument 2: exceptions.TypeError: one character string expected
- >>> print strchr("abcdef", "x")
- None
- >>> strchr("abcdef", "d")
- 'def'
- >>>
restype
如果外部函數(shù)返回一個整數(shù),您還可以使用可調(diào)用的Python對象(例如函數(shù)或類)作為屬性。將使用C函數(shù)返回的整數(shù)調(diào)用callable ,并且此調(diào)用的結(jié)果將用作函數(shù)調(diào)用的結(jié)果。這對于檢查錯誤返回值并自動引發(fā)異常非常有用:
- >>> GetModuleHandle = windll.kernel32.GetModuleHandleA
- >>> def ValidHandle(value):
- ... if value == 0:
- ... raise WinError()
- ... return value
- ...
- >>>
- >>> GetModuleHandle.restype = ValidHandle
- >>> GetModuleHandle(None)
- 486539264
- >>> GetModuleHandle("something silly")
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "<stdin>", line 3, in ValidHandle
- WindowsError: [Errno 126] The specified module could not be found.
- >>>
WinError
是一個函數(shù),它將調(diào)用Windows FormatMessage()
api來獲取錯誤代碼的字符串表示,并返回一個異常。 WinError
采用可選的錯誤代碼參數(shù),如果沒有使用,則調(diào)用它 GetLastError()
來檢索它。
請注意,通過該errcheck
屬性可以使用更強大的錯誤檢查機制; 有關(guān)詳細信息,請參閱參考手冊
有時,C api函數(shù)需要將指向數(shù)據(jù)類型的指針作為參數(shù),可能要寫入相應(yīng)的位置,或者數(shù)據(jù)太大而無法通過值傳遞。這也稱為通過引用傳遞參數(shù)。
ctypes
導(dǎo)出byref()
用于通過引用傳遞參數(shù)的函數(shù)。使用該pointer()
函數(shù)可以實現(xiàn)相同的效果 ,但是pointer()
由于它構(gòu)造了一個真正的指針對象,所以工作量更大,因此byref()
如果您不需要Python本身的指針對象,則使用它會更快:
- >>> i = c_int()
- >>> f = c_float()
- >>> s = create_string_buffer('\000' * 32)
- >>> print i.value, f.value, repr(s.value)
- 0 0.0 ''
- >>> libc.sscanf("1 3.14 Hello", "%d %f %s",
- ... byref(i), byref(f), s)
- 3
- >>> print i.value, f.value, repr(s.value)
- 1 3.1400001049 'Hello'
- >>>
結(jié)構(gòu)和聯(lián)合必須從導(dǎo)出Structure
和Union
其中所定義的基類ctypes
模塊。每個子類都必須定義一個_fields_
屬性。 _fields_
必須是2元組的列表 ,包含字段名稱和字段類型。
字段類型必須是ctypes
類型c_int
或任何其他派生ctypes
類型:結(jié)構(gòu),聯(lián)合,數(shù)組,指針。
下面是一個POINT結(jié)構(gòu)的簡單示例,它包含兩個名為x和y的整數(shù) ,還顯示了如何在構(gòu)造函數(shù)中初始化結(jié)構(gòu):
- >>> from ctypes import *
- >>> class POINT(Structure):
- ... _fields_ = [("x", c_int),
- ... ("y", c_int)]
- ...
- >>> point = POINT(10, 20)
- >>> print point.x, point.y
- 10 20
- >>> point = POINT(y=5)
- >>> print point.x, point.y
- 0 5
- >>> POINT(1, 2, 3)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- ValueError: too many initializers
- >>>
但是,您可以構(gòu)建更復(fù)雜的結(jié)構(gòu)。通過將結(jié)構(gòu)用作字段類型,結(jié)構(gòu)本身可以包含其他結(jié)構(gòu)。
這是一個RECT結(jié)構(gòu),它包含兩個名為upperleft和 lowerright的POINT:
- >>> class RECT(Structure):
- ... _fields_ = [("upperleft", POINT),
- ... ("lowerright", POINT)]
- ...
- >>> rc = RECT(point)
- >>> print rc.upperleft.x, rc.upperleft.y
- 0 5
- >>> print rc.lowerright.x, rc.lowerright.y
- 0 0
- >>>
嵌套結(jié)構(gòu)也可以通過以下幾種方式在構(gòu)造函數(shù)中初始化:
- >>> r = RECT(POINT(1, 2), POINT(3, 4))
- >>> r = RECT((1, 2), (3, 4))
字段描述符 S可從被檢索類,它們可用于調(diào)試有用,因為它們可以提供有用的信息:
- >>> print POINT.x
- <Field type=c_long, ofs=0, size=4>
- >>> print POINT.y
- <Field type=c_long, ofs=4, size=4>
- >>>
警告
ctypes
不支持通過值將具有位字段的聯(lián)合或結(jié)構(gòu)傳遞給函數(shù)。雖然這可能適用于32位x86,但是不能保證庫在一般情況下工作。具有位字段的聯(lián)合和結(jié)構(gòu)應(yīng)始終通過指針傳遞給函數(shù)。
默認(rèn)情況下,Structure和Union字段的對齊方式與C編譯器的方式相同??梢酝ㄟ^_pack_
在子類定義中指定類屬性來覆蓋此行為 。必須將其設(shè)置為正整數(shù),并指定字段的最大對齊方式。這也是MSVC中的做法。#pragma pack(n)
ctypes
使用結(jié)構(gòu)和聯(lián)合的本機字節(jié)順序。要建立與非本地字節(jié)順序結(jié)構(gòu),你可以使用一個 BigEndianStructure
,LittleEndianStructure
, BigEndianUnion
,和LittleEndianUnion
基類。這些類不能包含指針字段。
可以創(chuàng)建包含位字段的結(jié)構(gòu)和聯(lián)合。位字段僅適用于整數(shù)字段,位寬指定為_fields_
元組中的第三項:
- >>> class Int(Structure):
- ... _fields_ = [("first_16", c_int, 16),
- ... ("second_16", c_int, 16)]
- ...
- >>> print Int.first_16
- <Field type=c_long, ofs=0:0, bits=16>
- >>> print Int.second_16
- <Field type=c_long, ofs=0:16, bits=16>
- >>>
數(shù)組是序列,包含固定數(shù)量的相同類型的實例。
創(chuàng)建數(shù)組類型的推薦方法是將數(shù)據(jù)類型與正整數(shù)相乘:
TenPointsArrayType = POINT * 10
這是一個有點人為的數(shù)據(jù)類型的例子,一個包含4個POINT和其他東西的結(jié)構(gòu):
- >>> from ctypes import *
- >>> class POINT(Structure):
- ... _fields_ = ("x", c_int), ("y", c_int)
- ...
- >>> class MyStruct(Structure):
- ... _fields_ = [("a", c_int),
- ... ("b", c_float),
- ... ("point_array", POINT * 4)]
- >>>
- >>> print len(MyStruct().point_array)
- 4
- >>>
通過調(diào)用類以通常的方式創(chuàng)建實例:
- arr = TenPointsArrayType()
- for pt in arr:
- print pt.x, pt.y
上面的代碼打印了一系列行,因為數(shù)組內(nèi)容被初始化為零。0 0
也可以指定正確類型的初始化器:
- >>> from ctypes import *
- >>> TenIntegers = c_int * 10
- >>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
- >>> print ii
- <c_long_Array_10 object at 0x...>
- >>> for i in ii: print i,
- ...
- 1 2 3 4 5 6 7 8 9 10
- >>>
通過pointer()
在ctypes
類型上調(diào)用函數(shù) 來創(chuàng)建指針實例:
- >>> from ctypes import *
- >>> i = c_int(42)
- >>> pi = pointer(i)
- >>>
指針實例有一個contents
屬性,它返回指針指向的i
對象,上面的對象:
- >>> pi.contents
- c_long(42)
- >>>
注意,ctypes
沒有OOR(原始對象返回),每次檢索屬性時它都會構(gòu)造一個新的等效對象:
- >>> pi.contents is i
- False
- >>> pi.contents is pi.contents
- False
- >>>
將另一個c_int
實例分配給指針的contents屬性會導(dǎo)致指針指向存儲它的內(nèi)存位置:
- >>> i = c_int(99)
- >>> pi.contents = i
- >>> pi.contents
- c_long(99)
- >>>
指針實例也可以用整數(shù)索引:
- >>> pi[0]
- 99
- >>>
分配整數(shù)索引會更改指向的值:
- >>> print i
- c_long(99)
- >>> pi[0] = 22
- >>> print i
- c_long(22)
- >>>
也可以使用不同于0的索引,但您必須知道自己在做什么,就像在C中一樣:您可以訪問或更改任意內(nèi)存位置。通常,如果從C函數(shù)接收指針,則只使用此功能,并且您知道指針實際指向的是數(shù)組而不是單個項。
在幕后,該pointer()
函數(shù)不僅僅是創(chuàng)建指針實例,還必須首先創(chuàng)建指針類型。這是通過POINTER()
接受任何ctypes
類型的函數(shù)完成的,并返回一個新類型:
- >>> PI = POINTER(c_int)
- >>> PI
- <class 'ctypes.LP_c_long'>
- >>> PI(42)
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: expected c_long instead of int
- >>> PI(c_int(42))
- <ctypes.LP_c_long object at 0x...>
- >>>
調(diào)用不帶參數(shù)的指針類型會創(chuàng)建NULL
指針。 NULL
指針有一個False
布爾值:
- >>> null_ptr = POINTER(c_int)()
- >>> print bool(null_ptr)
- False
- >>>
ctypes
檢查NULL
何時解除引用指針(但解除引用無效的非NULL
指針會使Python崩潰):
- >>> null_ptr[0]
- Traceback (most recent call last):
- ....
- ValueError: NULL pointer access
- >>>
- >>> null_ptr[0] = 1234
- Traceback (most recent call last):
- ....
- ValueError: NULL pointer access
- >>>
通常,ctypes會進行嚴(yán)格的類型檢查。這意味著,如果您 POINTER(c_int)
在argtypes
函數(shù)列表中或在結(jié)構(gòu)定義中具有成員字段的類型,則只接受完全相同類型的實例。此規(guī)則有一些例外,其中ctypes接受其他對象。例如,您可以傳遞兼容的數(shù)組實例而不是指針類型。因此,對于POINTER(c_int)
,ctypes接受一個c_int數(shù)組:
- >>> class Bar(Structure):
- ... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
- ...
- >>> bar = Bar()
- >>> bar.values = (c_int * 3)(1, 2, 3)
- >>> bar.count = 3
- >>> for i in range(bar.count):
- ... print bar.values[i]
- ...
- 1
- 2
- 3
- >>>
此外,如果函數(shù)參數(shù)顯式聲明為指針類型(例如POINTER(c_int)
)in argtypes
,則可以將指向類型的對象(c_int
在本例中)傳遞給函數(shù)。ctypes將自動應(yīng)用所需的byref()
轉(zhuǎn)換。
要將POINTER類型字段設(shè)置為NULL
,您可以指定None
:
- >>> bar.values = None
- >>>
有時您會遇到不兼容類型的實例。在C中,您可以將一種類型轉(zhuǎn)換為另一種類型。 ctypes
提供cast()
可以以相同方式使用的功能。Bar
上面定義的結(jié)構(gòu)接受 其字段的POINTER(c_int)
指針或c_int
數(shù)組values
,但不接受其他類型的實例:
- >>> bar.values = (c_byte * 4)()
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
- >>>
對于這些情況,該cast()
功能很方便,cast還支持將python中的id(obj)即對象地址還原為對象
該cast()
函數(shù)可用于將ctypes實例轉(zhuǎn)換為指向不同ctypes數(shù)據(jù)類型的指針。 cast()
采用兩個參數(shù),一個或者可以轉(zhuǎn)換為某種指針的ctypes對象,以及一個ctypes指針類型。它返回第二個參數(shù)的實例,它引用與第一個參數(shù)相同的內(nèi)存塊:
- >>> a = (c_byte * 4)()
- >>> cast(a, POINTER(c_int))
- <ctypes.LP_c_long object at ...>
- >>>
所以,cast()
可以用來分配給結(jié)構(gòu)的values
字段Bar
:
- >>> bar = Bar()
- >>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
- >>> print bar.values[0]
- 0
- >>>
不完整類型是其成員尚未指定的結(jié)構(gòu),聯(lián)合或數(shù)組。在C中,它們由前向聲明指定,后面將定義:
- struct cell; /* forward declaration */
- struct cell {
- char *name;
- struct cell *next;
- };
直接轉(zhuǎn)換為ctypes代碼就是這樣,但它不起作用:
- >>> class cell(Structure):
- ... _fields_ = [("name", c_char_p),
- ... ("next", POINTER(cell))]
- ...
- Traceback (most recent call last):
- File "<stdin>", line 1, in <module>
- File "<stdin>", line 2, in cell
- NameError: name 'cell' is not defined
- >>>
因為new 語句本身不可用。在,我們可以 在類語句之后定義類并設(shè)置屬性:class cell
ctypes
cell
_fields_
- >>> from ctypes import *
- >>> class cell(Structure):
- ... pass
- ...
- >>> cell._fields_ = [("name", c_char_p),
- ... ("next", POINTER(cell))]
- >>>
讓我們試試吧。我們創(chuàng)建了兩個實例cell
,并讓它們相互指向,最后跟隨指針鏈幾次:
- >>> c1 = cell()
- >>> c1.name = "foo"
- >>> c2 = cell()
- >>> c2.name = "bar"
- >>> c1.next = pointer(c2)
- >>> c2.next = pointer(c1)
- >>> p = c1
- >>> for i in range(8):
- ... print p.name,
- ... p = p.next[0]
- ...
- foo bar foo bar foo bar foo bar
- >>>
ctypes
允許從Python callables創(chuàng)建C可調(diào)用函數(shù)指針。這些有時稱為回調(diào)函數(shù)。
首先,您必須為回調(diào)函數(shù)創(chuàng)建一個類,該類知道調(diào)用約定,返回類型以及此函數(shù)將接收的參數(shù)的數(shù)量和類型。
CFUNCTYPE工廠函數(shù)使用普通的cdecl調(diào)用約定為回調(diào)函數(shù)創(chuàng)建類型,在Windows上,WINFUNCTYPE工廠函數(shù)使用stdcall調(diào)用約定為回調(diào)函數(shù)創(chuàng)建類型。
這兩個工廠函數(shù)都以結(jié)果類型作為第一個參數(shù)調(diào)用,而回調(diào)函數(shù)將期望的參數(shù)類型作為其余參數(shù)。
我將在這里展示一個使用標(biāo)準(zhǔn)C庫qsort()
函數(shù)的示例,它用于在回調(diào)函數(shù)的幫助下對項目進行排序。 qsort()
將用于對整數(shù)數(shù)組進行排序:
- >>> IntArray5 = c_int * 5
- >>> ia = IntArray5(5, 1, 7, 33, 99)
- >>> qsort = libc.qsort
- >>> qsort.restype = None
- >>>
qsort()
必須使用指向要排序的數(shù)據(jù)的指針,數(shù)據(jù)數(shù)組中的項數(shù),一個項的大小以及指向比較函數(shù)(回調(diào))的指針來調(diào)用。然后使用兩個指向項目的指針調(diào)用回調(diào),如果第一個項目小于第二個項目,它必須返回一個負整數(shù),如果它們相等則返回零,然后返回一個正整數(shù)。
所以我們的回調(diào)函數(shù)接收指向整數(shù)的指針,并且必須返回一個整數(shù)。首先我們type
為回調(diào)函數(shù)創(chuàng)建:
- >>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
- >>>
對于回調(diào)函數(shù)的第一個實現(xiàn),我們只是打印我們得到的參數(shù),并返回0(增量開發(fā);-):
- >>> def py_cmp_func(a, b):
- ... print "py_cmp_func", a, b
- ... return 0
- ...
- >>>
創(chuàng)建C可調(diào)用回調(diào):
- >>> cmp_func = CMPFUNC(py_cmp_func)
- >>>
我們準(zhǔn)備好了:
- >>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>
- >>>
我們知道如何訪問指針的內(nèi)容,所以讓我們重新定義我們的回調(diào):
- >>> def py_cmp_func(a, b):
- ... print "py_cmp_func", a[0], b[0]
- ... return 0
- ...
- >>> cmp_func = CMPFUNC(py_cmp_func)
- >>>
以下是我們在Windows上獲得的內(nèi)容:
- >>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
- py_cmp_func 7 1
- py_cmp_func 33 1
- py_cmp_func 99 1
- py_cmp_func 5 1
- py_cmp_func 7 5
- py_cmp_func 33 5
- py_cmp_func 99 5
- py_cmp_func 7 99
- py_cmp_func 33 99
- py_cmp_func 7 33
- >>>
很有趣的是,在linux上,sort函數(shù)看起來效率更高,它做的比較少:
- >>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
- py_cmp_func 5 1
- py_cmp_func 33 99
- py_cmp_func 7 33
- py_cmp_func 5 7
- py_cmp_func 1 7
- >>>
啊,我們差不多完成了!最后一步是實際比較這兩個項并返回一個有用的結(jié)果:
- >>> def py_cmp_func(a, b):
- ... print "py_cmp_func", a[0], b[0]
- ... return a[0] - b[0]
- ...
- >>>
最終在Windows上運行:
- >>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
- py_cmp_func 33 7
- py_cmp_func 99 33
- py_cmp_func 5 99
- py_cmp_func 1 99
- py_cmp_func 33 7
- py_cmp_func 1 33
- py_cmp_func 5 33
- py_cmp_func 5 7
- py_cmp_func 1 7
- py_cmp_func 5 1
- >>>
在Linux上:
- >>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
- py_cmp_func 5 1
- py_cmp_func 33 99
- py_cmp_func 7 33
- py_cmp_func 1 7
- py_cmp_func 5 7
- >>>
很有趣的是,Windows qsort()
功能需要比Linux版本更多的比較!
我們可以輕松檢查,我們的數(shù)組現(xiàn)在排序:
- >>> for i in ia: print i,
- ...
- 1 5 7 33 99
- >>>
注意
CFUNCTYPE()
只要從C代碼中使用對象,請確保保留對對象的引用。ctypes
沒有,如果你不這樣做,它們可能被垃圾收集,在回調(diào)時崩潰你的程序。
另外,請注意,如果在Python控件之外創(chuàng)建的線程中調(diào)用回調(diào)函數(shù)(例如,通過調(diào)用回調(diào)的外部代碼),ctypes會在每次調(diào)用時創(chuàng)建一個新的虛擬Python線程。這種行為是對大多數(shù)的目的是正確的,但它意味著存儲的值threading.local
將不會在不同的回調(diào)生存,即使這些電話是從同一個C的線。
一些共享庫不僅導(dǎo)出函數(shù),還導(dǎo)出變量。Python庫本身的一個示例是Py_OptimizeFlag
,一個設(shè)置為0,1或2的整數(shù),具體取決于啟動時給出的-O
或-OO
標(biāo)志。
ctypes
可以使用in_dll()
類型的類方法訪問這樣的值。 pythonapi是一個預(yù)定義的符號,可以訪問Python C api:
- >>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
- >>> print opt_flag
- c_long(0)
- >>>
如果解釋器已經(jīng)啟動-O
,則樣本將打印c_long(1)
,或者c_long(2)
如果-OO
已經(jīng)指定。
一個擴展示例也演示了指針的使用,可以訪問PyImport_FrozenModules
Python導(dǎo)出的 指針。
引用Python文檔:此指針初始化為指向“struct _frozen”記錄的數(shù)組,由其成員全部為NULL或零的記錄終止。導(dǎo)入凍結(jié)模塊時,將在此表中搜索它。第三方代碼可以使用此方法來提供動態(tài)創(chuàng)建的凍結(jié)模塊集合。
所以操縱這個指針甚至可以證明是有用的。為了限制示例大小,我們僅顯示如何使用以下方法讀取此表ctypes
:
- >>> from ctypes import *
- >>>
- >>> class struct_frozen(Structure):
- ... _fields_ = [("name", c_char_p),
- ... ("code", POINTER(c_ubyte)),
- ... ("size", c_int)]
- ...
- >>>
我們已經(jīng)定義了數(shù)據(jù)類型,因此我們可以獲得指向表的指針:struct _frozen
- >>> FrozenTable = POINTER(struct_frozen)
- >>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
- >>>
因為table
是一個記錄pointer
數(shù)組struct_frozen
,我們可以迭代它,但我們必須確保我們的循環(huán)終止,因為指針沒有大小。遲早它可能會因訪問沖突或其他原因而崩潰,所以當(dāng)我們點擊NULL條目時最好突破循環(huán):
- >>> for item in table:
- ... print item.name, item.size
- ... if item.name is None:
- ... break
- ...
- __hello__ 104
- __phello__ -104
- __phello__.spam 104
- None 0
- >>>
標(biāo)準(zhǔn)Python具有凍結(jié)模塊和凍結(jié)包(由負大小成員指示)的事實并不為人所知,它僅用于測試。例如嘗試一下。import __hello__
在某些邊緣情況下ctypes
,您可能會發(fā)現(xiàn)實際情況以外的其他情況。
請考慮以下示例:
- >>> from ctypes import *
- >>> class POINT(Structure):
- ... _fields_ = ("x", c_int), ("y", c_int)
- ...
- >>> class RECT(Structure):
- ... _fields_ = ("a", POINT), ("b", POINT)
- ...
- >>> p1 = POINT(1, 2)
- >>> p2 = POINT(3, 4)
- >>> rc = RECT(p1, p2)
- >>> print rc.a.x, rc.a.y, rc.b.x, rc.b.y
- 1 2 3 4
- >>> # now swap the two points
- >>> rc.a, rc.b = rc.b, rc.a
- >>> print rc.a.x, rc.a.y, rc.b.x, rc.b.y
- 3 4 3 4
- >>>
嗯。我們當(dāng)然希望打印最后一份聲明。發(fā)生了什么?以下是上述行的步驟:3 4 1 2
rc.a, rc.b = rc.b, rc.a
- >>> temp0, temp1 = rc.b, rc.a
- >>> rc.a = temp0
- >>> rc.b = temp1
- >>>
請注意,temp0
并且temp1
還在使用的內(nèi)部緩沖器的對象rc
上述對象。因此執(zhí)行將緩沖區(qū)內(nèi)容復(fù)制到緩沖區(qū)中。反過來,這改變了內(nèi)容。因此,最后一項任務(wù),沒有預(yù)期的效果。rc.a = temp0
temp0
rc
temp1
rc.b = temp1
請記住,從Structure,Unions和Arrays中檢索子對象不會復(fù)制子對象,而是檢索訪問根對象底層緩沖區(qū)的包裝器對象。
另一個可能與預(yù)期不同的例子是:
- >>> s = c_char_p()
- >>> s.value = "abc def ghi"
- >>> s.value
- 'abc def ghi'
- >>> s.value is s.value
- False
- >>>
為什么打印False
?ctypes實例是包含內(nèi)存塊的對象以及訪問內(nèi)存內(nèi)容的一些描述符。在內(nèi)存塊中存儲Python對象不會存儲對象本身,而是存儲對象contents
的對象。再次訪問內(nèi)容每次構(gòu)造一個新的Python對象!
ctypes
為可變大小的數(shù)組和結(jié)構(gòu)提供了一些支持。
該resize()
函數(shù)可用于調(diào)整現(xiàn)有ctypes對象的內(nèi)存緩沖區(qū)的大小。該函數(shù)將對象作為第一個參數(shù),并將請求的大小(以字節(jié)為單位)作為第二個參數(shù)。內(nèi)存塊不能小于對象類型指定的自然內(nèi)存塊,ValueError
如果嘗試,則引發(fā)a :
- >>> short_array = (c_short * 4)()
- >>> print sizeof(short_array)
- 8
- >>> resize(short_array, 4)
- Traceback (most recent call last):
- ...
- ValueError: minimum size is 8
- >>> resize(short_array, 32)
- >>> sizeof(short_array)
- 32
- >>> sizeof(type(short_array))
- 8
- >>>
這很好很好,但是如何訪問此數(shù)組中包含的其他元素?由于類型仍然只知道4個元素,因此訪問其他元素時會出錯:
- >>> short_array[:]
- [0, 0, 0, 0]
- >>> short_array[7]
- Traceback (most recent call last):
- ...
- IndexError: invalid index
- >>>
使用可變大小數(shù)據(jù)類型的另一種方法ctypes
是使用Python的動態(tài)特性,并且在已知所需大小之后(根據(jù)具體情況)重新定義數(shù)據(jù)類型。
使用編譯語言編程時,在編譯/鏈接程序時以及程序運行時都會訪問共享庫。
該find_library()
函數(shù)的目的是以類似于編譯器的方式定位庫(在具有多個版本的共享庫的平臺上應(yīng)該加載最新版本),而ctypes庫加載器就像程序運行時一樣,并直接調(diào)用運行時加載程序。
該ctypes.util
模塊提供了一個函數(shù),可以幫助確定要加載的庫。
ctypes.util.
find_library
(姓名)
嘗試查找?guī)觳⒎祷芈窂矫?nbsp;名字是不一樣的任何前綴庫名的lib,標(biāo)的相同.so
,.dylib
或版本號(這是用于POSIX鏈接器選項的形式-l
)。如果找不到庫,則返回None
。
確切的功能取決于系統(tǒng)。
在Linux上,find_library()
嘗試運行外部程序(/sbin/ldconfig
,gcc
,和objdump
)找到庫文件。它返回庫文件的文件名。這里有些例子:
- >>> from ctypes.util import find_library
- >>> find_library("m")
- 'libm.so.6'
- >>> find_library("c")
- 'libc.so.6'
- >>> find_library("bz2")
- 'libbz2.so.1.0'
- >>>
在OS X上,find_library()
嘗試幾個預(yù)定義的命名方案和路徑來定位庫,如果成功則返回完整路徑名:
- >>> from ctypes.util import find_library
- >>> find_library("c")
- '/usr/lib/libc.dylib'
- >>> find_library("m")
- '/usr/lib/libm.dylib'
- >>> find_library("bz2")
- '/usr/lib/libbz2.dylib'
- >>> find_library("AGL")
- '/System/Library/Frameworks/AGL.framework/AGL'
- >>>
在Windows上,find_library()
沿系統(tǒng)搜索路徑進行搜索,并返回完整路徑名,但由于沒有預(yù)定義的命名方案,因此調(diào)用find_library("c")
將失敗并返回None
。
如果包裝共享庫ctypes
,它可以更好地確定在開發(fā)時共享庫的名字,并硬編碼到封裝模塊,而不是使用find_library()
定位在運行時庫。
有幾種方法可以將共享庫加載到Python進程中。一種方法是實例化以下類之一:
class ctypes.
CDLL
(name,mode = DEFAULT_MODE,handle = None,use_errno = False,use_last_error = False )
此類的實例表示已加載的共享庫。這些庫中的函數(shù)使用標(biāo)準(zhǔn)C調(diào)用約定,并假定返回 int
。
class ctypes.
OleDLL
(name,mode = DEFAULT_MODE,handle = None,use_errno = False,use_last_error = False )
僅限Windows:此類的實例表示已加載的共享庫,這些庫中的函數(shù)使用stdcall
調(diào)用約定,并假定返回特定于Windows的HRESULT
代碼。 HRESULT
values包含指定函數(shù)調(diào)用是否失敗或成功的信息,以及其他錯誤代碼。如果返回值表示失敗,WindowsError
則自動引發(fā)a。
class ctypes.
WinDLL
(name,mode = DEFAULT_MODE,handle = None,use_errno = False,use_last_error = False )
僅限Windows:此類的實例表示已加載的共享庫,這些庫中的函數(shù)使用stdcall
調(diào)用約定,并且int
默認(rèn)情況下假定返回。
在Windows CE上,僅使用標(biāo)準(zhǔn)調(diào)用約定,以方便 WinDLL
并OleDLL
使用此平臺上的標(biāo)準(zhǔn)調(diào)用約定。
Python 全局解釋器鎖在調(diào)用這些庫導(dǎo)出的任何函數(shù)之前發(fā)布,之后重新獲取。
class ctypes.
PyDLL
(name,mode = DEFAULT_MODE,handle = None )
CDLL
除了在函數(shù)調(diào)用期間未釋放Python GIL之外,此類的實例的行為與實例類似,并且在函數(shù)執(zhí)行之后,將檢查Python錯誤標(biāo)志。如果設(shè)置了錯誤標(biāo)志,則會引發(fā)Python異常。
因此,這僅對直接調(diào)用Python C api函數(shù)有用。
所有這些類都可以通過使用至少一個參數(shù)(共享庫的路徑名)調(diào)用它們來實例化。如果已有已加載的共享庫的現(xiàn)有句柄,則可以將其作為handle
命名參數(shù)傳遞,否則使用基礎(chǔ)平臺dlopen
或LoadLibrary
函數(shù)將庫加載到進程中,并獲取它的句柄。
該模式參數(shù)可用于指定庫的加載方式。有關(guān)詳細信息,請參閱dlopen(3)聯(lián)機幫助頁。在Windows上,模式被忽略。在posix系統(tǒng)上,始終添加RTLD_NOW,并且不可配置。
該use_errno參數(shù),當(dāng)設(shè)置為true,使一個ctypes機制,允許訪問該系統(tǒng)errno
以安全的方式錯誤號。 ctypes
維護系統(tǒng)errno
變量的線程局部副本; 如果在函數(shù)調(diào)用與ctypes私有副本交換之前調(diào)用用函數(shù)調(diào)用創(chuàng)建的外部函數(shù),use_errno=True
則在 errno
函數(shù)調(diào)用之后立即發(fā)生相同的操作。
該函數(shù)ctypes.get_errno()
返回ctypes私有副本的值,該函數(shù)將ctypes私有副本ctypes.set_errno()
更改為新值并返回前一個值。
的use_last_error參數(shù),設(shè)置為true時,使能由所述管理Windows錯誤代碼相同的機制GetLastError()
和 SetLastError()
Windows API函數(shù); ctypes.get_last_error()
并 ctypes.set_last_error()
用于請求和更改Windows錯誤代碼的ctypes私有副本。
新的2.6版:該use_last_error和use_errno可選參數(shù)添加。
ctypes.
RTLD_GLOBAL
用作模式參數(shù)的標(biāo)志。在此標(biāo)志不可用的平臺上,它被定義為整數(shù)零。
ctypes.
RTLD_LOCAL
用作模式參數(shù)的標(biāo)志。在沒有此功能的平臺上,它與RTLD_GLOBAL相同。
ctypes.
DEFAULT_MODE
用于加載共享庫的默認(rèn)模式。在OSX 10.3上,這是 RTLD_GLOBAL,否則它與RTLD_LOCAL相同。
這些類的實例沒有公共方法。共享庫導(dǎo)出的函數(shù)可以作為屬性或索引進行訪問。請注意,通過屬性訪問函數(shù)會緩存結(jié)果,因此每次重復(fù)訪問它都會返回相同的對象。另一方面,通過索引訪問它每次都會返回一個新對象:
- >>> libc.time == libc.time
- True
- >>> libc['time'] == libc['time']
- False
可以使用以下公共屬性,它們的名稱以下劃線開頭,以便不與導(dǎo)出的函數(shù)名沖突:
PyDLL.
_handle
用于訪問庫的系統(tǒng)句柄。
PyDLL.
_name
在構(gòu)造函數(shù)中傳遞的庫的名稱。
還可以使用其中一個預(yù)制對象(LibraryLoader
通過調(diào)用 LoadLibrary()
方法)或通過將庫檢索為加載程序?qū)嵗膶傩詠砑虞d共享庫。
class ctypes.
LibraryLoader
(dlltype )
加載共享庫的類。 dlltype應(yīng)該是一個 CDLL
,PyDLL
,WinDLL
或OleDLL
類型。
__getattr__()
具有特殊行為:它允許通過將其作為庫加載器實例的屬性訪問來加載共享庫。結(jié)果是緩存的,因此重復(fù)的屬性訪問每次都返回相同的庫。
LoadLibrary
(名字)
將共享庫加載到進程中并返回它。此方法始終返回庫的新實例。
這些預(yù)制庫加載器可用:
ctypes.
cdll
創(chuàng)建CDLL
實例。
ctypes.
windll
僅限Windows:創(chuàng)建WinDLL
實例。
ctypes.
oledll
僅限Windows:創(chuàng)建OleDLL
實例。
ctypes.
pydll
創(chuàng)建PyDLL
實例。
為了直接訪問C Python api,可以使用現(xiàn)成的Python共享庫對象:
ctypes.
pythonapi
它的一個實例PyDLL
將Python C API函數(shù)公開為屬性。請注意,假設(shè)所有這些函數(shù)都返回C int
,這當(dāng)然不總是事實,因此您必須分配正確的restype
屬性才能使用這些函數(shù)。
如前一節(jié)所述,外部函數(shù)可以作為加載的共享庫的屬性進行訪問。默認(rèn)情況下以這種方式創(chuàng)建的函數(shù)對象接受任意數(shù)量的參數(shù),接受任何ctypes數(shù)據(jù)實例作為參數(shù),并返回庫加載器指定的默認(rèn)結(jié)果類型。他們是私人班級的實例:
類ctypes.
_FuncPtr
C可調(diào)用外部函數(shù)的基類。
外部函數(shù)的實例也是C兼容的數(shù)據(jù)類型; 它們代表C函數(shù)指針。
可以通過分配外部函數(shù)對象的特殊屬性來自定義此行為。
restype
指定ctypes類型以指定外部函數(shù)的結(jié)果類型。使用None
的void
,功能不返回任何東西。
可以分配不是ctypes類型的可調(diào)用Python對象,在這種情況下假定函數(shù)返回C int
,并且將使用此整數(shù)調(diào)用callable,從而允許進一步處理或錯誤檢查。不推薦使用它,為了更靈活的后處理或錯誤檢查,請使用ctypes數(shù)據(jù)類型, restype
并為該errcheck
屬性分配一個callable 。
argtypes
分配ctypes類型的元組以指定函數(shù)接受的參數(shù)類型。使用stdcall
調(diào)用約定的函數(shù)只能使用與此元組的長度相同的參數(shù)數(shù)來調(diào)用; 使用C調(diào)用約定的函數(shù)也接受其他未指定的參數(shù)。
當(dāng)調(diào)用外部函數(shù)時,每個實際參數(shù)都傳遞給 元組中from_param()
項的 類方法argtypes
,此方法允許將實際參數(shù)調(diào)整為外部函數(shù)接受的對象。例如,元組中的c_char_p
項argtypes
將使用ctypes轉(zhuǎn)換規(guī)則將作為參數(shù)傳遞的unicode字符串轉(zhuǎn)換為字節(jié)字符串。
新增:現(xiàn)在可以將項目放在不是ctypes類型的argtypes中,但每個項目必須有一個from_param()
返回可用作參數(shù)的值的方法(整數(shù),字符串,ctypes實例)。這允許定義可以將自定義對象調(diào)整為函數(shù)參數(shù)的適配器。
errcheck
為此屬性分配Python函數(shù)或其他可調(diào)用函數(shù)。將使用三個或更多參數(shù)調(diào)用callable:
callable
(結(jié)果,函數(shù),參數(shù))
result是外部函數(shù)返回的內(nèi)容,由restype
屬性指定 。
func是外部函數(shù)對象本身,這允許重用相同的可調(diào)用對象來檢查或后處理幾個函數(shù)的結(jié)果。
arguments是一個包含最初傳遞給函數(shù)調(diào)用的參數(shù)的元組,這允許對所使用的參數(shù)進行特殊處理。
此函數(shù)返回的對象將從外部函數(shù)調(diào)用返回,但如果外部函數(shù)調(diào)用失敗,它還可以檢查結(jié)果值并引發(fā)異常。
異常ctypes.
ArgumentError
當(dāng)外部函數(shù)調(diào)用無法轉(zhuǎn)換其中一個傳遞的參數(shù)時,會引發(fā)此異常。
也可以通過實例化函數(shù)原型來創(chuàng)建外部函數(shù)。函數(shù)原型類似于C中的函數(shù)原型; 它們描述了一個函數(shù)(返回類型,參數(shù)類型,調(diào)用約定)而沒有定義實現(xiàn)。必須使用所需的結(jié)果類型和函數(shù)的參數(shù)類型調(diào)用工廠函數(shù)。
ctypes.
CFUNCTYPE
(restype,* argtypes,use_errno = False,use_last_error = False )
返回的函數(shù)原型創(chuàng)建使用標(biāo)準(zhǔn)C調(diào)用約定的函數(shù)。該功能將在通話期間釋放GIL。如果 use_errno設(shè)置為true,則系統(tǒng)errno
變量的ctypes私有副本將 與errno
調(diào)用前后的實際值進行交換; use_last_error對Windows錯誤代碼執(zhí)行相同操作。
版本2.6中已更改:添加了可選的use_errno和use_last_error參數(shù)。
ctypes.
WINFUNCTYPE
(restype,* argtypes,use_errno = False,use_last_error = False )
僅適用于Windows:返回的函數(shù)原型創(chuàng)建使用函數(shù) stdcall
調(diào)用約定,除了Windows CE地方 WINFUNCTYPE()
是一樣的CFUNCTYPE()
。該功能將在通話期間釋放GIL。 use_errno和use_last_error具有與上面相同的含義。
ctypes.
PYFUNCTYPE
(restype,* argtypes )
返回的函數(shù)原型創(chuàng)建使用Python調(diào)用約定的函數(shù)。該功能在通話期間不會釋放GIL。
由這些工廠函數(shù)創(chuàng)建的函數(shù)原型可以以不同的方式實例化,具體取決于調(diào)用中參數(shù)的類型和數(shù)量:
prototype
(地址)返回指定地址的外部函數(shù),該函數(shù)必須是整數(shù)。
prototype
(可贖回)從Python 可調(diào)用創(chuàng)建C可調(diào)用函數(shù)(回調(diào)函數(shù))。
prototype
(func_spec [,paramflags ] )返回由共享庫導(dǎo)出的外部函數(shù)。func_spec必須是2元組。第一項是導(dǎo)出函數(shù)的名稱作為字符串,或?qū)С龊瘮?shù)的序號作為小整數(shù)。第二項是共享庫實例。
(name_or_ordinal, library)
prototype
(vtbl_index,name [,paramflags [,iid ] ] )返回將調(diào)用COM方法的外部函數(shù)。vtbl_index是虛函數(shù)表的索引,是一個小的非負整數(shù)。name是COM方法的名稱。iid是指向擴展錯誤報告中使用的接口標(biāo)識符的可選指針。
COM方法使用特殊的調(diào)用約定:除了
argtypes
元組中指定的那些參數(shù)外,它們還需要指向COM接口的指針作為第一個參數(shù)。可選的paramflags參數(shù)創(chuàng)建的外部函數(shù)包裝器具有比上述功能更多的功能。
paramflags必須是一個長度相同的元組
argtypes
。此元組中的每個項目都包含有關(guān)參數(shù)的更多信息,它必須是包含一個,兩個或三個項目的元組。
第一項是一個整數(shù),包含參數(shù)的方向標(biāo)志組合:
1
指定函數(shù)的輸入?yún)?shù)。
2
輸出參數(shù)。外來函數(shù)填入一個值。
4
輸入?yún)?shù),默認(rèn)為整數(shù)零。
可選的第二項是參數(shù)名稱為字符串。如果指定了此參數(shù),則可以使用命名參數(shù)調(diào)用外部函數(shù)。
可選的第三項是此參數(shù)的默認(rèn)值。
此示例演示如何包裝Windows MessageBoxA
函數(shù),以便它支持默認(rèn)參數(shù)和命名參數(shù)。Windows頭文件中的C聲明是這樣的:
- WINUSERAPI int WINAPI
- MessageBoxA(
- HWND hWnd,
- LPCSTR lpText,
- LPCSTR lpCaption,
- UINT uType);
這是包裝ctypes
:
- >>> from ctypes import c_int, WINFUNCTYPE, windll
- >>> from ctypes.wintypes import HWND, LPCSTR, UINT
- >>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
- >>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)
- >>> MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)
- >>>
現(xiàn)在可以通過以下方式調(diào)用MessageBox外部函數(shù):
- >>> MessageBox()
- >>> MessageBox(text="Spam, spam, spam")
- >>> MessageBox(flags=2, text="foo bar")
- >>>
第二個例子演示了輸出參數(shù)。win32 GetWindowRect
函數(shù)通過將指定窗口的尺寸復(fù)制到RECT
調(diào)用者必須提供的結(jié)構(gòu)中來檢索它們的尺寸 。這是C聲明:
- WINUSERAPI BOOL WINAPI
- GetWindowRect(
- HWND hWnd,
- LPRECT lpRect);
這是包裝ctypes
:
- >>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
- >>> from ctypes.wintypes import BOOL, HWND, RECT
- >>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
- >>> paramflags = (1, "hwnd"), (2, "lprect")
- >>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
- >>>
具有輸出參數(shù)的函數(shù)將自動返回輸出參數(shù)值(如果存在單個參數(shù)值)或者包含輸出參數(shù)值的元組(如果有多個),則GetWindowRect函數(shù)現(xiàn)在會在調(diào)用時返回RECT實例。
輸出參數(shù)可以與errcheck
協(xié)議組合以進行進一步的輸出處理和錯誤檢查。win32 GetWindowRect
api函數(shù)返回一個BOOL
指示成功或失敗的信號,因此該函數(shù)可以執(zhí)行錯誤檢查,并在api調(diào)用失敗時引發(fā)異常:
- >>> def errcheck(result, func, args):
- ... if not result:
- ... raise WinError()
- ... return args
- ...
- >>> GetWindowRect.errcheck = errcheck
- >>>
如果errcheck
函數(shù)返回參數(shù)tuple,它接收不變,ctypes
繼續(xù)對輸出參數(shù)進行的正常處理。如果要返回窗口坐標(biāo)而不是RECT
實例的元組 ,可以檢索函數(shù)中的字段并返回它們,將不再進行正常處理:
- >>> def errcheck(result, func, args):
- ... if not result:
- ... raise WinError()
- ... rc = args[1]
- ... return rc.left, rc.top, rc.bottom, rc.right
- ...
- >>> GetWindowRect.errcheck = errcheck
- >>>
ctypes.
addressof
(obj )
以整數(shù)形式返回內(nèi)存緩沖區(qū)的地址。 obj必須是ctypes類型的實例。
ctypes.
alignment
(obj_or_type )
返回ctypes類型的對齊要求。obj_or_type必須是ctypes類型或?qū)嵗?/p>
ctypes.
byref
(obj [,offset ] )
返回一個指向obj的輕量級指針,該指針必須是ctypes類型的一個實例。 offset默認(rèn)為零,并且必須是將添加到內(nèi)部指針值的整數(shù)。
byref(obj, offset)
對應(yīng)于此C代碼:
(((char *)&obj) + offset)
返回的對象只能用作外部函數(shù)調(diào)用參數(shù)。它的行為類似pointer(obj)
,但構(gòu)造速度要快得多。
:在2.6版本中的新的偏移添加可選的參數(shù)。
ctypes.
cast
(obj,type )
此函數(shù)類似于C中的強制轉(zhuǎn)換運算符。它返回一個新的類型實例,它指向與obj相同的內(nèi)存塊。 type 必須是指針類型,obj必須是可以解釋為指針的對象。
ctypes.
create_string_buffer
(init_or_size [,size ] )
此函數(shù)創(chuàng)建一個可變字符緩沖區(qū)。返回的對象是ctypes數(shù)組c_char
。
init_or_size必須是一個整數(shù),它指定數(shù)組的大小,或者是一個用于初始化數(shù)組項的字符串。
如果將字符串指定為第一個參數(shù),則將緩沖區(qū)設(shè)置為大于字符串長度的一個項目,以便數(shù)組中的最后一個元素是NUL終止字符??梢詫⒄麛?shù)作為第二個參數(shù)傳遞,如果不應(yīng)使用字符串的長度,則允許指定數(shù)組的大小。
如果第一個參數(shù)是unicode字符串,則根據(jù)ctypes轉(zhuǎn)換規(guī)則將其轉(zhuǎn)換為8位字符串。
ctypes.
create_unicode_buffer
(init_or_size [,size ] )
此函數(shù)創(chuàng)建一個可變的unicode字符緩沖區(qū)。返回的對象是ctypes數(shù)組c_wchar
。
init_or_size必須是指定數(shù)組大小的整數(shù),或者是用于初始化數(shù)組項的unicode字符串。
如果將unicode字符串指定為第一個參數(shù),則將緩沖區(qū)設(shè)置為大于字符串長度的一個項目,以便數(shù)組中的最后一個元素是NUL終止字符。可以將整數(shù)作為第二個參數(shù)傳遞,如果不應(yīng)使用字符串的長度,則允許指定數(shù)組的大小。
如果第一個參數(shù)是8位字符串,則根據(jù)ctypes轉(zhuǎn)換規(guī)則將其轉(zhuǎn)換為unicode字符串。
ctypes.
DllCanUnloadNow
()
僅限Windows:此函數(shù)是一個鉤子,允許使用ctypes實現(xiàn)進程內(nèi)COM服務(wù)器。從DllCanUnloadNow函數(shù)調(diào)用_ctypes擴展dll導(dǎo)出。
ctypes.
DllGetClassObject
()
僅限Windows:此函數(shù)是一個鉤子,允許使用ctypes實現(xiàn)進程內(nèi)COM服務(wù)器。它是從_ctypes
擴展dll導(dǎo)出的DllGetClassObject函數(shù)調(diào)用的。
ctypes.util.
find_library
(名字)
嘗試查找?guī)觳⒎祷芈窂矫?nbsp;name是沒有任何前綴的庫名,如lib
后綴.so
,.dylib
或版本號(這是用于posix鏈接器選項的形式-l
)。如果找不到庫,則返回None
。
確切的功能取決于系統(tǒng)。
在版本2.6中更改:僅限Windows:find_library("m")
或find_library("c")
將調(diào)用的結(jié)果返回到find_msvcrt()
。
ctypes.util.
find_msvcrt
()
僅限Windows:返回Python使用的VC運行時庫的文件名,以及擴展模塊。如果無法確定庫的名稱,None
則返回。
如果需要釋放內(nèi)存,例如,由擴展模塊調(diào)用的內(nèi)存,則必須在分配內(nèi)存的同一庫中使用該功能。free(void *)
版本2.6中的新功能。
ctypes.
FormatError
([ code ] )
僅適用于Windows:返回的錯誤代碼的文本描述代碼。如果未指定錯誤代碼,則通過調(diào)用Windows api函數(shù)GetLastError來使用上一個錯誤代碼。
ctypes.
GetLastError
()
僅限Windows:返回Windows在調(diào)用線程中設(shè)置的最后一個錯誤代碼。此函數(shù)直接調(diào)用Windows GetLastError()函數(shù),它不返回錯誤代碼的ctypes-private副本。
ctypes.
get_errno
()
返回errno
調(diào)用線程中系統(tǒng)變量的ctypes-private副本的當(dāng)前值 。
版本2.6中的新功能。
ctypes.
get_last_error
()
僅限Windows:返回LastError
調(diào)用線程中系統(tǒng)變量的ctypes-private副本的當(dāng)前值 。
版本2.6中的新功能。
ctypes.
memmove
(dst,src,count )
與標(biāo)準(zhǔn)C memmove庫函數(shù)相同:將計數(shù)字節(jié)從 src復(fù)制到dst。dst和src必須是可以轉(zhuǎn)換為指針的整數(shù)或ctypes實例。
ctypes.
memset
(dst,c,count )
與標(biāo)準(zhǔn)C memset庫函數(shù)相同:使用值c的計數(shù)字節(jié)填充地址dst處的內(nèi)存塊。dst必須是指定地址的整數(shù)或ctypes實例。
ctypes.
POINTER
(類型)
此工廠函數(shù)創(chuàng)建并返回新的ctypes指針類型。指針類型在內(nèi)部被緩存和重用,因此重復(fù)調(diào)用此函數(shù)很便宜。type必須是ctypes類型。
ctypes.
pointer
(obj )
此函數(shù)創(chuàng)建一個指向obj的新指針實例。返回的對象屬于該類型POINTER(type(obj))
。
注意:如果您只想將指向?qū)ο蟮闹羔槀鬟f給外部函數(shù)調(diào)用,則應(yīng)該使用byref(obj)
哪個更快。
ctypes.
resize
(obj,size )
此函數(shù)調(diào)整obj的內(nèi)部內(nèi)存緩沖區(qū)的大小,obj必須是ctypes類型的實例。如下所示,不可能使緩沖區(qū)小于對象類型的本機大小sizeof(type(obj))
,但可以放大緩沖區(qū)。
ctypes.
set_conversion_mode
(編碼,錯誤)
此函數(shù)設(shè)置ctypes對象在8位字符串和unicode字符串之間進行轉(zhuǎn)換時使用的規(guī)則。 encoding必須是指定編碼的字符串,如'utf-8'
或'mbcs'
,錯誤必須是一個字符串,指定編碼/解碼錯誤的錯誤處理??赡艿闹档膶嵗?code>"strict","replace"
,或"ignore"
。
set_conversion_mode()
返回包含先前轉(zhuǎn)換規(guī)則的2元組。在Windows上,初始轉(zhuǎn)換規(guī)則在其他系統(tǒng)上。('mbcs', 'ignore')
('ascii', 'strict')
ctypes.
set_errno
(值)
將errno
調(diào)用線程中系統(tǒng)變量的ctypes-private副本的當(dāng)前值設(shè)置為value并返回先前的值。
版本2.6中的新功能。
ctypes.
set_last_error
(值)
僅限Windows:將LastError
調(diào)用線程中系統(tǒng)變量的ctypes-private副本的當(dāng)前值設(shè)置 為value并返回先前的值。
版本2.6中的新功能。
ctypes.
sizeof
(obj_or_type )
返回ctypes類型或?qū)嵗齼?nèi)存緩沖區(qū)的大?。ㄒ宰止?jié)為單位)。與C sizeof
運算符相同。
ctypes.
string_at
(地址[,大小] )
這個函數(shù)返回了從內(nèi)存地址字符串地址。如果指定了size,則將其用作size,否則假定該字符串為零終止。
ctypes.
WinError
(code = None,descr = None )
僅限Windows:此函數(shù)可能是ctypes中最糟糕的名稱。它創(chuàng)建了一個WindowsError實例。如果未指定代碼, GetLastError
則調(diào)用以確定錯誤代碼。如果descr
未指定,FormatError()
則調(diào)用以獲取錯誤的文本描述。
ctypes.
wstring_at
(地址[,大小] )
這個函數(shù)返回了從內(nèi)存地址寬字符串 地址為unicode字符串。如果指定了size,則將其用作字符串的字符數(shù),否則假定該字符串為零終止。
類ctypes.
_CData
這個非公共類是所有ctypes數(shù)據(jù)類型的公共基類。除此之外,所有ctypes類型實例都包含一個保存C兼容數(shù)據(jù)的內(nèi)存塊; addressof()
輔助函數(shù)返回內(nèi)存塊的地址 。另一個實例變量暴露為 _objects
; 這包含其他需要保持活動的Python對象,以防內(nèi)存塊包含指針。
ctypes數(shù)據(jù)類型的常用方法,這些都是類方法(確切地說,它們是元類的方法):
from_buffer
(來源[,偏移] )
此方法返回共享源對象的緩沖區(qū)的ctypes實例 。所述源對象必須支持可寫緩沖器接口。可選的offset參數(shù)指定源緩沖區(qū)的偏移量(以字節(jié)為單位); 默認(rèn)值為零。如果源緩沖區(qū)不夠大,ValueError
則會引發(fā)a。
版本2.6中的新功能。
from_buffer_copy
(來源[,偏移] )
此方法創(chuàng)建一個ctypes實例,從源對象緩沖區(qū)復(fù)制緩沖區(qū),該 緩沖區(qū)必須是可讀的??蛇x的offset 參數(shù)指定源緩沖區(qū)的偏移量(以字節(jié)為單位); 默認(rèn)值為零。如果源緩沖區(qū)不夠大,ValueError
則會引發(fā)a。
版本2.6中的新功能。
from_address
(地址)
此方法使用address指定的內(nèi)存返回ctypes類型實例,該內(nèi)存 必須是整數(shù)。
from_param
(obj )
此方法使obj適應(yīng)ctypes類型。當(dāng)外部函數(shù)的argtypes
元組中存在類型時,使用外部函數(shù)調(diào)用中使用的實際對象調(diào)用它; 它必須返回一個可以用作函數(shù)調(diào)用參數(shù)的對象。
所有ctypes數(shù)據(jù)類型都具有此類方法的默認(rèn)實現(xiàn),如果是類型的實例,則通常返回obj。某些類型也接受其他對象。
in_dll
(圖書館,名稱)
此方法返回由共享庫導(dǎo)出的ctypes類型實例。name是導(dǎo)出數(shù)據(jù)的符號的名稱,library 是加載的共享庫。
ctypes數(shù)據(jù)類型的常見實例變量:
_b_base_
有時ctypes數(shù)據(jù)實例不擁有它們包含的內(nèi)存塊,而是共享基礎(chǔ)對象的部分內(nèi)存塊。所述 _b_base_
只讀構(gòu)件是根ctypes的對象擁有該存儲器塊。
_b_needsfree_
當(dāng)ctypes數(shù)據(jù)實例已分配內(nèi)存塊本身時,此只讀變量為true,否則為false。
_objects
該成員None
或者是包含需要保持活動的Python對象的字典,以便內(nèi)存塊內(nèi)容保持有效。該對象僅用于調(diào)試; 永遠不要修改這本詞典的內(nèi)容。
類ctypes.
_SimpleCData
這個非公共類是所有基本ctypes數(shù)據(jù)類型的基類。這里提到它是因為它包含基本ctypes數(shù)據(jù)類型的公共屬性。 _SimpleCData
是它的子類 _CData
,因此它繼承了它們的方法和屬性。
在版本2.6中更改:現(xiàn)在可以對不包含指針但不包含指針的ctypes數(shù)據(jù)類型進行pickle。
實例具有單個屬性:
value
該屬性包含實例的實際值。對于整數(shù)和指針類型,它是一個整數(shù),對于字符類型,它是單個字符串,對于字符指針類型,它是Python字符串或unicode字符串。
當(dāng)value
屬性從一個ctypes實例獲取,通常是一個新的對象,每次返回。 ctypes
并沒有實現(xiàn)原來的目標(biāo)回報率,總是一個新的對象被創(chuàng)建。所有其他ctypes對象實例也是如此。
基本數(shù)據(jù)類型作為外部函數(shù)調(diào)用結(jié)果返回時,或者例如通過檢索結(jié)構(gòu)字段成員或數(shù)組項,將透明地轉(zhuǎn)換為本機Python類型。換句話說,如果一個外國函數(shù)有一個 restype
的c_char_p
,你總是會收到一個Python字符串, 不是一個c_char_p
實例。
基本數(shù)據(jù)類型的子類不會繼承此行為。因此,如果外部函數(shù)restype
是其子類c_void_p
,您將從函數(shù)調(diào)用中接收此子類的實例。當(dāng)然,您可以通過訪問value
屬性來獲取指針的值。
這些是基本的ctypes數(shù)據(jù)類型:
類ctypes.
c_byte
表示C 數(shù)據(jù)類型,并將該值解釋為小整數(shù)。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。signed char
類ctypes.
c_char
表示C char
數(shù)據(jù)類型,并將該值解釋為單個字符。構(gòu)造函數(shù)接受可選的字符串初始值設(shè)定項,字符串的長度必須恰好是一個字符。
類ctypes.
c_char_p
當(dāng)它指向以零結(jié)尾的字符串時表示C 數(shù)據(jù)類型。對于也可能指向二進制數(shù)據(jù)的通用字符指針, 必須使用。構(gòu)造函數(shù)接受整數(shù)地址或字符串。char *
POINTER(c_char)
類ctypes.
c_double
表示C double
數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的float初始化程序。
類ctypes.
c_longdouble
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的float初始化程序。在它是別名的平臺上。long double
sizeof(long double) == sizeof(double)
c_double
版本2.6中的新功能。
類ctypes.
c_float
表示C float
數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的float初始化程序。
類ctypes.
c_int
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。在它是別名的平臺上。signed int
sizeof(int) == sizeof(long)
c_long
類ctypes.
c_int8
表示C 8位數(shù)據(jù)類型。通常是別名 。signed int
c_byte
類ctypes.
c_int16
表示C 16位數(shù)據(jù)類型。通常是別名 。signed int
c_short
類ctypes.
c_int32
表示C 32位數(shù)據(jù)類型。通常是別名 。signed int
c_int
類ctypes.
c_int64
表示C 64位數(shù)據(jù)類型。通常是別名 。signed int
c_longlong
類ctypes.
c_long
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。signed long
類ctypes.
c_longlong
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。signed long long
類ctypes.
c_short
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。signed short
類ctypes.
c_size_t
表示C size_t
數(shù)據(jù)類型。
類ctypes.
c_ssize_t
表示C ssize_t
數(shù)據(jù)類型。
版本2.7中的新功能。
類ctypes.
c_ubyte
表示C 數(shù)據(jù)類型,它將值解釋為小整數(shù)。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。unsigned char
類ctypes.
c_uint
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。在它是別名的平臺上。unsigned int
sizeof(int) == sizeof(long)
c_ulong
類ctypes.
c_uint8
表示C 8位數(shù)據(jù)類型。通常是別名 。unsigned int
c_ubyte
類ctypes.
c_uint16
表示C 16位數(shù)據(jù)類型。通常是別名 。unsigned int
c_ushort
類ctypes.
c_uint32
表示C 32位數(shù)據(jù)類型。通常是別名 。unsigned int
c_uint
類ctypes.
c_uint64
表示C 64位數(shù)據(jù)類型。通常是別名 。unsigned int
c_ulonglong
類ctypes.
c_ulong
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。unsigned long
類ctypes.
c_ulonglong
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。unsigned long long
類ctypes.
c_ushort
表示C 數(shù)據(jù)類型。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項; 沒有進行溢出檢查。unsigned short
類ctypes.
c_void_p
表示C 類型。該值表示為整數(shù)。構(gòu)造函數(shù)接受可選的整數(shù)初始值設(shè)定項。void *
類ctypes.
c_wchar
表示C wchar_t
數(shù)據(jù)類型,并將該值解釋為單個字符的unicode字符串。構(gòu)造函數(shù)接受可選的字符串初始值設(shè)定項,字符串的長度必須恰好是一個字符。
類ctypes.
c_wchar_p
表示C 數(shù)據(jù)類型,該數(shù)據(jù)類型必須是指向以零結(jié)尾的寬字符串的指針。構(gòu)造函數(shù)接受整數(shù)地址或字符串。wchar_t *
類ctypes.
c_bool
表示C bool
數(shù)據(jù)類型(更準(zhǔn)確地說,_Bool
來自C99)。它的值可以是True
或者False
,構(gòu)造函數(shù)接受任何具有真值的對象。
版本2.6中的新功能。
類ctypes.
HRESULT
僅限Windows:表示一個HRESULT
值,其中包含函數(shù)或方法調(diào)用的成功或錯誤信息。
類ctypes.
py_object
表示C 數(shù)據(jù)類型。在沒有參數(shù)的情況下調(diào)用它會創(chuàng)建一個指針。PyObject *
NULL
PyObject *
該ctypes.wintypes
模塊提供了相當(dāng)長的一段其他Windows特定的數(shù)據(jù)類型,例如HWND
,WPARAM
或DWORD
。一些有用的結(jié)構(gòu),如MSG
或RECT
定義。
class ctypes.
Union
(* args,** kw )
原始字節(jié)順序的聯(lián)合的抽象基類。
class ctypes.
BigEndianStructure
(* args,** kw )
大端字節(jié)順序結(jié)構(gòu)的抽象基類。
class ctypes.
LittleEndianStructure
(* args,** kw )
小端字節(jié)順序結(jié)構(gòu)的抽象基類。
具有非本機字節(jié)順序的結(jié)構(gòu)不能包含指針類型字段或包含指針類型字段的任何其他數(shù)據(jù)類型。
class ctypes.Structure(* args,** kw )
本機字節(jié)順序的結(jié)構(gòu)的抽象基類。
必須通過繼承其中一種類型來創(chuàng)建具體結(jié)構(gòu)和聯(lián)合類型,并至少定義一個_fields_
類變量。ctypes
將創(chuàng)建描述符 s,允許通過直接屬性訪問來讀取和寫入字段。這些是
_fields_
定義結(jié)構(gòu)字段的序列。項目必須是2元組或3元組。第一項是字段的名稱,第二項指定字段的類型; 它可以是任何ctypes數(shù)據(jù)類型。
對于整數(shù)類型字段c_int
,可以給出第三個可選項。它必須是一個小的正整數(shù),用于定義字段的位寬。
字段名稱在一個結(jié)構(gòu)或聯(lián)合中必須是唯一的。未選中此選項,重復(fù)名稱時只能訪問一個字段。
可以在定義Structure子類的類語句之后定義_fields_
類變量,這允許創(chuàng)建直接或間接引用自身的數(shù)據(jù)類型:
- class List(Structure):
- pass
- List._fields_ = [("pnext", POINTER(List)),
- ...
- ]
的_fields_
類變量但是,必須被定義在第一次使用之前的類型(創(chuàng)建一個實例,sizeof()
被稱為其上,等等)。稍后對_fields_
類變量的賦值將引發(fā)AttributeError。
可以定義結(jié)構(gòu)類型的子類,它們繼承基類的字段以及_fields_
子子類中定義的字段(如果有的話)。
_pack_
一個可選的小整數(shù),允許覆蓋實例中結(jié)構(gòu)字段的對齊方式。 _pack_
必須在_fields_
分配時定義,否則它將無效。
_anonymous_
一個可選序列,列出未命名(匿名)字段的名稱。 _anonymous_
必須在_fields_
分配時定義,否則它將無效。
此變量中列出的字段必須是結(jié)構(gòu)或聯(lián)合類型字段。 ctypes
將在結(jié)構(gòu)類型中創(chuàng)建允許直接訪問嵌套字段的描述符,而無需創(chuàng)建結(jié)構(gòu)或聯(lián)合字段。
這是一個示例類型(Windows):
- class _U(Union):
- _fields_ = [("lptdesc", POINTER(TYPEDESC)),
- ("lpadesc", POINTER(ARRAYDESC)),
- ("hreftype", HREFTYPE)]
- class TYPEDESC(Structure):
- _anonymous_ = ("u",)
- _fields_ = [("u", _U),
- ("vt", VARTYPE)]
該TYPEDESC
結(jié)構(gòu)描述了COM數(shù)據(jù)類型,該vt
字段指定哪個聯(lián)合字段有效。由于該u
字段被定義為匿名字段,因此現(xiàn)在可以直接從TYPEDESC實例訪問成員。td.lptdesc
并且td.u.lptdesc
是等價的,但前者更快,因為它不需要創(chuàng)建臨時聯(lián)合實例:
- td = TYPEDESC()
- td.vt = VT_PTR
- td.lptdesc = POINTER(some_type)
- td.u.lptdesc = POINTER(some_type)
可以定義結(jié)構(gòu)的子類,它們繼承基類的字段。如果子類定義具有單獨的 _fields_
變量,則在此指定的字段將附加到基類的字段中。
結(jié)構(gòu)和聯(lián)合構(gòu)造函數(shù)接受位置和關(guān)鍵字參數(shù)。位置參數(shù)用于按照出現(xiàn)的順序初始化成員字段_fields_
。構(gòu)造函數(shù)中的關(guān)鍵字參數(shù)被解釋為屬性賦值,因此它們將_fields_
使用相同的名稱進行初始化 ,或者為不存在的名稱創(chuàng)建新屬性_fields_
。
class ctypes.
Array
(* args )
數(shù)組的抽象基類。
創(chuàng)建具體數(shù)組類型的推薦方法是將任何ctypes
數(shù)據(jù)類型與正整數(shù)相乘 ?;蛘撸梢岳^承此類型并定義_length_
和_type_
類變量??梢允褂脴?biāo)準(zhǔn)下標(biāo)和切片訪問來讀取和寫入數(shù)組元素; 對于切片讀取,所得到的物體是 不本身Array
。
_length_
一個正整數(shù),指定數(shù)組中元素的數(shù)量。超出范圍的下標(biāo)導(dǎo)致IndexError
。將由返回len()
。
_type_
指定數(shù)組中每個元素的類型。
數(shù)組子類構(gòu)造函數(shù)接受位置參數(shù),用于按順序初始化元素。
類ctypes.
_Pointer
指針的私有抽象基類。
通過POINTER()
使用將指向的類型調(diào)用來創(chuàng)建具體指針類型; 這是由自動完成的 pointer()
。
如果指針指向數(shù)組,則可以使用標(biāo)準(zhǔn)下標(biāo)和切片訪問來讀取和寫入其元素。指針對象沒有大小,因此len()
會提高TypeError
。否定下標(biāo)將在指針之前從內(nèi)存中讀?。ㄈ缭贑中),并且超出范圍的下標(biāo)可能會因訪問沖突而崩潰(如果您很幸運)。
_type_
指定指向的類型。
contents
返回指針指向的對象。分配給此屬性會將指針更改為指向指定的對象。