ctypes --- Thư viện hàm ngoại cho Python¶
Source code: Lib/ctypes
ctypes là thư viện hàm nước ngoài dành cho Python. Nó cung cấp các kiểu dữ liệu tương thích với C và cho phép gọi các hàm trong DLL hoặc thư viện dùng chung. Nó có thể được sử dụng để bọc các thư viện này bằng Python thuần túy.
Đây là một optional module. Nếu nó bị thiếu trong bản sao CPython của bạn, hãy tìm tài liệu từ nhà phân phối của bạn (nghĩa là bất kỳ ai đã cung cấp Python cho bạn). Nếu bạn là nhà phân phối, hãy xem Yêu cầu đối với các mô-đun tùy chọn.
hướng dẫn ctypes¶
Lưu ý: Một số mẫu mã tham chiếu loại ctypes c_int. Trên nền tảng có sizeof(long) == sizeof(int), nó là bí danh của c_long. Vì vậy, bạn không nên nhầm lẫn nếu c_long được in nếu bạn mong đợi c_int --- chúng thực sự cùng loại.
Đang tải thư viện liên kết động¶
ctypes xuất cdll và trên các đối tượng windll và oledll của Windows để tải các thư viện liên kết động.
Bạn tải các thư viện bằng cách truy cập chúng dưới dạng thuộc tính của các đối tượng này. cdll tải các thư viện xuất hàm bằng quy ước gọi cdecl tiêu chuẩn, trong khi thư viện windll gọi các hàm bằng quy ước gọi stdcall. oledll cũng sử dụng quy ước gọi stdcall và giả sử các hàm trả về mã lỗi HRESULT của Windows. Mã lỗi được sử dụng để tự động đưa ra ngoại lệ OSError khi lệnh gọi hàm không thành công.
Thay đổi trong phiên bản 3.3: Lỗi Windows từng gây ra WindowsError, hiện là bí danh của OSError.
Dưới đây là một số ví dụ cho Windows. Lưu ý rằng msvcrt là thư viện C tiêu chuẩn MS chứa hầu hết các hàm C tiêu chuẩn và sử dụng quy ước gọi cdecl
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windows tự động thêm hậu tố tệp .dll thông thường.
Ghi chú
Truy cập thư viện C tiêu chuẩn thông qua cdll.msvcrt sẽ sử dụng phiên bản thư viện lỗi thời có thể không tương thích với phiên bản đang được Python sử dụng. Nếu có thể, hãy sử dụng chức năng Python gốc hoặc nhập và sử dụng mô-đun msvcrt.
Các hệ thống khác yêu cầu phần mở rộng tên tệp including để tải thư viện, do đó không thể sử dụng quyền truy cập thuộc tính để tải thư viện. Nên sử dụng phương thức LoadLibrary() của trình tải dll hoặc bạn nên tải thư viện bằng cách tạo một phiên bản của CDLL bằng cách gọi hàm tạo.
Ví dụ: trên Linux:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
Trên macOS:
>>> cdll.LoadLibrary("libc.dylib")
<CDLL 'libc.dylib', handle ... at ...>
>>> libc = CDLL("libc.dylib")
>>> libc
<CDLL 'libc.dylib', handle ... at ...>
Truy cập các chức năng từ các dll được tải¶
Các hàm được truy cập dưới dạng thuộc tính của đối tượng dll:
>>> 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
>>>
Lưu ý rằng các dll hệ thống win32 như kernel32 và user32 thường xuất các phiên bản ANSI cũng như UNICODE của một hàm. Phiên bản UNICODE được xuất với W được thêm vào tên, trong khi phiên bản ANSI được xuất với A được thêm vào tên. Hàm win32 GetModuleHandle, trả về module handle cho một tên mô-đun nhất định, có nguyên mẫu C sau và macro được sử dụng để hiển thị một trong số chúng là GetModuleHandle tùy thuộc vào việc UNICODE có được xác định hay không:
/* phiên bản ANSI */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* phiên bản UNICODE */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll không cố gắng chọn một trong số chúng bằng phép thuật, bạn phải truy cập phiên bản bạn cần bằng cách chỉ định rõ ràng GetModuleHandleA hoặc GetModuleHandleW, sau đó gọi nó bằng các đối tượng byte hoặc chuỗi tương ứng.
Đôi khi, dll xuất các hàm có tên không phải là mã định danh Python hợp lệ, như "??2@YAPAXI@Z". Trong trường hợp này, bạn phải sử dụng getattr() để truy xuất hàm
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
Trên Windows, một số dll xuất hàm không theo tên mà theo thứ tự. Các chức năng này có thể được truy cập bằng cách lập chỉ mục đối tượng dll với số thứ tự
>>> 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
>>>
Chức năng gọi¶
Bạn có thể gọi các hàm này giống như bất kỳ hàm Python nào khác có thể gọi được. Ví dụ này sử dụng hàm rand(), hàm này không nhận đối số và trả về một số nguyên giả ngẫu nhiên:
>>> print(libc.rand())
1804289383
Trên Windows, bạn có thể gọi hàm GetModuleHandleA(), hàm này trả về một mã điều khiển mô-đun win32 (chuyển None dưới dạng đối số duy nhất để gọi nó bằng con trỏ NULL):
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError được nâng lên khi bạn gọi hàm stdcall với quy ước gọi cdecl hoặc ngược lại:
>>> 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(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
Để tìm ra quy ước gọi chính xác, bạn phải xem tệp tiêu đề C hoặc tài liệu về hàm bạn muốn gọi.
Trên Windows, ctypes sử dụng xử lý ngoại lệ có cấu trúc win32 để ngăn sự cố do lỗi bảo vệ chung khi các hàm được gọi với giá trị đối số không hợp lệ:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
Tuy nhiên, có đủ cách để phá hỏng Python bằng ctypes, vì vậy dù sao bạn cũng nên cẩn thận. Mô-đun faulthandler có thể hữu ích trong việc gỡ lỗi các sự cố (ví dụ: từ lỗi phân đoạn do lệnh gọi thư viện C sai).
None, số nguyên, đối tượng byte và chuỗi (unicode) là các đối tượng Python gốc duy nhất có thể được sử dụng trực tiếp làm tham số trong các lệnh gọi hàm này. None được truyền dưới dạng con trỏ C NULL, các đối tượng byte và chuỗi được truyền dưới dạng con trỏ tới khối bộ nhớ chứa dữ liệu của chúng (char* hoặc wchar_t*). Các số nguyên Python được chuyển dưới dạng loại C int mặc định của nền tảng, giá trị của chúng được che đi để phù hợp với loại C.
Trước khi chuyển sang gọi hàm với các loại tham số khác, chúng ta phải tìm hiểu thêm về các loại dữ liệu ctypes.
Các kiểu dữ liệu cơ bản¶
ctypes định nghĩa một số kiểu dữ liệu tương thích với C nguyên thủy:
loại ctype |
loại C |
loại Python |
|
|---|---|---|---|
_Bool |
|
||
char |
1-character |
|
|
|
1 ký tự |
|
|
char |
|
||
unsigned char |
|
||
short |
|
||
unsigned short |
|
||
int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
unsigned int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
long |
|
||
unsigned long |
|
||
long long |
|
||
unsigned long long |
|
||
|
* |
||
* |
|||
|
* |
||
float |
|
||
double |
|
||
long double |
|
||
char* (NUL đã chấm dứt) |
|
|
|
wchar_t* (NUL đã chấm dứt) |
|
|
|
void* |
|
|
|
|
|||
short int |
|
Ngoài ra, nếu số học phức tương thích IEC 60559 (Phụ lục G) được hỗ trợ trong cả C và libffi thì các loại phức sau đây sẽ khả dụng:
loại ctype |
loại C |
loại Python |
|
|---|---|---|---|
float complex |
|
||
double complex |
|
||
long double complex |
|
Tất cả các loại này có thể được tạo bằng cách gọi chúng bằng trình khởi tạo tùy chọn có đúng loại và giá trị
>>> c_int()
c_long(0)
>>> c_wchar_p("Xin chào thế giới")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Các hàm tạo cho kiểu số sẽ chuyển đổi đầu vào bằng __bool__(), __index__() (đối với int), __float__() hoặc __complex__(). Điều này có nghĩa là c_bool chấp nhận bất kỳ đối tượng nào có giá trị thật
>>> Danh sách trống = []
>>> c_bool(danh sách trống)
c_bool(Sai)
Vì các loại này có thể thay đổi nên giá trị của chúng cũng có thể được thay đổi sau đó
>>> i = c_int(42)
>>> in(i)
c_long(42)
>>> in(i.value)
42
>>> i.giá trị = -99
>>> in(i.value)
-99
>>>
Việc gán một giá trị mới cho các thể hiện của các loại con trỏ c_char_p, c_wchar_p và c_void_p sẽ thay đổi memory location mà chúng trỏ tới, not the contents của khối bộ nhớ (tất nhiên là không, vì các đối tượng chuỗi Python là bất biến):
>>> s = "Xin chào thế giới"
>>> c_s = c_wchar_p(s)
>>> in(c_s)
c_wchar_p(139966785747344)
>>> in(c_s.value)
Xin chào thế giới
>>> c_s.value = "Xin chào"
>>> print(c_s) # the vị trí bộ nhớ đã thay đổi
c_wchar_p(139966783348904)
>>> in(c_s.value)
Xin chào, ở đó
>>> đối tượng print(s) # first không thay đổi
Xin chào thế giới
>>>
Tuy nhiên, bạn nên cẩn thận không chuyển chúng cho các hàm mong đợi con trỏ tới bộ nhớ có thể thay đổi. Nếu bạn cần các khối bộ nhớ có thể thay đổi, ctypes có chức năng create_string_buffer() để tạo ra các khối này theo nhiều cách khác nhau. Nội dung khối bộ nhớ hiện tại có thể được truy cập (hoặc thay đổi) bằng thuộc tính raw; nếu bạn muốn truy cập nó dưới dạng chuỗi kết thúc NUL, hãy sử dụng thuộc tính value
>>> từ nhập ctypes *
>>> p = create_string_buffer(3) # create bộ đệm 3 byte, được khởi tạo thành byte NUL
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create một bộ đệm chứa chuỗi kết thúc NUL
>>> print(sizeof(p), repr(p.raw))
6 b'Xin chào\x00'
>>> print(repr(p.value))
b'Xin chào'
>>> p = create_string_buffer(b"Xin chào", 10) # create bộ đệm 10 byte
>>> print(sizeof(p), repr(p.raw))
10 b'Xin chào\x00\x00\x00\x00\x00'
>>> p.value = b"Xin chào"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
Hàm create_string_buffer() thay thế hàm c_buffer() cũ (vẫn có sẵn dưới dạng bí danh). Để tạo một khối bộ nhớ có thể thay đổi chứa các ký tự unicode loại C wchar_t, hãy sử dụng hàm create_unicode_buffer().
Chức năng gọi, tiếp tục¶
Lưu ý rằng printf in ra kênh đầu ra tiêu chuẩn thực, not đến sys.stdout, vì vậy những ví dụ này sẽ chỉ hoạt động tại dấu nhắc bảng điều khiển chứ không phải từ bên trong IDLE hoặc PythonWin:
>>> printf = libc.printf
>>> printf(b"Xin chào, %s\n", b"Thế giới!")
Xin chào Thế giới!
14
>>> printf(b"Xin chào, %S\n", "Thế giới!")
Xin chào Thế giới!
14
>>> printf(b"%d chai bia\n", 42)
42 chai bia
19
>>> printf(b"%f chai bia\n", 42.5)
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
ctypes.ArgumentError: đối số 2: TypeError: Không biết cách chuyển đổi tham số 2
>>>
Như đã đề cập trước đó, tất cả các loại Python ngoại trừ các đối tượng số nguyên, chuỗi và byte phải được gói trong loại ctypes tương ứng của chúng để chúng có thể được chuyển đổi sang loại dữ liệu C được yêu cầu:
>>> printf(b"Một int %d, một double %f\n", 1234, c_double(3.14))
Một int 1234, gấp đôi 3.140000
31
>>>
Gọi các hàm biến thiên¶
Trên nhiều nền tảng, việc gọi các hàm biến đổi thông qua ctypes hoàn toàn giống với việc gọi các hàm với một số tham số cố định. Trên một số nền tảng và đặc biệt là ARM64 dành cho Nền tảng Apple, quy ước gọi các hàm biến đổi khác với quy ước gọi các hàm thông thường.
Trên các nền tảng đó, bắt buộc phải chỉ định thuộc tính argtypes cho các đối số hàm thông thường, không biến đổi:
libc.printf.argtypes = [ctypes.c_char_p]
Bởi vì việc chỉ định thuộc tính không hạn chế khả năng di chuyển nên bạn nên luôn chỉ định argtypes cho tất cả các hàm biến thiên.
Gọi hàm với các kiểu dữ liệu tùy chỉnh của riêng bạn¶
Bạn cũng có thể tùy chỉnh chuyển đổi đối số ctypes để cho phép sử dụng các phiên bản của lớp của riêng bạn làm đối số hàm. ctypes tìm kiếm thuộc tính _as_parameter_ và sử dụng thuộc tính này làm đối số hàm. Thuộc tính phải là số nguyên, chuỗi, byte, phiên bản ctypes hoặc đối tượng có thuộc tính _as_parameter_
>>> Chai đẳng cấp:
... def __init__(tự, số):
... self._as_parameter_ = số
...
>>> chai = Chai(42)
>>> printf(b"%d chai bia\n", chai)
42 chai bia
19
>>>
Nếu bạn không muốn lưu trữ dữ liệu của phiên bản trong biến phiên bản _as_parameter_, bạn có thể xác định property để cung cấp thuộc tính theo yêu cầu.
Chỉ định các loại đối số cần thiết (nguyên mẫu hàm)¶
Có thể chỉ định các loại đối số cần thiết của hàm được xuất từ DLL bằng cách đặt thuộc tính argtypes.
argtypes phải là một chuỗi gồm các kiểu dữ liệu C (hàm printf() có lẽ không phải là một ví dụ hay ở đây, vì nó nhận một số lượng thay đổi và các loại tham số khác nhau tùy thuộc vào chuỗi định dạng, mặt khác, điều này khá tiện lợi để thử nghiệm tính năng này):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"Chuỗi '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
Chuỗi 'Xin chào', Int 10, Double 2.200000
37
>>>
Việc chỉ định một định dạng sẽ bảo vệ khỏi các loại đối số không tương thích (giống như nguyên mẫu cho hàm C) và cố gắng chuyển đổi các đối số thành loại hợp lệ
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
ctypes.ArgumentError: đối số 2: TypeError: đối tượng 'int' không thể được hiểu là ctypes.c_char_p
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
Nếu bạn đã xác định các lớp của riêng mình mà bạn chuyển đến các lệnh gọi hàm, thì bạn phải triển khai phương thức lớp from_param() để chúng có thể sử dụng chúng theo trình tự argtypes. Phương thức lớp from_param() nhận đối tượng Python được truyền vào lệnh gọi hàm, nó sẽ thực hiện kiểm tra đánh máy hoặc bất cứ điều gì cần thiết để đảm bảo đối tượng này có thể chấp nhận được, sau đó trả về chính đối tượng đó, thuộc tính _as_parameter_ của nó hoặc bất cứ điều gì bạn muốn chuyển làm đối số hàm C trong trường hợp này. Một lần nữa, kết quả phải là số nguyên, chuỗi, byte, phiên bản ctypes hoặc đối tượng có thuộc tính _as_parameter_.
Các kiểu trả về¶
Theo mặc định, các hàm được giả định trả về loại C int. Các kiểu trả về khác có thể được chỉ định bằng cách đặt thuộc tính restype của đối tượng hàm.
Nguyên mẫu C của time() là time_t time(time_t *). Vì time_t có thể thuộc loại khác với kiểu trả về mặc định int, bạn nên chỉ định thuộc tính restype:
>>> libc.time.resttype = c_time_t
The argument types can be specified using argtypes:
>>> libc.time.argtypes = (POINTER(c_time_t),)
Để gọi hàm với con trỏ NULL làm đối số đầu tiên, hãy sử dụng None:
>>> print(libc.time(None))
1150640792
Đây là một ví dụ nâng cao hơn, nó sử dụng hàm strchr(), hàm này yêu cầu một con trỏ chuỗi và một ký tự, đồng thời trả về một con trỏ tới một chuỗi:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Nếu bạn muốn tránh các lệnh gọi ord("x") ở trên, bạn có thể đặt thuộc tính argtypes và đối số thứ hai sẽ được chuyển đổi từ một đối tượng byte Python ký tự đơn thành char C:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (cuộc gọi gần đây nhất):
ctypes.ArgumentError: đối số 2: TypeError: dự kiến một byte ký tự, bytearray hoặc số nguyên
>>> print(strchr(b"abcdef", b"x"))
không có
>>> strchr(b"abcdef", b"d")
b'def'
>>>
Bạn cũng có thể sử dụng một đối tượng Python có thể gọi được (ví dụ: một hàm hoặc một lớp) làm thuộc tính restype, nếu hàm ngoại trả về một số nguyên. Hàm có thể gọi được sẽ được gọi với integer mà hàm C trả về và kết quả của lệnh gọi này sẽ được sử dụng làm kết quả của lệnh gọi hàm của bạn. Điều này rất hữu ích để kiểm tra các giá trị trả về lỗi và tự động đưa ra một ngoại lệ:
>>> 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
OSError: [Errno 126] The specified module could not be found.
>>>
WinError là một hàm sẽ gọi api Windows FormatMessage() để lấy chuỗi biểu thị mã lỗi và returns là một ngoại lệ. WinError lấy tham số mã lỗi tùy chọn, nếu không ai sử dụng thì nó gọi GetLastError() để lấy lại.
Xin lưu ý rằng có sẵn cơ chế kiểm tra lỗi mạnh mẽ hơn nhiều thông qua thuộc tính errcheck; xem tài liệu tham khảo để biết chi tiết.
Truyền con trỏ (hoặc: truyền tham số bằng tham chiếu)¶
Đôi khi, hàm C api yêu cầu pointer chuyển thành kiểu dữ liệu làm tham số, có thể ghi vào vị trí tương ứng hoặc nếu dữ liệu quá lớn không thể truyền theo giá trị. Điều này còn được gọi là passing parameters by reference.
ctypes xuất hàm byref() được sử dụng để truyền tham số theo tham chiếu. Hiệu ứng tương tự có thể đạt được với hàm pointer(), mặc dù pointer() thực hiện nhiều công việc hơn vì nó xây dựng một đối tượng con trỏ thực, do đó, sử dụng byref() sẽ nhanh hơn nếu bạn không cần đối tượng con trỏ trong chính Python:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0,0 b''
>>> libc.sscanf(b"1 3.14 Xin chào", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Xin chào'
>>>
Cơ cấu và công đoàn¶
Các cấu trúc và liên kết phải xuất phát từ các lớp cơ sở Structure và Union được xác định trong mô-đun ctypes. Mỗi lớp con phải xác định thuộc tính _fields_. _fields_ phải là danh sách 2-tuples, chứa field name và field type.
Loại trường phải là loại ctypes như c_int hoặc bất kỳ loại ctypes dẫn xuất nào khác: cấu trúc, liên kết, mảng, con trỏ.
Dưới đây là một ví dụ đơn giản về cấu trúc POINT, chứa hai số nguyên có tên x và y, đồng thời cho biết cách khởi tạo cấu trúc trong hàm tạo:
>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> điểm = POINT(10, 20)
>>> in(point.x, point.y)
10 20
>>> điểm = POINT(y=5)
>>> in(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
TypeError: quá nhiều công cụ khởi tạo
>>>
Tuy nhiên, bạn có thể xây dựng các cấu trúc phức tạp hơn nhiều. Bản thân một cấu trúc có thể chứa các cấu trúc khác bằng cách sử dụng cấu trúc làm kiểu trường.
Đây là cấu trúc RECT chứa hai POINT có tên upperleft và lowerright:
>>> lớp RECT(Cấu trúc):
... _fields_ = [("upperleft", POINT),
... ("phía dưới bên phải", POINT)]
...
>>> rc = RECT(điểm)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowright.x, rc.lowright.y)
0 0
>>>
Các cấu trúc lồng nhau cũng có thể được khởi tạo trong hàm tạo theo nhiều cách:
>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))
Trường descriptors có thể được truy xuất từ class, chúng rất hữu ích cho việc gỡ lỗi vì chúng có thể cung cấp thông tin hữu ích. Xem CField:
>>> POINT.x
<ctypes.CField 'x' type=c_int, ofs=0, size=4>
>>> POINT.y
<ctypes.CField 'y' type=c_int, ofs=4, size=4>
>>>
Cảnh báo
ctypes không hỗ trợ chuyển các liên kết hoặc cấu trúc có trường bit sang các hàm theo giá trị. Mặc dù điều này có thể hoạt động trên x86 32 bit, nhưng thư viện không đảm bảo nó sẽ hoạt động trong trường hợp chung. Các kết hợp và cấu trúc có trường bit phải luôn được chuyển đến các hàm bằng con trỏ.
Bố cục cấu trúc/liên kết, căn chỉnh và thứ tự byte¶
Theo mặc định, các trường Cấu trúc và Liên kết được trình bày giống như cách trình biên dịch C thực hiện. Có thể ghi đè hoàn toàn hành vi này bằng cách chỉ định thuộc tính lớp _layout_ trong định nghĩa lớp con; xem tài liệu thuộc tính để biết chi tiết.
Có thể chỉ định căn chỉnh tối đa cho các trường và/hoặc cho chính cấu trúc bằng cách đặt các thuộc tính lớp _pack_ và/hoặc _align_ tương ứng. Xem tài liệu thuộc tính để biết chi tiết.
ctypes sử dụng thứ tự byte gốc cho Cấu trúc và Liên minh. Để xây dựng cấu trúc với thứ tự byte không phải gốc, bạn có thể sử dụng một trong các lớp cơ sở BigEndianStructure, LittleEndianStructure, BigEndianUnion và LittleEndianUnion. Các lớp này không thể chứa các trường con trỏ.
Các trường bit trong cấu trúc và công đoàn¶
Có thể tạo các cấu trúc và liên kết chứa các trường bit. Các trường bit chỉ có thể áp dụng cho các trường số nguyên, độ rộng bit được chỉ định là mục thứ ba trong bộ dữ liệu _fields_
>>> lớp Int(Cấu trúc):
... _fields_ = [("first_16", c_int, 16),
... ("thứ hai_16", c_int, 16)]
...
>>> in(Int.first_16)
<ctypes.CField 'first_16' type=c_int, ofs=0, bit_size=16, bit_offset=0>
>>> in(Int.second_16)
<ctypes.CField 'second_16' type=c_int, ofs=0, bit_size=16, bit_offset=16>
Điều quan trọng cần lưu ý là việc phân bổ và bố cục trường bit trong bộ nhớ không được xác định là tiêu chuẩn C; việc triển khai của chúng là dành riêng cho trình biên dịch. Theo mặc định, Python sẽ cố gắng khớp hành vi của trình biên dịch "gốc" cho nền tảng hiện tại. Xem thuộc tính _layout_ để biết chi tiết về hành vi mặc định và cách thay đổi nó.
Mảng¶
Mảng là các chuỗi, chứa một số lượng cố định các thể hiện cùng loại.
Cách được khuyến nghị để tạo kiểu mảng là nhân kiểu dữ liệu với số nguyên dương:
TenPointsArrayType = POINT * 10
Dưới đây là ví dụ về kiểu dữ liệu hơi giả tạo, cấu trúc chứa 4 ĐIỂM trong số các nội dung khác
>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> lớp MyStruct(Cấu trúc):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct(). point_array))
4
>>>
Các cá thể được tạo theo cách thông thường, bằng cách gọi lớp:
mảng = TenPointsArrayType()
cho pt trong mảng:
in(pt.x, pt.y)
Đoạn mã trên in một loạt dòng 0 0, vì nội dung mảng được khởi tạo bằng 0.
Công cụ khởi tạo đúng loại cũng có thể được chỉ định
>>> từ nhập ctypes *
>>> Mười số nguyên = c_int * 10
>>> ii = Mười số nguyên(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> in(ii)
<c_long_Array_10 đối tượng ở 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>
Con trỏ¶
Các phiên bản con trỏ được tạo bằng cách gọi hàm pointer() trên loại ctypes
>>> từ nhập ctypes *
>>> i = c_int(42)
>>> pi = con trỏ(i)
>>>
Các cá thể con trỏ có thuộc tính contents trả về đối tượng mà con trỏ trỏ tới, đối tượng i ở trên:
>>> pi.contents
c_long(42)
>>>
Lưu ý rằng ctypes không có OOR (trả về đối tượng ban đầu), nó xây dựng một đối tượng mới, tương đương mỗi khi bạn truy xuất một thuộc tính
>>> pi.contents là tôi
sai
>>> pi.contents là pi.contents
sai
>>>
Việc gán một phiên bản c_int khác cho thuộc tính nội dung của con trỏ sẽ khiến con trỏ trỏ đến vị trí bộ nhớ nơi nó được lưu trữ
>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>
Các cá thể con trỏ cũng có thể được lập chỉ mục bằng số nguyên:
>>> pi[0]
99
>>>
Việc gán cho một chỉ mục số nguyên sẽ thay đổi giá trị được trỏ tới
>>> in(i)
c_long(99)
>>> pi[0] = 22
>>> in(i)
c_long(22)
>>>
Cũng có thể sử dụng các chỉ mục khác 0, nhưng bạn phải biết mình đang làm gì, giống như trong C: Bạn có thể truy cập hoặc thay đổi các vị trí bộ nhớ tùy ý. Nói chung, bạn chỉ sử dụng tính năng này nếu bạn nhận được một con trỏ từ hàm C và bạn biết rằng con trỏ đó thực sự trỏ đến một mảng thay vì một mục duy nhất.
Đằng sau, hàm pointer() không chỉ đơn giản là tạo các phiên bản con trỏ mà trước tiên nó phải tạo con trỏ types. Điều này được thực hiện bằng hàm POINTER(), chấp nhận bất kỳ loại ctypes nào và trả về một loại mới:
>>> PI = POINTER(c_int)
>>> PI
<lớp 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
TypeError: dự kiến c_long thay vì int
>>> PI(c_int(42))
<ctypes.LP_c_long đối tượng ở 0x...>
>>>
Gọi loại con trỏ mà không có đối số sẽ tạo ra con trỏ NULL. Con trỏ NULL có giá trị boolean False
>>> null_ptr = POINTER(c_int)()
>>> in(bool(null_ptr))
sai
>>>
ctypes kiểm tra NULL khi các con trỏ hội thảo không hợp lệ (nhưng các con trỏ không phảiNULL không hợp lệ sẽ làm hỏng Python):
>>> null_ptr[0]
Traceback (cuộc gọi gần đây nhất):
....
ValueError: truy cập con trỏ NULL
>>>
>>> null_ptr[0] = 1234
Traceback (cuộc gọi gần đây nhất):
....
ValueError: truy cập con trỏ NULL
>>>
An toàn chỉ khi không có GIL¶
Từ Python 3.13 trở đi, GIL có thể bị tắt trên free-threaded build. Trong ctypes, việc đọc và ghi đồng thời vào một đối tượng là an toàn, nhưng không an toàn trên nhiều đối tượng:
>>> số = c_int(42) >>> con trỏ_a = con trỏ(số) >>> con trỏ_b = con trỏ(số)
Ở trên, chỉ an toàn cho một đối tượng đọc và ghi vào địa chỉ cùng một lúc nếu GIL bị tắt. Vì vậy, pointer_a có thể được chia sẻ và ghi vào nhiều luồng, nhưng chỉ khi pointer_b không cố gắng làm điều tương tự. Nếu đây là sự cố, hãy cân nhắc sử dụng threading.Lock để đồng bộ hóa quyền truy cập vào bộ nhớ:
>>> nhập luồng >>> lock = threading.Lock() >>> # Thread 1 >>> có khóa: ... con trỏ_a.contents = 24 >>> # Thread 2 >>> có khóa: ... con trỏ_b.contents = 42
Chuyển đổi loại¶
Thông thường, ctypes thực hiện kiểm tra loại nghiêm ngặt. Điều này có nghĩa là, nếu bạn có POINTER(c_int) trong danh sách argtypes của một hàm hoặc là loại của trường thành viên trong định nghĩa cấu trúc thì chỉ các phiên bản có cùng loại mới được chấp nhận. Có một số ngoại lệ đối với quy tắc này, trong đó ctypes chấp nhận các đối tượng khác. Ví dụ: bạn có thể chuyển các thể hiện mảng tương thích thay vì các kiểu con trỏ. Vì vậy, đối với POINTER(c_int), ctypes chấp nhận một mảng c_int:
>>> Thanh lớp (Cấu trúc):
... _fields_ = [("đếm", c_int), ("giá trị", POINTER(c_int))]
...
>>> thanh = Thanh()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> cho i trong phạm vi (bar.count):
... print(bar.values[i])
...
1
2
3
>>>
Ngoài ra, nếu một đối số hàm được khai báo rõ ràng là một loại con trỏ (chẳng hạn như POINTER(c_int)) trong argtypes, thì một đối tượng thuộc loại nhọn (c_int trong trường hợp này) có thể được truyền cho hàm. ctypes sẽ tự động áp dụng chuyển đổi byref() cần thiết trong trường hợp này.
Để đặt trường loại POINTER thành NULL, bạn có thể gán None:
>>> bar.values = Không
>>>
Đôi khi bạn có những trường hợp thuộc loại không tương thích. Trong C, bạn có thể chuyển kiểu này sang kiểu khác. ctypes cung cấp chức năng cast() có thể được sử dụng theo cách tương tự. Cấu trúc Bar được xác định ở trên chấp nhận các con trỏ POINTER(c_int) hoặc mảng c_int cho trường values của nó, nhưng không chấp nhận các phiên bản của các loại khác:
>>> bar.values = (c_byte * 4)()
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
TypeError: các loại không tương thích, phiên bản c_byte_Array_4 thay vì phiên bản LP_c_long
>>>
Đối với những trường hợp này, chức năng cast() rất hữu ích.
Hàm cast() có thể được sử dụng để chuyển một thể hiện ctypes thành một con trỏ tới một kiểu dữ liệu ctypes khác. cast() có hai tham số, một đối tượng ctypes đang hoặc có thể được chuyển đổi thành một loại con trỏ nào đó và một loại con trỏ ctypes. Nó trả về một thể hiện của đối số thứ hai, tham chiếu cùng khối bộ nhớ với đối số đầu tiên
>>> a = (c_byte * 4)()
>>> diễn viên(a, POINTER(c_int))
<ctypes.LP_c_long đối tượng tại ...>
>>>
Vì vậy, cast() có thể được sử dụng để gán cho trường values của Bar cấu trúc:
>>> thanh = Thanh()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
Các loại chưa hoàn chỉnh¶
Incomplete Types là các cấu trúc, liên kết hoặc mảng mà các thành viên chưa được chỉ định. Trong C, chúng được chỉ định bằng các khai báo chuyển tiếp, được xác định sau
tế bào cấu trúc; /* khai báo chuyển tiếp */
ô cấu trúc {
char *tên;
ô cấu trúc *tiếp theo;
};
Bản dịch đơn giản sang mã ctypes sẽ là thế này, nhưng nó không hoạt động
>>> ô lớp (Cấu trúc):
... _fields_ = [("tên", c_char_p),
... ("tiếp theo", POINTER(ô))]
...
Traceback (cuộc gọi gần đây nhất):
Tệp "<stdin>", dòng 1, trong <module>
Tệp "<stdin>", dòng 2, trong ô
NameError: tên 'ô' không được xác định
>>>
bởi vì class cell mới không có sẵn trong chính câu lệnh lớp. Trong ctypes, chúng ta có thể định nghĩa lớp cell và đặt thuộc tính _fields_ sau, sau câu lệnh lớp
>>> từ nhập ctypes *
>>> ô lớp (Cấu trúc):
... vượt qua
...
>>> cell._fields_ = [("name", c_char_p),
... ("tiếp theo", POINTER(ô))]
>>>
Hãy thử nó. Chúng tôi tạo hai phiên bản của cell và để chúng trỏ đến nhau và cuối cùng theo chuỗi con trỏ một vài lần
>>> c1 = ô()
>>> c1.name = b"foo"
>>> c2 = ô()
>>> c2.name = b"bar"
>>> c1.next = con trỏ(c2)
>>> c2.next = con trỏ(c1)
>>> p = c1
>>> cho i trong phạm vi (8):
... print(p.name, end=" ")
... p = p.next[0]
...
thanh foo thanh foo thanh foo thanh foo thanh
>>>
Chức năng gọi lại¶
ctypes cho phép tạo các con trỏ hàm có thể gọi được C từ các lệnh gọi được của Python. Đôi khi chúng được gọi là callback functions.
Đầu tiên, bạn phải tạo một lớp cho hàm gọi lại. Lớp biết quy ước gọi, kiểu trả về cũng như số lượng và kiểu đối số mà hàm này sẽ nhận được.
Hàm xuất xưởng CFUNCTYPE() tạo các loại cho hàm gọi lại bằng cách sử dụng quy ước gọi cdecl. Trên Windows, hàm xuất xưởng WINFUNCTYPE() tạo các kiểu cho hàm gọi lại bằng cách sử dụng quy ước gọi stdcall.
Cả hai hàm xuất xưởng này đều được gọi với loại kết quả là đối số đầu tiên và các hàm gọi lại dự kiến loại đối số là đối số còn lại.
Ở đây tôi sẽ trình bày một ví dụ sử dụng hàm qsort() của thư viện C tiêu chuẩn, được dùng để sắp xếp các mục với sự trợ giúp của hàm gọi lại. qsort() sẽ được sử dụng để sắp xếp một mảng các số nguyên:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = Không có
>>>
qsort() phải được gọi bằng một con trỏ tới dữ liệu cần sắp xếp, số lượng mục trong mảng dữ liệu, kích thước của một mục và một con trỏ tới hàm so sánh, hàm gọi lại. Lệnh gọi lại sau đó sẽ được gọi với hai con trỏ tới các mục và nó phải trả về một số nguyên âm nếu mục đầu tiên nhỏ hơn mục thứ hai, số 0 nếu chúng bằng nhau và một số nguyên dương nếu ngược lại.
Vì vậy, hàm gọi lại của chúng ta nhận con trỏ tới số nguyên và phải trả về một số nguyên. Đầu tiên chúng ta tạo type cho hàm gọi lại:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
Để bắt đầu, đây là một lệnh gọi lại đơn giản hiển thị các giá trị mà nó được chuyển:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... trả về 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Kết quả:
>>> 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
>>>
Bây giờ chúng ta thực sự có thể so sánh hai mục và trả về một kết quả hữu ích
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> 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
>>>
Như chúng ta có thể dễ dàng kiểm tra, mảng của chúng ta hiện đã được sắp xếp:
>>> cho i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
Các nhà máy chức năng có thể được sử dụng làm nhà máy trang trí, vì vậy chúng ta cũng có thể viết:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... trả về a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), 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
>>>
Ghi chú
Đảm bảo bạn giữ các tham chiếu đến đối tượng CFUNCTYPE() miễn là chúng được sử dụng từ mã C. ctypes thì không, và nếu bạn không làm vậy, chúng có thể bị thu gom rác, làm hỏng chương trình của bạn khi thực hiện lệnh gọi lại.
Ngoài ra, hãy lưu ý rằng nếu hàm gọi lại được gọi trong một luồng được tạo bên ngoài sự kiểm soát của Python (ví dụ: bằng mã nước ngoài gọi hàm gọi lại), thì ctypes sẽ tạo một luồng Python giả mới trên mỗi lệnh gọi. Hành vi này đúng cho hầu hết các mục đích, nhưng điều đó có nghĩa là các giá trị được lưu trữ bằng threading.local sẽ tồn tại qua các lệnh gọi lại khác nhau, ngay cả khi các lệnh gọi đó được thực hiện từ cùng một luồng C.
Truy cập các giá trị được xuất từ dll¶
Một số thư viện dùng chung không chỉ xuất các hàm mà còn xuất các biến. Một ví dụ trong chính thư viện Python là Py_Version, số phiên bản thời gian chạy Python được mã hóa dưới dạng một số nguyên không đổi.
ctypes có thể truy cập các giá trị như thế này bằng các phương thức lớp in_dll() cùng loại. pythonapi là biểu tượng được xác định trước cho phép truy cập vào api Python C
>>> phiên bản = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> in(hex(version.value))
0x30c00a0
Một ví dụ mở rộng cũng minh họa cách sử dụng con trỏ truy cập con trỏ PyImport_FrozenModules do Python xuất.
Trích dẫn tài liệu cho giá trị đó:
Con trỏ này được khởi tạo để trỏ đến một mảng các bản ghi
_frozen, được kết thúc bởi một bản ghi có tất cả các thành viên làNULLhoặc 0. Khi một mô-đun bị đóng băng được nhập vào, nó sẽ được tìm kiếm trong bảng này. Mã của bên thứ ba có thể giở trò với điều này để cung cấp một bộ sưu tập các mô-đun cố định được tạo động.
Vì vậy, việc thao tác con trỏ này thậm chí có thể hữu ích. Để hạn chế kích thước ví dụ, chúng tôi chỉ hiển thị cách đọc bảng này bằng ctypes:
>>> từ nhập ctypes *
>>>
>>> lớp struct_frozen(Cấu trúc):
... _fields_ = [("tên", c_char_p),
... ("mã", POINTER(c_ubyte)),
... ("kích thước", c_int),
... ("get_code", POINTER(c_ubyte)), con trỏ # Function
...]
...
>>>
Chúng tôi đã xác định kiểu dữ liệu _frozen, vì vậy chúng tôi có thể đưa con trỏ tới bảng
>>> FrozenTable = POINTER(struct_frozen)
>>> bảng = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>
Vì table là một pointer của mảng các bản ghi struct_frozen nên chúng ta có thể lặp lại nó, nhưng chúng ta chỉ cần đảm bảo rằng vòng lặp của chúng ta kết thúc, vì con trỏ không có kích thước. Sớm hay muộn nó có thể sẽ gặp sự cố do vi phạm quyền truy cập hoặc bất cứ điều gì, vì vậy tốt hơn là bạn nên thoát ra khỏi vòng lặp khi chúng ta nhấn vào mục NULL:
>>> cho mục trong bảng:
... nếu item.name là Không có:
... nghỉ
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
zipnhập khẩu 12345
>>>
Việc Python tiêu chuẩn có mô-đun cố định và gói cố định (được biểu thị bằng thành viên size âm) không được nhiều người biết đến, nó chỉ được sử dụng để thử nghiệm. Hãy dùng thử với import __hello__ chẳng hạn.
bất ngờ¶
Có một số khía cạnh trong ctypes mà bạn có thể mong đợi điều gì đó khác với những gì thực sự xảy ra.
Hãy xem xét ví dụ sau:
>>> từ nhập ctypes *
>>> lớp POINT(Cấu trúc):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> lớp RECT(Cấu trúc):
... _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 hoán đổi hai điểm
>>> 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
>>>
Ừm. Chúng tôi chắc chắn đã mong đợi câu lệnh cuối cùng sẽ in 3 4 1 2. Chuyện gì đã xảy ra thế? Dưới đây là các bước của dòng rc.a, rc.b = rc.b, rc.a ở trên:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
Lưu ý rằng temp0 và temp1 là các đối tượng vẫn sử dụng bộ đệm trong của đối tượng rc ở trên. Vì vậy, việc thực thi rc.a = temp0 sẽ sao chép nội dung bộ đệm của temp0 vào bộ đệm của rc. Điều này lần lượt thay đổi nội dung của temp1. Vì vậy, nhiệm vụ cuối cùng rc.b = temp1 không mang lại hiệu quả như mong đợi.
Hãy nhớ rằng việc truy xuất các đối tượng con từ Cấu trúc, Liên kết và Mảng không copy đối tượng phụ, thay vào đó, nó truy xuất một đối tượng trình bao bọc truy cập vào bộ đệm bên dưới của đối tượng gốc.
Một ví dụ khác có thể hoạt động khác với những gì người ta mong đợi là đây:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.giá trị
b'abc def ghi'
>>> s.value là s.value
sai
>>>
Ghi chú
Các đối tượng được khởi tạo từ c_char_p chỉ có thể đặt giá trị của chúng thành byte hoặc số nguyên.
Tại sao nó in False? Các phiên bản ctypes là các đối tượng chứa một khối bộ nhớ cộng với một số descriptors truy cập nội dung của bộ nhớ. Việc lưu trữ một đối tượng Python trong khối bộ nhớ không lưu trữ chính đối tượng đó, thay vào đó, contents của đối tượng được lưu trữ. Việc truy cập lại nội dung sẽ tạo ra một đối tượng Python mới mỗi lần!
Các kiểu dữ liệu có kích thước thay đổi¶
ctypes cung cấp một số hỗ trợ cho các mảng và cấu trúc có kích thước thay đổi.
Hàm resize() có thể được sử dụng để thay đổi kích thước bộ nhớ đệm của đối tượng ctypes hiện có. Hàm lấy đối tượng làm đối số đầu tiên và kích thước được yêu cầu tính bằng byte làm đối số thứ hai. Khối bộ nhớ không thể được tạo nhỏ hơn khối bộ nhớ tự nhiên được chỉ định bởi loại đối tượng, ValueError sẽ tăng lên nếu điều này được thử
>>> short_array = (c_short * 4)()
>>> in(sizeof(short_array))
8
>>> thay đổi kích thước(short_array, 4)
Traceback (cuộc gọi gần đây nhất):
...
ValueError: kích thước tối thiểu là 8
>>> thay đổi kích thước(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
Điều này tốt và ổn, nhưng làm cách nào để truy cập các phần tử bổ sung có trong mảng này? Vì loại vẫn chỉ biết về 4 phần tử nên chúng tôi gặp lỗi khi truy cập các phần tử khác
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (cuộc gọi gần đây nhất):
...
IndexError: chỉ mục không hợp lệ
>>>
Một cách khác để sử dụng các loại dữ liệu có kích thước thay đổi với ctypes là sử dụng tính chất động của Python và (-) xác định lại loại dữ liệu sau khi đã biết kích thước yêu cầu, tùy từng trường hợp.
tài liệu tham khảo ctypes¶
Chức năng đối ngoại¶
Như đã giải thích ở phần trước, các hàm ngoại có thể được truy cập dưới dạng thuộc tính của các thư viện dùng chung được tải. Các đối tượng hàm được tạo theo cách này theo mặc định chấp nhận bất kỳ số lượng đối số nào, chấp nhận bất kỳ trường hợp dữ liệu ctypes nào làm đối số và trả về loại kết quả mặc định được chỉ định bởi trình tải thư viện.
Chúng là các phiên bản của lớp _FuncPtr cục bộ riêng tư (không được hiển thị trong ctypes) kế thừa từ lớp _CFuncPtr riêng tư:
>>> nhập ctypes
>>> lib = ctypes.CDLL(Không có)
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
đúng
>>> lib._FuncPtr là ctypes._CFuncPtr
sai
- class ctypes._CFuncPtr¶
Lớp cơ sở cho các hàm nước ngoài có thể gọi được trong C.
Các phiên bản của hàm ngoại cũng là loại dữ liệu tương thích với C; chúng đại diện cho các con trỏ hàm C.
Hành vi này có thể được tùy chỉnh bằng cách gán cho các thuộc tính đặc biệt của đối tượng hàm ngoại.
- restype¶
Gán loại ctypes để chỉ định loại kết quả của hàm ngoại. Sử dụng
Nonecho void, một hàm không trả về bất cứ thứ gì.Có thể gán một đối tượng Python có thể gọi được không phải là loại ctypes, trong trường hợp này, hàm được giả sử trả về C int và đối tượng có thể gọi được sẽ được gọi với số nguyên này, cho phép xử lý thêm hoặc kiểm tra lỗi. Việc sử dụng tính năng này không được dùng nữa, để xử lý bài đăng hoặc kiểm tra lỗi linh hoạt hơn, hãy sử dụng kiểu dữ liệu ctypes là
restypevà gán một lệnh gọi được cho thuộc tínherrcheck.
- argtypes¶
Gán một bộ loại ctypes để chỉ định loại đối số mà hàm chấp nhận. Các hàm sử dụng quy ước gọi
stdcallchỉ có thể được gọi với cùng số lượng đối số bằng độ dài của bộ dữ liệu này; các hàm sử dụng quy ước gọi C cũng chấp nhận các đối số bổ sung, không xác định.Khi một hàm ngoại được gọi, mỗi đối số thực tế sẽ được chuyển đến phương thức lớp
from_param()của các mục trong bộargtypes, phương thức này cho phép điều chỉnh đối số thực tế cho phù hợp với một đối tượng mà hàm ngoại chấp nhận. Ví dụ: một mụcc_char_ptrong bộargtypessẽ chuyển đổi một chuỗi được truyền dưới dạng đối số thành đối tượng byte bằng cách sử dụng quy tắc chuyển đổi ctypes.Mới: Hiện tại có thể đặt các mục trong argtypes không phải là loại ctypes, nhưng mỗi mục phải có một phương thức
from_param()trả về một giá trị có thể sử dụng làm đối số (ví dụ số nguyên, chuỗi, ctypes). Điều này cho phép xác định các bộ điều hợp có thể điều chỉnh các đối tượng tùy chỉnh dưới dạng tham số hàm.
- errcheck¶
Gán một hàm Python hoặc một hàm khác có thể gọi được cho thuộc tính này. Có thể gọi được sẽ được gọi với ba đối số trở lên:
- callable(result, func, arguments)
result là hàm ngoại trả về, như được chỉ định bởi thuộc tính
restype.func chính là đối tượng hàm ngoại, điều này cho phép sử dụng lại cùng một đối tượng có thể gọi được để kiểm tra hoặc đăng xử lý kết quả của một số hàm.
arguments là một bộ chứa các tham số ban đầu được truyền cho lệnh gọi hàm, điều này cho phép chuyên môn hóa hành vi trên các đối số được sử dụng.
Đối tượng mà hàm này trả về sẽ được trả về từ lệnh gọi hàm ngoại, nhưng nó cũng có thể kiểm tra giá trị kết quả và đưa ra một ngoại lệ nếu lệnh gọi hàm ngoại không thành công.
Trên Windows, khi một lệnh gọi hàm nước ngoài phát sinh một ngoại lệ hệ thống (ví dụ: do vi phạm quyền truy cập), nó sẽ bị ghi lại và thay thế bằng một ngoại lệ Python phù hợp. Hơn nữa, một sự kiện kiểm tra ctypes.set_exception với đối số code sẽ được đưa ra, cho phép móc kiểm tra thay thế ngoại lệ bằng ngoại lệ của chính nó.
Một số cách để gọi các lệnh gọi hàm nước ngoài cũng như một số hàm trong mô-đun này có thể tạo ra sự kiện kiểm tra ctypes.call_function với các đối số function pointer và arguments.
Nguyên mẫu chức năng¶
Các hàm ngoại cũng có thể được tạo bằng cách khởi tạo các nguyên mẫu hàm. Nguyên mẫu hàm tương tự như nguyên mẫu hàm trong C; họ mô tả một hàm (kiểu trả về, kiểu đối số, quy ước gọi) mà không xác định cách triển khai. Các hàm xuất xưởng phải được gọi với loại kết quả mong muốn và các loại đối số của hàm, đồng thời có thể được sử dụng làm các xưởng trang trí và do đó, được áp dụng cho các hàm thông qua cú pháp @wrapper. Xem Chức năng gọi lại để biết ví dụ.
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi C tiêu chuẩn. Chức năng này sẽ giải phóng GIL trong khi gọi. Nếu use_errno được đặt thành true, bản sao riêng ctypes của biến
errnohệ thống sẽ được trao đổi với giá trịerrnothực trước và sau cuộc gọi; use_last_error cũng làm tương tự với mã lỗi Windows.
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi
stdcall. Chức năng này sẽ giải phóng GIL trong khi gọi. use_errno và use_last_error có ý nghĩa tương tự như trên.sẵn có: Windows
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
Nguyên mẫu hàm được trả về tạo ra các hàm sử dụng quy ước gọi Python. Chức năng này sẽ not giải phóng GIL trong khi gọi.
Nguyên mẫu hàm được tạo bởi các hàm xuất xưởng này có thể được khởi tạo theo nhiều cách khác nhau, tùy thuộc vào loại và số lượng tham số trong lệnh gọi:
- prototype(address)
Trả về hàm ngoại tại địa chỉ đã chỉ định và phải là số nguyên.
- prototype(callable)
Tạo hàm có thể gọi C (hàm gọi lại) từ Python callable.
- prototype(func_spec[, paramflags])
Trả về một hàm ngoại được xuất bởi thư viện dùng chung. func_spec phải là
(name_or_ordinal, library)gồm 2 bộ. Mục đầu tiên là tên của hàm được xuất dưới dạng chuỗi hoặc thứ tự của hàm được xuất dưới dạng số nguyên nhỏ. Mục thứ hai là phiên bản thư viện dùng chung.
- prototype(vtbl_index, name[, paramflags[, iid]])
Trả về một hàm ngoại sẽ gọi phương thức COM. vtbl_index là chỉ số trong bảng hàm ảo, một số nguyên nhỏ không âm. name là tên của phương thức COM. iid là một con trỏ tùy chọn tới mã định danh giao diện được sử dụng trong báo cáo lỗi mở rộng.
Nếu iid không được chỉ định,
OSErrorsẽ được nâng lên nếu lệnh gọi phương thức COM không thành công. Nếu iid được chỉ định, thay vào đó,COMErrorsẽ được nâng lên.Các phương thức COM sử dụng quy ước gọi đặc biệt: Chúng yêu cầu một con trỏ tới giao diện COM làm đối số đầu tiên, ngoài các tham số được chỉ định trong bộ dữ liệu
argtypes.sẵn có: Windows
Tham số paramflags tùy chọn tạo ra các trình bao bọc hàm ngoại với nhiều chức năng hơn các tính năng được mô tả ở trên.
paramflags phải là một bộ có cùng độ dài với argtypes.
Mỗi mục trong bộ dữ liệu này chứa thông tin bổ sung về một tham số, nó phải là một bộ dữ liệu chứa một, hai hoặc ba mục.
Mục đầu tiên là một số nguyên chứa tổ hợp các cờ chỉ hướng cho tham số:
- 1
Chỉ định một tham số đầu vào cho hàm.
- 2
Tham số đầu ra. Hàm nước ngoài điền vào một giá trị.
- 4
Tham số đầu vào mặc định là số nguyên 0.
Mục thứ hai tùy chọn là tên tham số dưới dạng chuỗi. Nếu điều này được chỉ định, hàm ngoại có thể được gọi với các tham số được đặt tên.
Mục thứ ba tùy chọn là giá trị mặc định cho tham số này.
Ví dụ sau đây minh họa cách bọc hàm Windows MessageBoxW để nó hỗ trợ các tham số mặc định và các đối số được đặt tên. Khai báo C từ tệp tiêu đề windows là thế này
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
chú thích LPCWSTR lp,
UINT uType);
Đây là gói với ctypes:
>>> từ ctypes nhập c_int, WINFUNCTYPE, windll
>>> từ ctypes.wintypes nhập HWND, LPCWSTR, UINT
>>> nguyên mẫu = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Xin chào từ ctypes"), (1, "flags", 0)
>>> MessageBox = nguyên mẫu(("MessageBoxW", Windll.user32), paramflags)
Giờ đây, hàm ngoại MessageBox có thể được gọi theo những cách sau:
>>> Hộp tin nhắn()
>>> MessageBox(text="Thư rác, thư rác, thư rác")
>>> MessageBox(flags=2, text="foo bar")
Ví dụ thứ hai thể hiện các tham số đầu ra. Hàm win32 GetWindowRect truy xuất kích thước của một cửa sổ được chỉ định bằng cách sao chép chúng vào cấu trúc RECT mà người gọi phải cung cấp. Đây là khai báo C:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
Đây là gói với ctypes:
>>> từ ctypes nhập POINTER, WINFUNCTYPE, windll, WinError
>>> từ ctypes.wintypes nhập BOOL, HWND, RECT
>>> nguyên mẫu = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lpfect")
>>> GetWindowRect = nguyên mẫu(("GetWindowRect", windll.user32), paramflags)
>>>
Các hàm có tham số đầu ra sẽ tự động trả về giá trị tham số đầu ra nếu có một giá trị tham số đầu ra hoặc một bộ chứa các giá trị tham số đầu ra khi có nhiều hơn một, do đó, hàm GetWindowRect hiện trả về một phiên bản RECT khi được gọi.
Các tham số đầu ra có thể được kết hợp với giao thức errcheck để thực hiện thêm quá trình xử lý đầu ra và kiểm tra lỗi. Hàm api win32 GetWindowRect trả về BOOL để báo hiệu thành công hay thất bại, do đó, hàm này có thể thực hiện kiểm tra lỗi và đưa ra một ngoại lệ khi lệnh gọi api không thành công:
>>> def errcheck(kết quả, func, args):
... nếu không có kết quả:
... nâng cao WinError()
... trả về đối số
...
>>> GetWindowRect.errcheck = errcheck
>>>
Nếu hàm errcheck trả về bộ đối số mà nó nhận được không thay đổi, thì ctypes sẽ tiếp tục quá trình xử lý bình thường đối với các tham số đầu ra. Nếu muốn trả về một bộ tọa độ cửa sổ thay vì phiên bản RECT, bạn có thể truy xuất các trường trong hàm và trả về chúng, quá trình xử lý thông thường sẽ không diễn ra nữa:
>>> def errcheck(kết quả, func, args):
... nếu không có kết quả:
... nâng cao WinError()
... rc = args[1]
... trả về rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>
Chức năng tiện ích¶
- ctypes.addressof(obj)¶
Trả về địa chỉ của bộ nhớ đệm dưới dạng số nguyên. obj phải là phiên bản của loại ctypes.
Tăng một auditing event
ctypes.addressofvới đối sốobj.
- ctypes.alignment(obj_or_type)¶
Trả về các yêu cầu căn chỉnh của loại ctypes. obj_or_type phải là loại hoặc phiên bản ctypes.
- ctypes.byref(obj[, offset])¶
Trả về một con trỏ nhẹ cho obj, con trỏ này phải là một phiên bản của loại ctypes. offset mặc định là 0 và phải là số nguyên sẽ được thêm vào giá trị con trỏ bên trong.
byref(obj, offset)tương ứng với mã C này:(((char *)&obj) + offset)
Đối tượng được trả về chỉ có thể được sử dụng làm tham số gọi hàm ngoại. Nó hoạt động tương tự như
pointer(obj), nhưng việc xây dựng nhanh hơn rất nhiều.
- ctypes.CopyComPointer(src, dst)¶
Sao chép con trỏ COM từ src sang dst và trả về giá trị
HRESULTcụ thể của Windows.Nếu src không phải là
NULL, phương thứcAddRefcủa nó sẽ được gọi, tăng số lượng tham chiếu.Ngược lại, số tham chiếu của dst sẽ không bị giảm trước khi gán giá trị mới. Trừ khi dst là
NULL, người gọi có trách nhiệm giảm số lượng tham chiếu bằng cách gọi phương thứcReleasecủa nó khi cần thiết.sẵn có: Windows
Added in version 3.14.
- ctypes.cast(obj, type)¶
Hàm này tương tự như toán tử ép kiểu trong C. Nó trả về một phiên bản mới của type trỏ đến cùng khối bộ nhớ với obj. type phải là một loại con trỏ và obj phải là một đối tượng có thể được hiểu là một con trỏ.
- ctypes.create_string_buffer(init, size=None)¶
- ctypes.create_string_buffer(size)
Hàm này tạo ra một bộ đệm ký tự có thể thay đổi. Đối tượng được trả về là mảng ctypes của
c_char.Nếu size được cung cấp (chứ không phải
None), thì đó phải làint. Nó chỉ định kích thước của mảng trả về.Nếu đối số init được đưa ra thì nó phải là
bytes. Nó được sử dụng để khởi tạo các mục mảng. Các byte không được khởi tạo theo cách này được đặt thành 0 (NUL).Nếu size không được cung cấp (hoặc nếu là
None), bộ đệm sẽ được tạo một phần tử lớn hơn init, thêm một bộ kết thúc NUL một cách hiệu quả.Nếu cả hai đối số đều được đưa ra thì size không được nhỏ hơn
len(init).Cảnh báo
Nếu size bằng
len(init)thì dấu kết thúc NUL sẽ không được thêm vào. Đừng coi bộ đệm như vậy là chuỗi C.Ví dụ:
>>> byte(create_string_buffer(2)) b'\x00\x00' >>> byte(create_string_buffer(b'ab')) b'ab\x00' >>> byte(create_string_buffer(b'ab', 2)) b'ab' >>> byte(create_string_buffer(b'ab', 4)) b'ab\x00\x00' >>> byte(create_string_buffer(b'abcdef', 2)) Traceback (cuộc gọi gần đây nhất): ... ValueError: chuỗi byte quá dài
Tăng một auditing event
ctypes.create_string_buffervới các đối sốinit,size.
- ctypes.create_unicode_buffer(init, size=None)¶
- ctypes.create_unicode_buffer(size)
Hàm này tạo ra một bộ đệm ký tự unicode có thể thay đổi. Đối tượng được trả về là mảng ctypes của
c_wchar.Hàm này nhận các đối số giống như
create_string_buffer()ngoại trừ init phải là một chuỗi và size đếmc_wchar.Tăng một auditing event
ctypes.create_unicode_buffervới các đối sốinit,size.
- ctypes.DllCanUnloadNow()¶
Chức năng này là một cái móc cho phép triển khai các máy chủ COM trong quá trình với ctypes. Nó được gọi từ hàm DllCanUnloadNow mà dll mở rộng _ctypes xuất.
sẵn có: Windows
- ctypes.DllGetClassObject()¶
Chức năng này là một cái móc cho phép triển khai các máy chủ COM trong quá trình với ctypes. Nó được gọi từ hàm DllGetClassObject mà dll mở rộng
_ctypesxuất.sẵn có: Windows
- ctypes.util.find_library(name)¶
Cố gắng tìm một thư viện và trả về tên đường dẫn. name là tên thư viện không có tiền tố như
lib, hậu tố như.so,.dylibhoặc số phiên bản (đây là dạng dùng cho tùy chọn liên kết posix-l). Nếu không tìm thấy thư viện, trả vềNone.Chức năng chính xác phụ thuộc vào hệ thống.
Xem Tìm thư viện chia sẻ để có tài liệu đầy đủ.
- ctypes.util.find_msvcrt()¶
Trả về tên tệp của thư viện thời gian chạy VC được Python sử dụng và bởi các mô-đun mở rộng. Nếu không xác định được tên thư viện,
Nonesẽ được trả về.Ví dụ: nếu bạn cần giải phóng bộ nhớ được phân bổ bởi mô-đun mở rộng có lệnh gọi đến
free(void *), điều quan trọng là bạn phải sử dụng hàm trong cùng thư viện đã phân bổ bộ nhớ.sẵn có: Windows
- ctypes.util.dllist()¶
Cố gắng cung cấp danh sách đường dẫn của các thư viện dùng chung được tải vào quy trình hiện tại. Những đường dẫn này không được chuẩn hóa hoặc xử lý theo bất kỳ cách nào. Hàm này có thể tăng
OSErrornếu API nền tảng cơ bản không thành công. Chức năng chính xác phụ thuộc vào hệ thống.On most platforms, the first element of the list represents the current executable file. Nó có thể là một chuỗi trống.
sẵn có: Windows, macOS, iOS, glibc, BSD libc, musl
Added in version 3.14.
- ctypes.FormatError([code])¶
Trả về mô tả bằng văn bản của mã lỗi code. Nếu không chỉ định mã lỗi, mã lỗi cuối cùng sẽ được sử dụng bằng cách gọi hàm Windows API
GetLastError().sẵn có: Windows
- ctypes.GetLastError()¶
Trả về mã lỗi cuối cùng do Windows đặt trong chuỗi cuộc gọi. Hàm này gọi trực tiếp hàm
GetLastError()của Windows, nó không trả về bản sao mã lỗi ctypes-private.sẵn có: Windows
- ctypes.get_errno()¶
Trả về giá trị hiện tại của bản sao ctypes-private của biến
errnohệ thống trong luồng gọi.Tăng auditing event
ctypes.get_errnomà không có đối số.
- ctypes.get_last_error()¶
Trả về giá trị hiện tại của bản sao ctypes-private của biến
LastErrorhệ thống trong luồng gọi.sẵn có: Windows
Tăng auditing event
ctypes.get_last_errormà không có đối số.
- ctypes.memmove(dst, src, count)¶
Tương tự như chức năng thư viện memmove C tiêu chuẩn: sao chép byte count từ src sang dst. dst và src phải là số nguyên hoặc ctypes có thể được chuyển đổi thành con trỏ.
- ctypes.memset(dst, c, count)¶
Tương tự như chức năng thư viện bộ nhớ C tiêu chuẩn: lấp đầy khối bộ nhớ tại địa chỉ dst với count byte có giá trị c. dst phải là số nguyên chỉ định địa chỉ hoặc phiên bản ctypes.
- ctypes.POINTER(type, /)¶
Tạo hoặc trả về kiểu con trỏ ctypes. Các loại con trỏ được lưu vào bộ nhớ đệm và tái sử dụng nội bộ, vì vậy việc gọi hàm này nhiều lần sẽ rẻ hơn. type phải là loại ctypes.
Kiểu con trỏ kết quả được lưu trữ trong thuộc tính
__pointer_type__của type. Có thể đặt thuộc tính này trước lệnh gọi đầu tiên tớiPOINTERđể đặt loại con trỏ tùy chỉnh. Tuy nhiên, việc này không được khuyến khích: việc tạo thủ công một loại con trỏ phù hợp là khó khăn nếu không dựa vào các chi tiết triển khai có thể thay đổi trong các phiên bản Python trong tương lai.
- ctypes.pointer(obj, /)¶
Tạo một thể hiện con trỏ mới, trỏ tới obj. Đối tượng được trả về có kiểu
POINTER(type(obj)).Lưu ý: Nếu bạn chỉ muốn chuyển con trỏ tới một đối tượng cho lệnh gọi hàm ngoại, bạn nên sử dụng
byref(obj)để nhanh hơn nhiều.
- ctypes.resize(obj, size)¶
Hàm này thay đổi kích thước bộ đệm bộ nhớ trong của obj, bộ đệm này phải là một phiên bản của loại ctypes. Không thể làm cho bộ đệm nhỏ hơn kích thước gốc của loại đối tượng, như được đưa ra bởi
sizeof(type(obj)), nhưng có thể phóng to bộ đệm.
- ctypes.set_errno(value)¶
Đặt giá trị hiện tại của bản sao ctypes-private của biến
errnohệ thống trong luồng gọi thành value và trả về giá trị trước đó.Tăng một auditing event
ctypes.set_errnovới đối sốerrno.
- ctypes.set_last_error(value)¶
Đặt giá trị hiện tại của bản sao ctypes-private của biến
LastErrorhệ thống trong luồng gọi thành value và trả về giá trị trước đó.sẵn có: Windows
Tăng một auditing event
ctypes.set_last_errorvới đối sốerror.
- ctypes.sizeof(obj_or_type)¶
Trả về kích thước tính bằng byte của loại ctypes hoặc bộ đệm bộ nhớ phiên bản. Thực hiện tương tự như toán tử C
sizeof.
- ctypes.string_at(ptr, size=-1)¶
Trả về chuỗi byte tại void *ptr. If size được chỉ định, nó được sử dụng làm kích thước, nếu không thì chuỗi được coi là kết thúc bằng 0.
Tăng một auditing event
ctypes.string_atvới các đối sốptr,size.
- ctypes.WinError(code=None, descr=None)¶
Tạo một phiên bản của
OSError. Nếu code không được chỉ định,GetLastError()sẽ được gọi để xác định mã lỗi. Nếu descr không được chỉ định,FormatError()sẽ được gọi để nhận mô tả bằng văn bản về lỗi.sẵn có: Windows
Thay đổi trong phiên bản 3.3: Một phiên bản của
WindowsErrorđã từng được tạo, hiện là bí danh củaOSError.
- ctypes.wstring_at(ptr, size=-1)¶
Trả về chuỗi ký tự rộng tại void *ptr. If size được chỉ định, nó được sử dụng làm số ký tự của chuỗi, nếu không thì chuỗi được coi là kết thúc bằng 0.
Tăng một auditing event
ctypes.wstring_atvới các đối sốptr,size.
- ctypes.memoryview_at(ptr, size, readonly=False)¶
Trả về đối tượng
memoryviewcó độ dài size tham chiếu bộ nhớ bắt đầu từ void *ptr.Nếu readonly là đúng, đối tượng
memoryviewđược trả về không thể được sử dụng để sửa đổi bộ nhớ cơ bản. (Những thay đổi được thực hiện bằng các phương tiện khác sẽ vẫn được phản ánh trong đối tượng được trả về.)Chức năng này tương tự như
string_at()với điểm khác biệt chính là không tạo bản sao của bộ nhớ được chỉ định. Nó là một giải pháp thay thế tương đương về mặt ngữ nghĩa (nhưng hiệu quả hơn) chomemoryview((c_byte * size).from_address(ptr)). (Trong khifrom_address()chỉ lấy số nguyên, ptr cũng có thể được cung cấp dưới dạng đối tượngctypes.POINTERhoặcbyref().)Tăng một auditing event
ctypes.memoryview_atvới các đối sốaddress,size,readonly.Added in version 3.14.
Các kiểu dữ liệu¶
- class ctypes._CData¶
Lớp không công khai này là lớp cơ sở chung của tất cả các kiểu dữ liệu ctypes. Trong số những thứ khác, tất cả các phiên bản loại ctypes đều chứa một khối bộ nhớ chứa dữ liệu tương thích với C; địa chỉ của khối bộ nhớ được trả về bởi hàm trợ giúp
addressof(). Một biến thể hiện khác được hiển thị dưới dạng_objects; cái này chứa các đối tượng Python khác cần được duy trì trong trường hợp khối bộ nhớ chứa con trỏ.Các phương thức phổ biến của kiểu dữ liệu ctypes, đây đều là các phương thức lớp (chính xác thì chúng là các phương thức của metaclass):
- from_buffer(source[, offset])¶
Phương thức này trả về một thể hiện ctypes chia sẻ bộ đệm của đối tượng source. Đối tượng source phải hỗ trợ giao diện bộ đệm có thể ghi. Tham số offset tùy chọn chỉ định phần bù vào bộ đệm nguồn theo byte; mặc định là bằng không. Nếu bộ đệm nguồn không đủ lớn thì
ValueErrorsẽ được nâng lên.Tăng một auditing event
ctypes.cdata/buffervới các đối sốpointer,size,offset.
- from_buffer_copy(source[, offset])¶
Phương thức này tạo một thể hiện ctypes, sao chép bộ đệm từ bộ đệm đối tượng source mà bộ đệm này phải có thể đọc được. Tham số offset tùy chọn chỉ định phần bù vào bộ đệm nguồn tính bằng byte; mặc định là bằng không. Nếu bộ đệm nguồn không đủ lớn thì
ValueErrorsẽ được nâng lên.Tăng một auditing event
ctypes.cdata/buffervới các đối sốpointer,size,offset.
- from_address(address)¶
Phương thức này trả về một thể hiện kiểu ctypes sử dụng bộ nhớ được chỉ định bởi address, bộ nhớ này phải là số nguyên.
Phương thức này và các phương thức khác gọi gián tiếp phương thức này sẽ tạo ra một auditing event
ctypes.cdatavới đối sốaddress.
- from_param(obj)¶
Phương pháp này điều chỉnh obj thành loại ctypes. Nó được gọi với đối tượng thực tế được sử dụng trong lệnh gọi hàm ngoại khi loại này có trong bộ dữ liệu
argtypescủa hàm ngoại; nó phải trả về một đối tượng có thể được sử dụng làm tham số gọi hàm.Tất cả các kiểu dữ liệu ctypes đều có cách triển khai mặc định của phương thức lớp này thường trả về obj nếu đó là một phiên bản của kiểu đó. Một số loại cũng chấp nhận các đối tượng khác.
- in_dll(library, name)¶
Phương thức này trả về một thể hiện kiểu ctypes được xuất bởi thư viện dùng chung. name là tên của biểu tượng xuất dữ liệu, library là thư viện chia sẻ được tải.
Các biến lớp phổ biến của kiểu dữ liệu ctypes:
- __pointer_type__¶
Kiểu con trỏ được tạo bằng cách gọi
POINTER()cho kiểu dữ liệu ctypes tương ứng. Nếu loại con trỏ chưa được tạo thì thuộc tính đó sẽ bị thiếu.Added in version 3.14.
Các biến thể hiện phổ biến của kiểu dữ liệu ctypes:
- _b_base_¶
Đôi khi các phiên bản dữ liệu ctypes không sở hữu khối bộ nhớ mà chúng chứa, thay vào đó chúng chia sẻ một phần khối bộ nhớ của một đối tượng cơ sở. Thành viên chỉ đọc
_b_base_là đối tượng ctypes gốc sở hữu khối bộ nhớ.
- _b_needsfree_¶
Biến chỉ đọc này là đúng khi phiên bản dữ liệu ctypes đã tự phân bổ khối bộ nhớ, nếu không thì sai.
- _objects¶
Thành viên này là
Nonehoặc một từ điển chứa các đối tượng Python cần được duy trì để nội dung khối bộ nhớ được giữ hợp lệ. Đối tượng này chỉ được hiển thị để gỡ lỗi; không bao giờ sửa đổi nội dung của từ điển này.
Các kiểu dữ liệu cơ bản¶
- class ctypes._SimpleCData¶
Lớp không công khai này là lớp cơ sở của tất cả các kiểu dữ liệu ctypes cơ bản. Nó được đề cập ở đây vì nó chứa các thuộc tính chung của các kiểu dữ liệu ctypes cơ bản.
_SimpleCDatalà lớp con của_CDatanên nó kế thừa các phương thức và thuộc tính của chúng. Các kiểu dữ liệu ctypes không có và không chứa con trỏ giờ đây có thể được chọn.Các trường hợp có một thuộc tính duy nhất:
- value¶
Thuộc tính này chứa giá trị thực tế của thể hiện. Đối với các kiểu số nguyên và con trỏ, nó là một số nguyên, đối với các kiểu ký tự, nó là một đối tượng hoặc chuỗi byte ký tự đơn, đối với các kiểu con trỏ ký tự, nó là một đối tượng hoặc chuỗi byte Python.
Khi thuộc tính
valueđược truy xuất từ phiên bản ctypes, thông thường một đối tượng mới sẽ được trả về mỗi lần.ctypesnot thực hiện trả về đối tượng ban đầu, luôn có một đối tượng mới được xây dựng. Điều này cũng đúng với tất cả các phiên bản đối tượng ctypes khác.
Mỗi lớp con có một thuộc tính lớp:
- _type_¶
Thuộc tính lớp chứa mã loại nội bộ, dưới dạng chuỗi ký tự đơn. Xem Các kiểu dữ liệu cơ bản để biết tóm tắt.
Các loại được đánh dấu * trong phần tóm tắt có thể (hoặc luôn luôn là) bí danh của một lớp con
_SimpleCDatakhác và sẽ không nhất thiết phải sử dụng mã loại được liệt kê. Ví dụ: nếu các loại long, long long và time_t C của nền tảng giống nhau thìc_long,c_longlongvàc_time_tđều đề cập đến một lớp duy nhất,c_long, có mã_type_là'l'. Mã'L'sẽ không được sử dụng.
Các kiểu dữ liệu cơ bản, khi được trả về dưới dạng kết quả lệnh gọi hàm ngoại, hoặc, ví dụ, bằng cách truy xuất các thành viên trường cấu trúc hoặc các mục mảng, sẽ được chuyển đổi rõ ràng thành các kiểu Python gốc. Nói cách khác, nếu một hàm ngoại có restype là c_char_p, bạn sẽ luôn nhận được một đối tượng byte Python, not một phiên bản c_char_p.
Các lớp con của kiểu dữ liệu cơ bản not kế thừa hành vi này. Vì vậy, nếu hàm ngoại restype là lớp con của c_void_p, bạn sẽ nhận được một phiên bản của lớp con này từ lệnh gọi hàm. Tất nhiên, bạn có thể lấy giá trị của con trỏ bằng cách truy cập thuộc tính value.
Đây là các kiểu dữ liệu ctypes cơ bản:
- class ctypes.c_byte¶
Biểu thị kiểu dữ liệu C signed char và diễn giải giá trị dưới dạng số nguyên nhỏ. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_char¶
Đại diện cho kiểu dữ liệu C char và diễn giải giá trị dưới dạng một ký tự đơn. Hàm tạo chấp nhận trình khởi tạo chuỗi tùy chọn, độ dài của chuỗi phải chính xác là một ký tự.
- class ctypes.c_char_p¶
Biểu thị kiểu dữ liệu C char* khi nó trỏ đến một chuỗi kết thúc bằng 0. Đối với một con trỏ ký tự chung cũng có thể trỏ đến dữ liệu nhị phân, phải sử dụng
POINTER(c_char). Hàm tạo chấp nhận một địa chỉ số nguyên hoặc một đối tượng byte.
- class ctypes.c_double¶
Đại diện cho kiểu dữ liệu C double. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn.
- class ctypes.c_longdouble¶
Đại diện cho kiểu dữ liệu C long double. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn. Trên nền tảng có
sizeof(long double) == sizeof(double), nó là bí danh củac_double.
- class ctypes.c_float¶
Đại diện cho kiểu dữ liệu C float. Hàm tạo chấp nhận một bộ khởi tạo float tùy chọn.
- class ctypes.c_double_complex¶
Đại diện cho kiểu dữ liệu C double complex, nếu có. Hàm tạo chấp nhận trình khởi tạo
complextùy chọn.Added in version 3.14.
- class ctypes.c_float_complex¶
Đại diện cho kiểu dữ liệu C float complex, nếu có. Hàm tạo chấp nhận trình khởi tạo
complextùy chọn.Added in version 3.14.
- class ctypes.c_longdouble_complex¶
Đại diện cho kiểu dữ liệu C long double complex, nếu có. Hàm tạo chấp nhận trình khởi tạo
complextùy chọn.Added in version 3.14.
- class ctypes.c_int¶
Đại diện cho kiểu dữ liệu C signed int. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có
sizeof(int) == sizeof(long), nó là bí danh củac_long.
- class ctypes.c_int64¶
Đại diện cho kiểu dữ liệu signed int C 64-bit. Thường là bí danh cho
c_longlong.
- class ctypes.c_long¶
Đại diện cho kiểu dữ liệu C signed long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_longlong¶
Đại diện cho kiểu dữ liệu C signed long long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có
sizeof(long long) == sizeof(long), nó là bí danh củac_long.
- class ctypes.c_short¶
Đại diện cho kiểu dữ liệu C signed short. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_size_t¶
Đại diện cho kiểu dữ liệu C
size_t. Thường là bí danh cho một loại số nguyên không dấu khác.
- class ctypes.c_ssize_t¶
Đại diện cho kiểu dữ liệu
Py_ssize_t. Đây là phiên bản có chữ ký củasize_t; tức là loại POSIXssize_t. Thường là bí danh cho một loại số nguyên khác.Added in version 3.2.
- class ctypes.c_time_t¶
Đại diện cho kiểu dữ liệu C
time_t. Thường là bí danh cho một loại số nguyên khác.Added in version 3.12.
- class ctypes.c_ubyte¶
Đại diện cho kiểu dữ liệu C unsigned char, nó diễn giải giá trị dưới dạng số nguyên nhỏ. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_uint¶
Đại diện cho kiểu dữ liệu C unsigned int. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có
sizeof(int) == sizeof(long), nó là bí danh củac_ulong.
- class ctypes.c_uint16¶
Đại diện cho kiểu dữ liệu unsigned int C 16-bit. Thường là bí danh cho
c_ushort.
- class ctypes.c_uint32¶
Đại diện cho kiểu dữ liệu unsigned int C 32-bit. Thường là bí danh cho
c_uint.
- class ctypes.c_uint64¶
Đại diện cho kiểu dữ liệu unsigned int C 64-bit. Thường là bí danh cho
c_ulonglong.
- class ctypes.c_ulong¶
Đại diện cho kiểu dữ liệu C unsigned long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_ulonglong¶
Đại diện cho kiểu dữ liệu C unsigned long long. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện. Trên nền tảng có
sizeof(long long) == sizeof(long), nó là bí danh củac_long.
- class ctypes.c_ushort¶
Đại diện cho kiểu dữ liệu C unsigned short. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn; không có kiểm tra tràn được thực hiện.
- class ctypes.c_void_p¶
Đại diện cho loại C void*. Giá trị được biểu diễn dưới dạng số nguyên. Hàm tạo chấp nhận một bộ khởi tạo số nguyên tùy chọn.
- class ctypes.c_wchar¶
Đại diện cho kiểu dữ liệu C
wchar_tvà diễn giải giá trị dưới dạng một chuỗi unicode ký tự đơn. Hàm tạo chấp nhận trình khởi tạo chuỗi tùy chọn, độ dài của chuỗi phải chính xác là một ký tự.
- class ctypes.c_wchar_p¶
Đại diện cho kiểu dữ liệu C wchar_t*, phải là một con trỏ tới chuỗi ký tự rộng có đầu cuối bằng 0. Hàm tạo chấp nhận một địa chỉ số nguyên hoặc một chuỗi.
- class ctypes.c_bool¶
Đại diện cho kiểu dữ liệu C bool (chính xác hơn là _Bool từ C99). Giá trị của nó có thể là
TruehoặcFalsevà hàm tạo chấp nhận bất kỳ đối tượng nào có giá trị thật.
- class ctypes.HRESULT¶
Đại diện cho giá trị
HRESULT, chứa thông tin thành công hoặc lỗi cho lệnh gọi hàm hoặc phương thức.sẵn có: Windows
- class ctypes.py_object¶
Đại diện cho kiểu dữ liệu C PyObject*. Gọi cái này mà không có đối số sẽ tạo ra một con trỏ
NULLPyObject*.Thay đổi trong phiên bản 3.14:
py_objectbây giờ là generic type.
Mô-đun ctypes.wintypes cung cấp khá nhiều loại dữ liệu cụ thể khác của Windows, ví dụ HWND, WPARAM, VARIANT_BOOL hoặc DWORD. Một số cấu trúc hữu ích như MSG hoặc RECT cũng được xác định.
Các kiểu dữ liệu có cấu trúc¶
- class ctypes.Union(*args, **kw)¶
Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte gốc.
Các công đoàn chia sẻ các thuộc tính và hành vi chung với các cơ cấu; xem tài liệu
Structuređể biết chi tiết.
- class ctypes.BigEndianUnion(*args, **kw)¶
Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte big endian.
Added in version 3.11.
- class ctypes.LittleEndianUnion(*args, **kw)¶
Lớp cơ sở trừu tượng cho các kết hợp theo thứ tự byte little endian.
Added in version 3.11.
- class ctypes.BigEndianStructure(*args, **kw)¶
Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte big endian.
- class ctypes.LittleEndianStructure(*args, **kw)¶
Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte little endian.
Các cấu trúc và liên kết có thứ tự byte không gốc không thể chứa các trường loại con trỏ hoặc bất kỳ loại dữ liệu nào khác chứa các trường loại con trỏ.
- class ctypes.Structure(*args, **kw)¶
Lớp cơ sở trừu tượng cho các cấu trúc theo thứ tự byte native.
Cấu trúc cụ thể và các kiểu kết hợp phải được tạo bằng cách phân lớp con một trong các kiểu này và ít nhất xác định một biến lớp
_fields_.ctypessẽ tạo descriptors cho phép đọc và ghi các trường bằng cách truy cập thuộc tính trực tiếp. Đây là những- _fields_¶
Một trình tự xác định các trường cấu trúc. Các mục phải là 2 bộ hoặc 3 bộ. Mục đầu tiên là tên của trường, mục thứ hai chỉ định loại trường; nó có thể là bất kỳ kiểu dữ liệu ctypes nào.
Đối với các trường loại số nguyên như
c_int, có thể cung cấp mục tùy chọn thứ ba. Nó phải là một số nguyên dương nhỏ xác định độ rộng bit của trường.Tên trường phải là duy nhất trong một cấu trúc hoặc liên kết. Điều này không được kiểm tra, chỉ có thể truy cập một trường khi tên được lặp lại.
Có thể định nghĩa biến lớp
_fields_after câu lệnh lớp xác định lớp con Cấu trúc, điều này cho phép tạo các kiểu dữ liệu tham chiếu trực tiếp hoặc gián tiếp đến chính chúng:Danh sách lớp (Cấu trúc): vượt qua List._fields_ = [("pnext", POINTER(List)), ... ]
Biến lớp
_fields_chỉ có thể được đặt một lần. Các bài tập sau sẽ tăngAttributeError.Ngoài ra, biến lớp
_fields_phải được xác định trước khi cấu trúc hoặc loại kết hợp được sử dụng lần đầu tiên: một phiên bản hoặc lớp con được tạo,sizeof()được gọi trên đó, v.v. Các nhiệm vụ sau này cho_fields_sẽ tăngAttributeError. Nếu_fields_chưa được đặt trước khi sử dụng, cấu trúc hoặc liên kết sẽ không có trường riêng, như thể_fields_trống.Các lớp con của các kiểu cấu trúc kế thừa các trường của lớp cơ sở cộng với
_fields_được xác định trong lớp con, nếu có.
- _pack_¶
Một số nguyên nhỏ tùy chọn cho phép ghi đè việc căn chỉnh các trường cấu trúc trong phiên bản.
Điều này chỉ được triển khai cho bố cục bộ nhớ tương thích với MSVC (xem
_layout_).Đặt
_pack_thành 0 cũng giống như không cài đặt gì cả. Nếu không, giá trị phải là lũy thừa dương của hai. Hiệu ứng này tương đương với#pragma pack(N)trong C, ngoại trừctypescó thể cho phép n lớn hơn mức mà trình biên dịch chấp nhận._pack_phải được xác định khi_fields_được gán, nếu không nó sẽ không có hiệu lực.Không được dùng nữa kể từ phiên bản 3.14, sẽ bị xóa trong phiên bản 3.19: Vì lý do lịch sử, nếu
_pack_khác 0 thì bố cục tương thích với MSVC sẽ được sử dụng theo mặc định. Trên các nền tảng không phải Windows, mặc định này không được dùng nữa và dự kiến sẽ trở thành lỗi trong Python 3.19. Nếu có ý định, hãy đặt_layout_thành'ms'một cách rõ ràng.
- _align_¶
Một số nguyên nhỏ tùy chọn cho phép tăng sự liên kết của cấu trúc khi được đóng gói hoặc giải nén vào/từ bộ nhớ.
Giá trị không được âm. Hiệu ứng này tương đương với
__attribute__((aligned(N)))trên GCC hoặc#pragma align(N)trên MSVC, ngoại trừctypescó thể cho phép các giá trị mà trình biên dịch sẽ từ chối._align_chỉ có thể increase các yêu cầu căn chỉnh của cấu trúc. Đặt nó thành 0 hoặc 1 không có hiệu lực.Việc sử dụng các giá trị không phải là lũy thừa của 2 không được khuyến khích và có thể dẫn đến hành vi đáng ngạc nhiên.
_align_phải được xác định khi_fields_được gán, nếu không nó sẽ không có hiệu lực.Added in version 3.13.
- _layout_¶
Một chuỗi tùy chọn đặt tên cho bố cục struct/union. Hiện tại nó có thể được đặt thành:
"ms": bố cục được trình biên dịch Microsoft sử dụng (MSVC). Trên GCC và Clang, bố cục này có thể được chọn bằng__attribute__((ms_struct))."gcc-sysv": bố cục được GCC sử dụng với mô hình dữ liệu System V hoặc “giống SysV”, như được sử dụng trên Linux và macOS. Với cách bố trí này,_pack_phải được bỏ đặt hoặc bằng 0.
Nếu không được đặt rõ ràng,
ctypessẽ sử dụng giá trị mặc định phù hợp với quy ước của nền tảng. Mặc định này có thể thay đổi trong các bản phát hành Python trong tương lai (ví dụ: khi một nền tảng mới nhận được hỗ trợ chính thức hoặc khi tìm thấy sự khác biệt giữa các nền tảng tương tự). Hiện tại mặc định sẽ là:Trên Windows:
"ms"Khi
_pack_được chỉ định:"ms". (Điều này không được dùng nữa; xem tài liệu_pack_.)Mặt khác:
"gcc-sysv"
_layout_phải được xác định khi_fields_được gán, nếu không nó sẽ không có hiệu lực.Added in version 3.14.
- _anonymous_¶
Một chuỗi tùy chọn liệt kê tên của các trường không tên (ẩn danh).
_anonymous_phải được xác định trước khi_fields_được gán, nếu không nó sẽ không có hiệu lực.Các trường được liệt kê trong biến này phải là các trường loại cấu trúc hoặc kết hợp.
ctypessẽ tạo các bộ mô tả theo kiểu cấu trúc cho phép truy cập trực tiếp vào các trường lồng nhau mà không cần tạo cấu trúc hoặc trường kết hợp.Đây là một loại ví dụ (Windows):
lớp _U(Liên minh): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] lớp TYPEDESC (Cấu trúc): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
Cấu trúc
TYPEDESCmô tả kiểu dữ liệu COM, trườngvtchỉ định một trong các trường hợp hợp lệ. Vì trườnguđược xác định là trường ẩn danh nên giờ đây có thể truy cập trực tiếp vào các thành viên từ phiên bản TYPEDESC.td.lptdescvàtd.u.lptdesctương đương nhau, nhưng cái trước nhanh hơn vì nó không cần tạo một phiên bản hợp nhất tạm thời:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Có thể định nghĩa các lớp con của cấu trúc, chúng kế thừa các trường của lớp cơ sở. Nếu định nghĩa lớp con có một biến
_fields_riêng biệt thì các trường được chỉ định trong phần này sẽ được thêm vào các trường của lớp cơ sở.Các hàm tạo cấu trúc và hợp nhất chấp nhận cả đối số vị trí và từ khóa. Đối số vị trí được sử dụng để khởi tạo các trường thành viên theo thứ tự giống như chúng xuất hiện trong
_fields_. Các đối số từ khóa trong hàm tạo được hiểu là các phép gán thuộc tính, vì vậy chúng sẽ khởi tạo_fields_với cùng tên hoặc tạo thuộc tính mới cho các tên không có trong_fields_.
- class ctypes.CField(*args, **kw)¶
Bộ mô tả cho các trường của
StructurevàUnion. Ví dụ:>>> Màu lớp (Cấu trúc): ... _fields_ = ( ... ('đỏ', c_uint8), ...('xanh', c_uint8), ... ('blue', c_uint8), ... ('dữ dội', c_bool, 1), ... ('nhấp nháy', c_bool, 1), ... ) ... >>> Màu.red <ctypes.CField 'red' type=c_ubyte, ofs=0, size=1> >>> Màu.green.type <lớp 'ctypes.c_ubyte'> >>> Color.blue.byte_offset 2 >>> Màu sắc. mãnh liệt <ctypes.CField 'dữ dội' type=c_bool, ofs=3, bit_size=1, bit_offset=0> >>> Màu.blinking.bit_offset 1
Tất cả các thuộc tính là chỉ đọc.
Các đối tượng
CFieldđược tạo thông qua_fields_; không khởi tạo lớp một cách trực tiếp.Added in version 3.14: Trước đây, bộ mô tả chỉ có thuộc tính
offsetvàsizevà biểu diễn chuỗi có thể đọc được; lớpCFieldkhông có sẵn trực tiếp.- name¶
Tên của trường, dưới dạng một chuỗi.
- type¶
Loại trường, dưới dạng ctypes class.
- offset¶
- byte_offset¶
Độ lệch của trường, tính bằng byte.
Đối với các trường bit, đây là phần bù của storage unit được căn chỉnh theo byte cơ bản; xem
bit_offset.
- byte_size¶
Kích thước của trường, tính bằng byte.
Đối với các trường bit, đây là kích thước của storage unit cơ bản. Thông thường, nó có cùng kích thước với loại của bitfield.
- size¶
Đối với các trường không phải bit, tương đương với
byte_size.Đối với các trường bit, giá trị này chứa giá trị đóng gói bit tương thích ngược kết hợp
bit_sizevàbit_offset. Thay vào đó, hãy ưu tiên sử dụng các thuộc tính rõ ràng.
- is_bitfield¶
Đúng nếu đây là một bitfield.
- bit_offset¶
- bit_size¶
Vị trí của một trường bit trong storage unit của nó, nghĩa là trong
byte_sizebyte bộ nhớ bắt đầu từbyte_offset.Để nhận giá trị của trường, hãy đọc đơn vị lưu trữ dưới dạng số nguyên, shift left x
bit_offsetvà lấy các bit có trọng số nhỏ nhất củabit_size.Đối với các trường không phải bit,
bit_offsetbằng 0 vàbit_sizebằngbyte_size * 8.
- is_anonymous¶
Đúng nếu trường này ẩn danh, nghĩa là nó chứa các trường con lồng nhau cần được hợp nhất thành một cấu trúc hoặc liên kết chứa.
Mảng và con trỏ¶
- class ctypes.Array(*args)¶
Lớp cơ sở trừu tượng cho mảng.
Cách được khuyến nghị để tạo các kiểu mảng cụ thể là nhân bất kỳ kiểu dữ liệu
ctypesnào với một số nguyên không âm. Ngoài ra, bạn có thể phân lớp loại này và xác định các biến lớp_length_và_type_. Các phần tử mảng có thể được đọc và ghi bằng cách sử dụng các truy cập chỉ số và lát cắt tiêu chuẩn; đối với các lần đọc lát cắt, đối tượng kết quả chính là not vàArray.- _length_¶
Một số nguyên dương xác định số phần tử trong mảng. Các chỉ số đăng ký ngoài phạm vi sẽ dẫn đến
IndexError. Sẽ được trả lại bởilen().
- _type_¶
Xác định kiểu của từng phần tử trong mảng.
Hàm tạo của lớp con mảng chấp nhận các đối số vị trí, được sử dụng để khởi tạo các phần tử theo thứ tự.
- ctypes.ARRAY(type, length)¶
Tạo một mảng. Tương đương với
type * length, trong đó type là kiểu dữ liệuctypesvà length là số nguyên.Hàm này là soft deprecated thiên về phép nhân. Không có kế hoạch để loại bỏ nó.
- class ctypes._Pointer¶
Lớp cơ sở riêng tư, trừu tượng cho con trỏ.
Các kiểu con trỏ cụ thể được tạo bằng cách gọi
POINTER()với kiểu sẽ được trỏ tới; việc này được thực hiện tự động bởipointer().Nếu một con trỏ trỏ đến một mảng, các phần tử của nó có thể được đọc và ghi bằng cách sử dụng các truy cập chỉ số dưới và lát cắt tiêu chuẩn. Đối tượng con trỏ không có kích thước nên
len()sẽ tăngTypeError. Các chỉ số âm sẽ đọc từ bộ nhớ before con trỏ (như trong C) và các chỉ số ngoài phạm vi có thể sẽ gặp sự cố do vi phạm quyền truy cập (nếu bạn may mắn).- _type_¶
Chỉ định loại được trỏ đến.
- contents¶
Trả về đối tượng mà con trỏ trỏ tới. Việc gán cho thuộc tính này sẽ thay đổi con trỏ để trỏ tới đối tượng được gán.
Ngoại lệ¶
- exception ctypes.ArgumentError¶
Ngoại lệ này xuất hiện khi lệnh gọi hàm ngoại không thể chuyển đổi một trong các đối số được truyền.
- exception ctypes.COMError(hresult, text, details)¶
Ngoại lệ này được đưa ra khi lệnh gọi phương thức COM không thành công.
- hresult¶
Giá trị số nguyên đại diện cho mã lỗi.
- text¶
Thông báo lỗi.
- details¶
5 bộ
(descr, source, helpfile, helpcontext, progid).descr là mô tả bằng văn bản. source là
ProgIDphụ thuộc vào ngôn ngữ dành cho lớp hoặc ứng dụng đã gây ra lỗi. helpfile là đường dẫn của file trợ giúp. helpcontext là mã định danh ngữ cảnh trợ giúp. progid làProgIDcủa giao diện xác định lỗi.
sẵn có: Windows
Added in version 3.14.