Spec-Zone.ru › Python 3.7

ctypes — Библиотека внешних функций для Python

ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции в DLL или динамических библиотеках. Её можно использовать для обертывания этих библиотек в чистом Python.

Учебник по ctypes

Примечание: примеры кода в этом учебнике используют doctest, чтобы убедиться, что они работают. Поскольку некоторые примеры кода ведут себя по-разному под Linux, Windows или Mac OS X, они содержат директивы doctest в комментариях.

Примечание: некоторые примеры кода ссылаются на тип c_int в библиотеке ctypes. На платформах, где sizeof(long) == sizeof(int) он является псевдонимом для c_long. Поэтому вы не должны удивляться, если будет напечатан c_long, а ожидается c_int — они, по сути, являются одним и тем же типом.

Загрузка динамических библиотек

ctypes экспортирует объекты cdll, а на Windows — windll и oledll для загрузки динамических библиотек.

Библиотеки загружаются путём обращения к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции с использованием стандартной cdecl конвенции вызова, в то время как windll загружает библиотеки, вызывающие функции с использованием stdcall конвенции вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают Windows HRESULT код ошибки. Код ошибки используется для автоматического повышения исключения OSError, если вызов функции завершается неудачно.

Изменено в версии 3.3: Ошибки Windows теперь вызывают WindowsError, который теперь является псевдонимом OSError.

Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека MS C, содержащая большинство стандартных функций C, и использует конвенцию вызова cdecl:

>>> from ctypes import *
>>> print(windll.kernel32)  
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)      
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt      
>>>

Windows автоматически добавляет обычный .dll расширение файла.

Примечание

Доступ к стандартной библиотеке C через cdll.msvcrt будет использовать устаревшую версию библиотеки, которая может быть несовместима с той, что используется Python. В случае возможности используйте родные функции Python, или же импортируйте и используйте модуль msvcrt.

В Linux требуется указать имя файла, включая расширение, для загрузки библиотеки, поэтому доступ к атрибутам для загрузки библиотек невозможен. Следует использовать метод LoadLibrary() загрузчиков dll, или загружать библиотеку, создавая экземпляр CDLL, вызывая конструктор:

>>> cdll.LoadLibrary("libc.so.6")  
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")       
>>> libc                           
<CDLL 'libc.so.6', handle ... at ...>
>>>

Доступ к функциям из загруженных dll

Функции доступны как атрибуты объектов 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
>>>

Обратите внимание, что системные dll win32, такие как kernel32 и user32 часто экспортируют ANSI и UNICODE версии функции. Версия UNICODE экспортируется с W добавленным к имени, а версия ANSI — с A.

Функция win32 GetModuleHandle, которая возвращает дескриптор модуля для данного имени модуля, имеет следующий прототип C, и используется макрос для экспорта одного из них как GetModuleHandle в зависимости от того, определён ли UNICODE или нет:

/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll не пытается выбрать одну из них магически, вам необходимо явно указать GetModuleHandleA или GetModuleHandleW, а затем вызвать ее с байтовыми или строковыми объектами соответственно.

Иногда dll экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, например "??2@YAPAXI@Z". В этом случае вы должны использовать getattr() для извлечения функции:

>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")  
<_FuncPtr object at 0x...>
>>>

В Windows некоторые dll экспортируют функции не по имени, а по порядковому номеру. К этим функциям можно получить доступ, индексируя объект dll порядковым номером:

>>> 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
>>>

Вызов функций

Эти функции можно вызывать как любые другие вызываемые объекты Python. В этом примере используется функция time(), которая возвращает системное время в секундах с начала эпохи Unix, и функция GetModuleHandleA(), которая возвращает дескриптор модуля win32.

В этом примере обе функции вызываются с указателем NULL (None должен использоваться как указатель NULL):

>>> print(libc.time(None))  
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))  
0x1d000000
>>>

ValueError генерируется при вызове функции с stdcall конвенцией вызова с cdecl конвенцией вызова, или наоборот:

>>> 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)
>>>

Чтобы определить правильную конвенцию вызова, необходимо обратиться к файлу заголовков C или документации для вызываемой функции.

В Windows, ctypes использует обработку структурированных исключений win32, чтобы предотвратить сбои из-за ошибок общей защиты при вызове функций с недопустимыми значениями аргументов:

>>> windll.kernel32.GetModuleHandleA(32)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>

Однако, существует достаточно способов аварийно завершить работу Python с ctypes, поэтому вы должны быть осторожны. Модуль faulthandler может быть полезен для отладки сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотек C).

None, целые числа, байтовые объекты и (строки unicode) — единственные базовые объекты Python, которые могут быть напрямую использованы в качестве параметров в этих вызовах функций. None передаётся как C-указатель NULL, байтовые объекты и строки передаются как указатели на блок памяти, содержащий их данные (char * или wchar_t *). Целые числа Python передаются как платформенные типы C int, их значение маскируется для соответствия типу C.

Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.

Основные типы данных

ctypes определяет ряд примитивных типов данных, совместимых с C:

Тип ctypes

Тип C

Тип Python

c_bool

_Bool

bool (1)

c_char

char

байтовый объект из 1 символа

c_wchar

wchar_t

строка из 1 символа

c_byte

char

целое число

c_ubyte

unsigned char

целое число

c_short

short

целое число

c_ushort

unsigned short

целое число

c_int

int

целое число

c_uint

unsigned int

целое число

c_long

long

целое число

c_ulong

unsigned long

целое число

c_longlong

__int64 или long long

целое число

c_ulonglong

unsigned __int64 или unsigned long long

целое число

c_size_t

size_t

целое число

c_ssize_t

ssize_t или Py_ssize_t

целое число

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char * (с завершением NUL)

объект bytes или None

c_wchar_p

wchar_t * (с завершением NUL)

строка или None

c_void_p

void *

целое число или None

  1. Конструктор принимает любой объект с истинным значением.

Все эти типы можно создать, вызвав их с необязательным инициализатором нужного типа и значения:

>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> 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 изменяет местоположение в памяти, на которое они указывают, а не содержимое блока памяти (конечно, потому что объекты Python bytes неизменяемы):

>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>

Однако следует быть осторожным, чтобы не передавать их функциям, ожидающим указателей на изменяемую память. Если вам нужны изменяемые блоки памяти, ctypes имеет функцию create_string_buffer(), которая создаёт их различными способами. Текущее содержимое блока памяти можно получить (или изменить) с помощью свойства raw; если вы хотите получить доступ к нему как к строке с завершением 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 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello")     # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

Функция create_string_buffer() заменяет функцию c_buffer() (которая всё ещё доступна как псевдоним), а также функцию c_string() из предыдущих релизов ctypes. Для создания изменяемого блока памяти, содержащего символы Юникода типа C wchar_t, используйте функцию create_unicode_buffer().

Вызов функций, продолжение

Обратите внимание, что printf выводит данные на реальный канал стандартного вывода, а не на sys.stdout, поэтому эти примеры будут работать только в командной строке, а не внутри IDLE или PythonWin:

>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%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
>>>

Как уже упоминалось, все типы Python, кроме целых чисел, строк и байтовых объектов, должны быть обернуты в соответствующий тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C:

>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>

Вызов функций с собственными пользовательскими типами данных

Вы также можете настроить преобразование аргументов ctypes, чтобы экземпляры ваших собственных классов можно было использовать в качестве аргументов функции. ctypes ищет атрибут _as_parameter_, использует его в качестве аргумента функции. Конечно, это должно быть целое число, строка или байты:

>>> class Bottles:
...     def __init__(self, number):
...         self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>

Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, вы можете определить свойство property, которое делает атрибут доступным по запросу.

Указание требуемых типов аргументов (прототипы функций)

Можно указать требуемые типы аргументов функций, экспортированных из DLL, установив атрибут argtypes.

argtypes должен быть последовательностью типов данных C (функция printf — вероятно, не лучший пример здесь, так как она принимает переменное количество и разные типы параметров в зависимости от строки формата. С другой стороны, это довольно удобно для экспериментов с этой функцией):

>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>

Указание формата защищает от несовместимых типов аргументов (точно так же, как прототип функции C) и пытается преобразовать аргументы в допустимые типы:

>>> printf(b"%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(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

Если вы определили собственные классы, которые передаёте в вызовы функций, вы должны реализовать метод класса from_param(), чтобы иметь возможность использовать их в последовательности argtypes. Метод класса from_param() получает переданный в вызов функции объект Python, должен выполнить проверку типа или другие необходимые действия, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_, или что угодно, что вы хотите передать как аргумент функции C в этом случае. Результат снова должен быть целым числом, строкой, байтами, экземпляром ctypes или объектом с атрибутом _as_parameter_.

Типы возвращаемых значений

По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут strchr объекта функции.

Вот более продвинутый пример. Он использует функцию strchr, которая принимает указатель на строку и символ и возвращает указатель на строку:

>>> 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
>>>

Если вы хотите избежать вызовов ord("x") выше, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из объекта Python bytes, содержащего единственный символ, в символ C:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>

Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращённым функцией C, а результат этого вызова будет использован как результат вашего вызова функции. Это полезно для проверки значений возврата ошибок и автоматического повышения исключения:

>>> 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 — это функция, которая вызовет Windows FormatMessage() API, чтобы получить строковое представление кода ошибки, и возвращает исключение. WinError принимает необязательный параметр кода ошибки. Если он не используется, он вызовет GetLastError() для его получения.

Обратите внимание, что механизм проверки ошибок значительно мощнее через атрибут errcheck; см. справочное руководство для получения подробной информации.

Передача указателей (или: передача параметров по ссылке)

Иногда функция C API ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее местоположение, или если данные слишком велики, чтобы передавать по значению. Это также известно как передача параметров по ссылке.

ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно получить с помощью функции pointer(), хотя pointer() выполняет гораздо больше работы, так как она создаёт реальный объект указателя, поэтому быстрее использовать byref(), если вам не нужен объект указателя в самом 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 Hello", b"%d %f %s",
...             byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>

Структуры и объединения

Структуры и объединения должны наследоваться от базовых классов Structure и Union, которые определены в модуле ctypes. Каждый подкласс должен определить атрибут _fields_. _fields_ должен быть списком 2-кортежей, содержащим имя поля и тип поля.

Тип поля должен быть типом ctypes, например, c_int, или любым другим производным типом ctypes: структура, объединение, массив, указатель.

Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также демонстрирует, как инициализировать структуру в конструкторе:

>>> 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>
TypeError: too many initializers
>>>

Однако вы можете создавать гораздо более сложные структуры. Структура сама может содержать другие структуры, используя структуру как тип поля.

Вот структура RECT, которая содержит два POINT с именами upperleft и lowerright:

>>> 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
>>>

Вложенные структуры также можно инициализировать в конструкторе несколькими способами:

>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))

Поля дескрипторов можно извлечь из класса, они полезны для отладки, так как могут предоставить полезную информацию:

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>

Предупреждение

ctypes не поддерживает передачу объединений или структур с битами в функции по значению. Хотя это может работать на 32-битном x86, библиотека не гарантирует, что это будет работать в общем случае. Объединения и структуры с битами всегда должны передаваться в функции по указателю.

Выравнивание и порядок байтов структуры/объединения

По умолчанию поля структуры и объединения выравниваются так же, как это делает компилятор C. Можно переопределить это поведение, указав атрибут класса _pack_ в определении подкласса. Он должен быть установленным положительным целым числом и определяет максимальное выравнивание для полей. Это то, что #pragma pack(n) также делает в MSVC.

ctypes использует собственный порядок байтов для структур и объединений. Для создания структур с другим порядком байтов можно использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion, и LittleEndianUnion. Эти классы не могут содержать поля указателя.

Битовые поля в структурах и объединениях

Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей, ширина бита задаётся как третий элемент в кортежах _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>
>>>

Массивы

Массивы являются последовательностями, содержащими фиксированное число экземпляров одного типа.

Рекомендуемый способ создания массивов — умножение типа данных на положительное целое число:

TenPointsArrayType = POINT * 10

Вот пример несколько искусственного типа данных, структуры, содержащей 4 POINT среди прочего:

>>> 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
>>>

Экземпляры создаются обычным способом, вызывая класс:

arr = TenPointsArrayType()
for pt in arr:
    print(pt.x, pt.y)

Вышеприведённый код выводит серию 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, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>

Указатели

Экземпляры указателей создаются, вызывая функцию pointer() на типе ctypes:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>

Экземпляры указателей имеют атрибут contents, который возвращает объект, на который указывает указатель, объект i выше:

>>> pi.contents
c_long(42)
>>>

Обратите внимание, что ctypes не имеет OOR (возврат оригинального объекта), каждый раз, когда вы получаете атрибут, он создаёт новый эквивалентный объект:

>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>

Присвоение другого экземпляра c_int атрибуту содержимого указателя заставит указатель указывать на местоположение памяти, где это хранится:

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>

Экземпляры указателей также могут быть индексированы целыми числами:

>>> pi[0]
99
>>>

Присвоение целочисленного индекса изменяет значение, на которое указывает указатель:

>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>

Также можно использовать индексы, отличные от 0, но вы должны знать, что делаете, так же, как и в C: вы можете получить доступ к произвольным местам памяти или изменить их. Обычно вы используете эту функцию только тогда, когда получаете указатель от функции C и знаете, что указатель действительно указывает на массив, а не на один элемент.

Под капотом функция pointer() делает больше, чем просто создаёт экземпляры указателей, она должна сначала создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:

>>> 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...>
>>>

Вызов типа указателя без аргумента создаёт 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 выполняет строгую проверку типов. Это означает, что если у вас POINTER(c_int) в списке параметров функции или как тип поля члена в определении структуры, принимаются только экземпляры ровно того же типа. Есть некоторые исключения из этого правила, где ctypes принимает другие объекты. Например, вместо типов указателей можно передать совместимые экземпляры массивов. Итак, для POINTER(c_int), ctypes принимает массив c_int:

>>> 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
>>>

Кроме того, если параметр функции явно объявлен как тип указателя (например, POINTER(c_int)) в argtypes, к функции можно передать объект указанного типа (c_int в данном случае). ctypes автоматически применит необходимое преобразование byref() в этом случае.

Чтобы установить поле типа POINTER в NULL, можно присвоить None:

>>> bar.values = None
>>>

Иногда у вас есть экземпляры несовместимых типов. В C вы можете привести один тип к другому. ctypes предоставляет функцию cast(), которую можно использовать аналогичным образом. Структура Bar , определённая выше, принимает указатели POINTER(c_int) или массивы c_int для поля 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() может использоваться для приведения экземпляра ctypes к указателю на другой тип данных ctypes. cast() принимает два параметра: объект ctypes, который может быть преобразован в указатель определённого типа, и тип указателя ctypes. Она возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:

>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>

Итак, cast() можно использовать для присвоения полю values структуры Bar:

>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>

Неполные типы

Неполные типы — это структуры, объединения или массивы, члены которых ещё не определены. В C они задаются с помощью предварительных объявлений, которые определяются позже:

struct cell; /* forward declaration */

struct cell {
    char *name;
    struct cell *next;
};

Прямой перевод на 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
>>>

потому что новый class cell недоступен в самом операторе класса. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после оператора класса:

>>> from ctypes import *
>>> class cell(Structure):
...     pass
...
>>> cell._fields_ = [("name", c_char_p),
...                  ("next", POINTER(cell))]
>>>

Давайте попробуем. Мы создадим два экземпляра 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, end=" ")
...     p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>

Функции обратного вызова

ctypes позволяет создавать вызываемые из C указатели на функции на основе Python-вызовов. Иногда их называют функциями обратного вызова.

Сначала необходимо создать класс для функции обратного вызова. Класс знает соглашение о вызове, тип возвращаемого значения и количество и типы аргументов, которые получит эта функция.

Функция-фабрика CFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове cdecl. В Windows функция-фабрика WINFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове stdcall.

Обе эти функции-фабрики вызываются с типом результата в качестве первого аргумента и типами ожидаемых аргументов функций обратного вызова в качестве оставшихся аргументов.

Здесь я приведу пример, который использует стандартную C-библиотеку qsort() функцию, которая используется для сортировки элементов с помощью функции обратного вызова. qsort() будет использоваться для сортировки массива целых чисел:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>

qsort() должен быть вызван с указателем на данные для сортировки, числом элементов в массиве данных, размером одного элемента и указателем на функцию сравнения — функцию обратного вызова. Затем функция обратного вызова будет вызвана с двумя указателями на элементы, и она должна вернуть отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.

Таким образом, наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала мы создаём type для функции обратного вызова:

>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>

Для начала вот простой обратный вызов, который демонстрирует значения, которые ему передаются:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>

Результат:

>>> 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
>>>

Теперь мы можем фактически сравнить два элемента и вернуть полезный результат:

>>> 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
>>>

Как мы легко можем проверить, наш массив теперь отсортирован:

>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>

Функции-фабрики могут использоваться как фабрики декораторов, поэтому мы можем написать:

>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... 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), 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
>>>

Примечание

Убедитесь, что вы сохраняете ссылки на CFUNCTYPE() объекты до тех пор, пока они используются кодом C. ctypes этого не делает, и если вы этого не сделаете, они могут быть собраны сборщиком мусора, что приведёт к сбою вашей программы при вызове обратного вызова.

Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне управления Python (например, кодом внешнего приложения, вызывающим обратный вызов), ctypes создаёт новый фиктивный поток Python при каждом вызове. Это поведение верно для большинства целей, но это означает, что значения, хранящиеся с помощью threading.local, не сохранятся между различными обратными вызовами, даже когда эти вызовы выполняются из одного и того же C-потока.

Доступ к значениям, экспортированным из dll

Некоторые общие библиотеки экспортируют не только функции, но и переменные. Примером в самой библиотеке Python является Py_OptimizeFlag, целое число, установленное в 0, 1 или 2 в зависимости от флага -O или -OO, заданного при запуске.

ctypes может получить доступ к таким значениям с помощью методов класса in_dll() типа. pythonapi — предопределённая переменная, дающая доступ к C-API Python:

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>

Если бы интерпретатор запускался с -O, пример напечатал бы c_long(1), или c_long(2), если бы был указан -OO.

Расширенный пример, который также демонстрирует использование указателей, обращается к указателю PyImport_FrozenModules, экспортированному Python.

Цитата из документации по этому значению:

Этот указатель инициализируется для указания на массив записей struct _frozen, завершённых записью, члены которой все NULL или ноль. При импорте замороженного модуля он ищется в этой таблице. Код третьих лиц может обмануть это, чтобы предоставить динамически созданный набор замороженных модулей.

Таким образом, манипулирование этим указателем может оказаться полезным. Чтобы ограничить размер примера, мы покажем только то, как эта таблица может быть прочитана с помощью ctypes:

>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("code", POINTER(c_ubyte)),
...                 ("size", c_int)]
...
>>>

Мы определили тип данных struct _frozen, поэтому мы можем получить указатель на таблицу:

>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>

Поскольку table является указателем на массив записей struct_frozen, мы можем перебрать его, но мы должны убедиться, что наш цикл завершается, потому что у указателей нет размера. Рано или поздно он, вероятно, вызовет ошибку доступа или что-то подобное, поэтому лучше прервать цикл, когда мы достигнем записи NULL.

>>> for item in table:
...     if item.name is None:
...         break
...     print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>

Тот факт, что стандартный Python имеет замороженный модуль и замороченный пакет (указанный отрицательным членом size ) малоизвестен, он используется только для тестирования. Попробуйте использовать import __hello__, например.

Неожиданности

В ctypes есть некоторые особенности, где вы можете ожидать чего-то иного, чем происходит на самом деле.

Рассмотрим следующий пример:

>>> 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
>>>

Хм. Мы, безусловно, ожидали, что последняя строка выведет 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 — это объекты, которые по-прежнему используют внутренний буфер объекта rc выше. Поэтому выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. В свою очередь, это изменяет содержимое temp1. Таким образом, последнее присваивание rc.b = temp1, не имеет ожидаемого эффекта.

Помните, что извлечение подобъектов из структур, объединений и массивов не копирует подобъект, а возвращает объект-обёртку, обращаясь к внутреннему буферу корневого объекта.

Ещё один пример, который может вести себя иначе, чем ожидалось, это:

>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>

Примечание

Объекты, созданные из c_char_p, могут иметь своё значение, установленное только на байты или целые числа.

Почему он выводит False? Экземпляры ctypes — это объекты, содержащие блок памяти плюс некоторые дескрипторы, которые обращаются к содержимому памяти. Хранение объекта Python в блоке памяти не хранит сам объект, вместо этого хранится contents объекта. Обращение к содержимому снова создаёт новый объект Python каждый раз!

Типы данных с переменным размером

ctypes обеспечивает некоторую поддержку массивов и структур с переменным размером.

Функция resize() может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и требуемый размер в байтах как второй аргумент. Блок памяти не может быть меньше естественного блока памяти, указанного типом объекта, если это попытаться сделать, то возбуждается ValueError:

>>> 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
>>>

Это хорошо и замечательно, но как получить доступ к дополнительным элементам в этом массиве? Поскольку тип по-прежнему знает только о 4 элементах, мы получаем ошибки при доступе к другим элементам:

>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
    ...
IndexError: invalid index
>>>

Другой способ использования типов данных с переменным размером с помощью ctypes заключается в использовании динамической природы Python и (пере)определении типа данных после того, как требуемый размер уже известен, в каждом конкретном случае.

Справочник по ctypes

Поиск общих библиотек

При программировании на языке с компиляцией, общие библиотеки загружаются при компиляции/связывании программы, и когда программа выполняется.

Цель функции find_library() — найти библиотеку аналогичным образом, как это делает компилятор или загрузчик времени выполнения (на платформах с несколькими версиями общей библиотеки должна загружаться самая последняя), в то время как загрузчики библиотек ctypes действуют так, как если бы программа выполнялась и непосредственно вызывали загрузчик времени выполнения.

Модуль ctypes.util предоставляет функцию, которая может помочь определить библиотеку для загрузки.

ctypes.util.find_library(name)

Попытаться найти библиотеку и вернуть путь к ней. name — имя библиотеки без каких-либо префиксов, таких как lib, суффиксов, таких как .so, .dylib или номера версии (этот формат используется для параметра позикс-линкера -l). Если библиотека не найдена, возвращает None.

Точная функциональность зависит от системы.

В Linux, find_library() пытается запустить внешние программы (/sbin/ldconfig, gcc, objdump и ld), чтобы найти файл библиотеки. Она возвращает имя файла библиотеки.

Изменено в версии 3.6: В Linux, при поиске библиотек используется значение переменной окружения LD_LIBRARY_PATH, если библиотека не может быть найдена другими способами.

Вот некоторые примеры:

>>> 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() пытается использовать несколько предопределённых схем именования и путей для поиска библиотеки и возвращает полный путь, если поиск успешен:

>>> 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() ищет библиотеку по пути поиска системы и возвращает полный путь. Однако, так как нет предопределённой схемы именования, вызов find_library("c") завершится неудачей и вернёт None.

При оборачивании динамической библиотеки с помощью ctypes, может быть лучше определить имя динамической библиотеки на этапе разработки и встроить его в модуль-обёртку, вместо использования find_library() для поиска библиотеки во время выполнения.

Загрузка динамических библиотек

Существует несколько способов загрузки динамических библиотек в процесс Python. Один из способов — создание экземпляра одного из следующих классов:

class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)

Экземпляры этого класса представляют загруженные динамические библиотеки. Функции в этих библиотеках используют стандартную C-конвенцию вызовов и предполагается, что они возвращают int.

class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)

Только Windows: Экземпляры этого класса представляют загруженные динамические библиотеки, функции в этих библиотеках используют stdcall конвенцию вызова и предполагается, что они возвращают специфичный для Windows код HRESULT. Значения HRESULT содержат информацию, указывающую, завершился ли вызов функции успешно или нет, вместе с дополнительным кодом ошибки. Если возвращаемое значение указывает на ошибку, автоматически возбуждается исключение OSError.

Изменено в версии 3.3: Раньше возбуждалось исключение WindowsError.

class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)

Только Windows: Экземпляры этого класса представляют загруженные динамические библиотеки, функции в этих библиотеках используют stdcall конвенцию вызова и предполагается, что они возвращают int по умолчанию.

Только в Windows CE используется стандартная конвенция вызова; для удобства WinDLL и OleDLL используют стандартную конвенцию вызова на этой платформе.

Блокировка глобального интерпретатора Python (GIL) снимается перед вызовом любой функции, экспортированной этими библиотеками, и восстанавливается после него.

class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)

Экземпляры этого класса ведут себя как экземпляры CDLL, за исключением того, что GIL Python не снимается во время вызова функции, а после выполнения функции проверяется флаг ошибки Python. Если флаг ошибки установлен, возбуждается исключение Python.

Таким образом, это полезно только для прямого вызова функций API C Python.

Все эти классы можно создать, вызвав их с по крайней мере одним аргументом — путём к динамической библиотеке. Если у вас уже есть дескриптор уже загруженной динамической библиотеки, его можно передать в качестве параметра с именем handle, в противном случае используются функции dlopen или LoadLibrary для загрузки библиотеки в процесс и получения дескриптора.

Параметр mode может быть использован для указания способа загрузки библиотеки. Подробности см. в справке по команде dlopen(3). В Windows параметр mode игнорируется. На системах POSIX всегда добавляется RTLD_NOW и не настраивается.

Параметр use_errno, установленный в значение true, включает механизм ctypes, позволяющий безопасно получить системное значение ошибки errno. ctypes сохраняет локальную для потока копию системной переменной errno; если вы вызываете внешние функции, созданные с помощью use_errno=True, то значение errno до вызова функции меняется на копию ctypes, и то же самое происходит сразу после вызова функции.

Функция ctypes.get_errno() возвращает значение копии ctypes, а функция ctypes.set_errno() изменяет копию ctypes на новое значение и возвращает прежнее значение.

Параметр use_last_error, установленный в значение true, включает тот же механизм для кода ошибки Windows, который обрабатывается функциями GetLastError() и SetLastError() функций API Windows; ctypes.get_last_error() и ctypes.set_last_error() используются для запроса и изменения копии ctypes кода ошибки Windows.

ctypes.RTLD_GLOBAL

Флаг для использования в качестве параметра mode. На платформах, где этот флаг недоступен, он определяется как целое число ноль.

ctypes.RTLD_LOCAL

Флаг для использования в качестве параметра mode. На платформах, где этот флаг недоступен, он эквивалентен RTLD_GLOBAL.

ctypes.DEFAULT_MODE

Режим по умолчанию, используемый для загрузки динамических библиотек. В OSX 10.3 это RTLD_GLOBAL, в противном случае это то же самое, что и RTLD_LOCAL.

У экземпляров этих классов нет общедоступных методов. К экспортированным функциям динамической библиотеки можно получить доступ как к атрибутам или по индексу. Обратите внимание, что доступ к функции через атрибут кэширует результат, и повторный доступ возвращает один и тот же объект каждый раз. С другой стороны, доступ через индекс возвращает новый объект каждый раз:

>>> from ctypes import CDLL
>>> libc = CDLL("libc.so.6")  # On Linux
>>> libc.time == libc.time
True
>>> libc['time'] == libc['time']
False

Доступны следующие общедоступные атрибуты, их имя начинается с нижнего подчеркивания, чтобы не создавать конфликтов с именами экспортированных функций:

PyDLL._handle

Системный дескриптор, используемый для доступа к библиотеке.

PyDLL._name

Имя библиотеки, переданное в конструктор.

Динамические библиотеки также можно загружать, используя готовые объекты, которые являются экземплярами класса LibraryLoader, вызывая метод LoadLibrary() или получая библиотеку как атрибут экземпляра загрузчика.

class ctypes.LibraryLoader(dlltype)

Класс для загрузки динамических библиотек. dlltype должен быть одним из типов CDLL, PyDLL, WinDLL или OleDLL.

__getattr__() имеет специальное поведение: он позволяет загружать динамическую библиотеку, обращаясь к ней как к атрибуту экземпляра загрузчика. Результат кэшируется, поэтому повторный доступ к атрибуту каждый раз возвращает одну и ту же библиотеку.

LoadLibrary(name)

Загружает динамическую библиотеку в процесс и возвращает её. Этот метод всегда возвращает новый экземпляр библиотеки.

Доступны следующие готовые загрузчики библиотек:

ctypes.cdll

Создаёт экземпляры CDLL.

ctypes.windll

Только Windows: Создаёт экземпляры WinDLL.

ctypes.oledll

Только Windows: Создаёт экземпляры OleDLL.

ctypes.pydll

Создаёт экземпляры PyDLL.

Для прямого доступа к API C Python доступен готовый объект динамической библиотеки Python:

ctypes.pythonapi

Экземпляр PyDLL, который предоставляет функции Python C API в качестве атрибутов. Обратите внимание, что все эти функции предполагается, что возвращают C int, что, конечно, не всегда соответствует действительности, поэтому вам необходимо назначить правильный restype атрибут для использования этих функций.

Внешние функции

Как было объяснено в предыдущем разделе, к внешним функциям можно получить доступ в качестве атрибутов загруженных разделяемых библиотек. Объекты функций, созданные таким образом, по умолчанию принимают любое количество аргументов, принимают любые экземпляры данных ctypes в качестве аргументов и возвращают тип результата по умолчанию, заданный загрузчиком библиотеки. Они являются экземплярами частного класса:

class ctypes._FuncPtr

Базовый класс для вызываемых внешних функций C.

Экземпляры внешних функций также являются совместимыми с C типами данных; они представляют указатели на функции C.

Это поведение можно настроить, назначив специальные атрибуты объекту внешней функции.

restype

Назначьте тип ctypes для указания типа возвращаемого значения внешней функции. Используйте None для void, функции, не возвращающей ничего.

Можно назначить вызываемый объект Python, который не является типом ctypes, в этом случае предполагается, что функция возвращает целое число C int, а вызываемый объект будет вызван с этим целым числом, что позволит выполнить дальнейшую обработку или проверку ошибок. Использование этого устарело; для более гибкой последующей обработки или проверки ошибок используйте тип данных ctypes в качестве restype и назначьте вызываемый объект атрибуту errcheck.

argtypes

Назначьте кортеж типов ctypes, чтобы указать типы аргументов, которые принимает функция. Функции, использующие stdcall соглашение о вызове, могут вызываться только с таким же количеством аргументов, как длина этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные не указанные аргументы.

При вызове внешней функции каждый фактический аргумент передается методу from_param() класса для элементов кортежа argtypes. Этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элемент c_char_p в кортеже argtypes преобразует строку, переданную в качестве аргумента, в объект bytes, используя правила преобразования ctypes.

Новое: теперь можно помещать в argtypes элементы, которые не являются типами ctypes, но каждый элемент должен иметь метод from_param() , который возвращает значение, пригодное в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определять адаптеры, которые могут адаптировать пользовательские объекты в качестве параметров функции.

errcheck

Назначьте этой атрибуту функцию Python или другой вызываемый объект. Вызываемый объект будет вызван с тремя или более аргументами:

callable(result, func, arguments)

result — это то, что возвращает внешняя функция, как указано атрибутом restype.

func — сам объект внешней функции; это позволяет повторно использовать тот же вызываемый объект для проверки или обработки результатов нескольких функций.

arguments — кортеж, содержащий параметры, изначально переданные в вызов функции; это позволяет специализировать поведение в зависимости от используемых аргументов.

Объект, который возвращает эта функция, будет возвращен из вызова внешней функции, но он также может проверять значение результата и генерировать исключение, если вызов внешней функции завершился неудачей.

exception ctypes.ArgumentError

Это исключение генерируется, когда вызов внешней функции не может преобразовать один из переданных аргументов.

Прототипы функций

Внешние функции также можно создавать, создавая экземпляры прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове) без определения реализации. Функции-фабрики должны вызываться с требуемым типом возвращаемого значения и типами аргументов функции и могут использоваться в качестве фабрик декораторов, а следовательно, применяться к функциям через синтаксис @wrapper. См. Функции обратного вызова для примеров.

ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Возвращаемый прототип функции создает функции, которые используют стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если use_errno установлено в true, частная копия ctypes переменной системы errno обменивается со значением реальной переменной errno до и после вызова; use_last_error делает то же самое для кода ошибки Windows.

ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Только для Windows: Возвращаемый прототип функции создает функции, которые используют stdcall соглашение о вызове, за исключением Windows CE, где WINFUNCTYPE() такое же, как CFUNCTYPE(). Функция освободит GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.

ctypes.PYFUNCTYPE(restype, *argtypes)

Возвращаемый прототип функции создает функции, которые используют соглашение о вызове Python. Функция не освободит GIL во время вызова.

Прототипы функций, созданные этими функциями-фабриками, могут быть созданы различными способами в зависимости от типа и количества параметров в вызове:

prototype(address)

Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.

prototype(callable)

Создает вызываемую функцию C (функцию обратного вызова) из Python callable.

prototype(func_spec[, paramflags])

Возвращает внешнюю функцию, экспортированную разделяемой библиотекой. func_spec должен быть 2-кортежем (name_or_ordinal, library). Первый элемент — имя экспортированной функции в виде строки или порядковый номер экспортированной функции как маленькое целое число. Второй элемент — экземпляр разделяемой библиотеки.

prototype(vtbl_index, name[, paramflags[, iid]])

Возвращает внешнюю функцию, которая будет вызывать метод COM. vtbl_index — индекс в таблице виртуальных функций, малое неотрицательное целое число. name — имя метода COM. iid — необязательный указатель на идентификатор интерфейса, который используется в расширенном сообщении об ошибках.

Методы COM используют специальное соглашение о вызове: они требуют указателя на COM интерфейс в качестве первого аргумента, кроме тех параметров, которые указаны в кортеже argtypes.

Необязательный параметр paramflags создает обертки внешних функций с гораздо большей функциональностью, чем описанные выше функции.

paramflags должен быть кортежем той же длины, что и argtypes.

Каждый элемент этого кортежа содержит дополнительную информацию о параметре; это должен быть кортеж, содержащий один, два или три элемента.

Первый элемент — целое число, содержащее комбинацию флагов направления для параметра:

1

Указывает входной параметр функции.

2

Параметр вывода. Внешняя функция заполняет значение.

4

Входной параметр, который по умолчанию равен целому числу ноль.

Необязательный второй элемент — имя параметра в виде строки. Если это указано, внешняя функция может вызываться с именованными параметрами.

Необязательный третий элемент — значение параметра по умолчанию.

Этот пример демонстрирует, как обернуть функцию Windows MessageBoxW так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление C из файла заголовков Windows такое:

WINUSERAPI int WINAPI
MessageBoxW(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType);

Вот обертка с использованием ctypes:

>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)

Внешнюю функцию MessageBox теперь можно вызвать следующим образом:

>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")

Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect получает размеры указанного окна, копируя их в структуру RECT , которую должен предоставить вызывающий код. Вот объявление 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)
>>>

Функции с выходными параметрами автоматически возвращают значение выходного параметра, если их один, или кортеж, содержащий значения выходных параметров, если их несколько, поэтому функция GetWindowRect теперь возвращает экземпляр RECT при вызове.

Выходные параметры можно комбинировать с протоколом errcheck для дальнейшей обработки выходных данных и проверки ошибок. Функция win32 GetWindowRect api возвращает BOOL для сигнализации об успехе или неудаче, поэтому эта функция могла бы выполнить проверку ошибок и генерировать исключение, когда вызов api завершился неудачей:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     return args
...
>>> GetWindowRect.errcheck = errcheck
>>>

Если функция errcheck возвращает полученный кортеж аргументов без изменений, ctypes продолжает стандартную обработку выходных параметров. Если вы хотите вернуть кортеж координат окна вместо экземпляра RECT , вы можете извлечь поля в функции и вернуть их вместо этого; стандартная обработка больше не будет выполняться:

>>> 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)

Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.

ctypes.alignment(obj_or_type)

Возвращает требования к выравниванию для типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.

ctypes.byref(obj[, offset])

Возвращает легкий указатель на obj, который должен быть экземпляром типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему значению указателя.

byref(obj, offset) соответствует следующему коду C:

(((char *)&obj) + offset)

Возвращаемый объект может использоваться только в качестве параметра вызова внешней функции. Он ведет себя аналогично pointer(obj), но создание происходит намного быстрее.

ctypes.cast(obj, type)

Эта функция похожа на оператор приведения типов в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj — объектом, который может интерпретироваться как указатель.

ctypes.create_string_buffer(init_or_size, size=None)

Эта функция создаёт изменяемый буфер символов. Возвращаемый объект — массив ctypes типа c_char.

init_or_size должно быть целым числом, определяющим размер массива, или объектом типа bytes, который будет использован для инициализации элементов массива.

Если в качестве первого аргумента указан объект типа bytes, буфер увеличивается на один элемент по сравнению с его длиной, чтобы последний элемент массива был символом NUL. В качестве второго аргумента может быть передано целое число, позволяющее указать размер массива, если длина объекта bytes не должна использоваться.

ctypes.create_unicode_buffer(init_or_size, size=None)

Эта функция создаёт изменяемый буфер символов Юникод. Возвращаемый объект — массив ctypes типа c_wchar.

init_or_size должно быть целым числом, определяющим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.

Если в качестве первого аргумента указана строка, буфер увеличивается на один элемент по сравнению с её длиной, чтобы последний элемент массива был символом NUL. В качестве второго аргумента может быть передано целое число, позволяющее указать размер массива, если длина строки не должна использоваться.

ctypes.DllCanUnloadNow()

Только Windows: Эта функция является обработчиком, который позволяет реализовать COM-серверы в процессе с помощью ctypes. Она вызывается функцией DllCanUnloadNow, экспортируемой dll расширения _ctypes.

ctypes.DllGetClassObject()

Только Windows: Эта функция является обработчиком, который позволяет реализовать COM-серверы в процессе с помощью ctypes. Она вызывается функцией DllGetClassObject, экспортируемой dll расширения _ctypes.

ctypes.util.find_library(name)

Попытка найти библиотеку и вернуть путь к ней. name — имя библиотеки без префиксов, таких как lib, суффиксов, таких как .so, .dylib, или номера версии (это форма, используемая для опции линковщика posix -l). Если библиотека не найдена, возвращается None.

Точная функциональность зависит от системы.

ctypes.util.find_msvcrt()

Только Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки определить не удаётся, возвращается None.

Если необходимо освободить память, например, выделенную модулем расширения с помощью вызова функции free(void *), важно использовать функцию из той же библиотеки, которая выделила память.

ctypes.FormatError([code])

Только Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows GetLastError.

ctypes.GetLastError()

Только Windows: Возвращает последний код ошибки, установленный Windows в потоке вызова. Эта функция напрямую вызывает функцию Windows GetLastError(), она не возвращает копию кода ошибки, хранимую в ctypes.

ctypes.get_errno()

Возвращает текущее значение копии, хранимой в ctypes, системной переменной errno в потоке вызова.

ctypes.get_last_error()

Только Windows: возвращает текущее значение копии, хранимой в ctypes, системной переменной LastError в потоке вызова.

ctypes.memmove(dst, src, count)

Аналогична стандартной функции C memmove: копирует count байт из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые могут быть преобразованы в указатели.

ctypes.memset(dst, c, count)

Аналогична стандартной функции C memset: заполняет блок памяти по адресу dst значением c на count байт. dst должен быть целым числом, определяющим адрес, или экземпляром ctypes.

ctypes.POINTER(type)

Эта функция-фабрика создаёт и возвращает новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри, поэтому повторный вызов этой функции не затратен. type должен быть типом ctypes.

ctypes.pointer(obj)

Эта функция создаёт новый экземпляр указателя, указывающий на obj. Возвращаемый объект имеет тип POINTER(type(obj)).

Примечание: Если вам нужно только передать указатель на объект во внешнюю функцию, используйте byref(obj), что намного быстрее.

ctypes.resize(obj, size)

Эта функция изменяет размер внутреннего буфера памяти obj, который должен быть экземпляром типа ctypes. Невозможно сделать буфер меньше, чем родной размер типа объекта, указанный sizeof(type(obj)), но можно увеличить буфер.

ctypes.set_errno(value)

Устанавливает текущее значение копии, хранимой в ctypes, системной переменной errno в потоке вызова на value и возвращает предыдущее значение.

ctypes.set_last_error(value)

Только Windows: устанавливает текущее значение копии, хранимой в ctypes, системной переменной LastError в потоке вызова на value и возвращает предыдущее значение.

ctypes.sizeof(obj_or_type)

Возвращает размер буфера памяти типа ctypes или экземпляра в байтах. Делает то же самое, что и оператор C sizeof.

ctypes.string_at(address, size=-1)

Эта функция возвращает строку C, начинающуюся с адреса памяти address, в виде объекта bytes. Если задан размер, он используется в качестве размера, иначе строка предполагается завершённой нулём.

ctypes.WinError(code=None, descr=None)

Только Windows: эта функция, вероятно, имеет наихудшее название в ctypes. Она создаёт экземпляр OSError. Если code не указан, вызывается GetLastError для определения кода ошибки. Если descr не указан, вызывается FormatError() для получения текстового описания ошибки.

Изменено в версии 3.3: Создавался экземпляр WindowsError.

ctypes.wstring_at(address, size=-1)

Эта функция возвращает строку символов Юникод, начинающуюся с адреса памяти address, в виде строки. Если size задан, он используется как число символов строки, иначе строка предполагается завершённой нулём.

Типы данных

class ctypes._CData

Этот непубличный класс является общим базовым классом всех типов данных ctypes. В частности, все экземпляры типов ctypes содержат блок памяти, хранящий данные, совместимые с C; адрес блока памяти возвращается функцией-помощником addressof(). Другая переменная экземпляра представлена как _objects; она содержит другие объекты Python, которые необходимо поддерживать в живом состоянии, если блок памяти содержит указатели.

Общие методы типов данных ctypes, это все методы класса (точнее, методы метакласса):

from_buffer(source[, offset])

Этот метод возвращает экземпляр ctypes, который использует буфер объекта source. Объект source должен поддерживать интерфейс записи в буфер. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию равно нулю. Если буфер источника недостаточно велик, возникает ValueError.

from_buffer_copy(source[, offset])

Этот метод создает экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию равно нулю. Если буфер источника недостаточно велик, возникает ValueError.

from_address(address)

Этот метод возвращает экземпляр типа ctypes, используя память, указанную адресом address, который должен быть целым числом.

from_param(obj)

Этот метод адаптирует obj к типу ctypes. Он вызывается с фактическим объектом, используемым в вызове внешней функции, когда тип присутствует в кортеже argtypes внешней функции; он должен вернуть объект, который может быть использован в качестве параметра вызова функции.

Все типы данных ctypes имеют реализацию по умолчанию этого метода класса, который обычно возвращает obj, если это экземпляр типа. Некоторые типы также принимают другие объекты.

in_dll(library, name)

Этот метод возвращает экземпляр типа ctypes, экспортируемый из библиотеки динамической загрузки. name — имя символа, который экспортирует данные, library — загруженная библиотека динамической загрузки.

Общие переменные экземпляров типов данных ctypes:

_b_base_

Иногда экземпляры типов данных ctypes не владеют блоком памяти, который они содержат, вместо этого они используют часть блока памяти базового объекта. Член только для чтения _b_base_ — это корневой объект ctypes, который владеет блоком памяти.

_b_needsfree_

Эта переменная только для чтения равна true, когда экземпляр данных ctypes сам выделил блок памяти, и false в противном случае.

_objects

Этот член является либо None, либо словарем, содержащим объекты Python, которые необходимо поддерживать в живом состоянии, чтобы содержимое блока памяти оставалось валидным. Этот объект предоставляется только для отладки; никогда не изменяйте содержимое этого словаря.

Основные типы данных

class ctypes._SimpleCData

Этот непубличный класс является базовым классом всех основных типов данных ctypes. Он упомянут здесь, потому что содержит общие атрибуты основных типов данных ctypes. _SimpleCData является подклассом _CData, поэтому он наследует их методы и атрибуты. Типы данных ctypes, которые не являются и не содержат указателей, теперь могут быть сериализованы.

Экземпляры имеют один атрибут:

value

Этот атрибут содержит фактическое значение экземпляра. Для целочисленных и указательных типов это целое число, для символьных типов это одиночный объект bytes или строка, для указателей на символы это объект Python bytes или строка.

При получении атрибута value из экземпляра ctypes обычно каждый раз возвращается новый объект. ctypes не реализует возвращение оригинального объекта, всегда создается новый объект. То же самое относится ко всем другим экземплярам объектов ctypes.

Основные типы данных, возвращаемые в качестве результатов вызова внешних функций или, например, при получении членов структур или элементов массивов, прозрачно преобразуются в собственные типы Python. Другими словами, если внешняя функция имеет restype типа c_char_p, вы всегда получите объект Python bytes, а не экземпляр c_char_p.

Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса из вызова функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.

Вот основные типы данных ctypes:

class ctypes.c_byte

Представляет тип данных C signed char, и интерпретирует значение как небольшое целое число. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется.

class ctypes.c_char

Представляет тип данных C char, и интерпретирует значение как один символ. Конструктор принимает необязательную строку инициализации, длина строки должна быть ровно одним символом.

class ctypes.c_char_p

Представляет тип данных C char * при указании на строку с нулевым окончанием. Для общего указателя на символ, который также может указывать на двоичные данные, необходимо использовать POINTER(c_char). Конструктор принимает целочисленный адрес или объект bytes.

class ctypes.c_double

Представляет тип данных C double. Конструктор принимает необязательную плавающую точку инициализации.

class ctypes.c_longdouble

Представляет тип данных C long double. Конструктор принимает необязательную инициализацию с плавающей точкой. На платформах, где sizeof(long double) == sizeof(double), он является псевдонимом для c_double.

class ctypes.c_float

Представляет тип данных C float. Конструктор принимает необязательную инициализацию с плавающей точкой.

class ctypes.c_int

Представляет тип данных C signed int. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long), он является псевдонимом для c_long.

class ctypes.c_int8

Представляет 8-битный тип данных C signed int. Обычно является псевдонимом для c_byte.

class ctypes.c_int16

Представляет 16-битный тип данных C signed int. Обычно является псевдонимом для c_short.

class ctypes.c_int32

Представляет 32-битный тип данных C signed int. Обычно является псевдонимом для c_int.

class ctypes.c_int64

Представляет 64-битный тип данных C signed int. Обычно является псевдонимом для c_longlong.

class ctypes.c_long

Представляет тип данных C signed long. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется.

class ctypes.c_longlong

Представляет тип данных C signed long long. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется.

class ctypes.c_short

Представляет тип данных C signed short. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется.

class ctypes.c_size_t

Представляет тип данных C size_t.

class ctypes.c_ssize_t

Представляет тип данных C ssize_t.

Добавлена в версии 3.2.

class ctypes.c_ubyte

Представляет тип данных C unsigned char, он интерпретирует значение как небольшое целое число. Конструктор принимает необязательное целое число инициализации; проверка переполнения не выполняется.

class ctypes.c_uint

Представляет тип данных C unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long) это псевдоним для c_ulong.

class ctypes.c_uint8

Представляет 8-битный тип данных C unsigned int. Обычно является псевдонимом для c_ubyte.

class ctypes.c_uint16

Представляет 16-битный тип данных C unsigned int. Обычно является псевдонимом для c_ushort.

class ctypes.c_uint32

Представляет 32-битный тип данных C unsigned int. Обычно является псевдонимом для c_uint.

class ctypes.c_uint64

Представляет 64-битный тип данных C unsigned int. Обычно является псевдонимом для c_ulonglong.

class ctypes.c_ulong

Представляет тип данных C unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ulonglong

Представляет тип данных C unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ushort

Представляет тип данных C unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_void_p

Представляет тип C void *. Значение представлено как целое число. Конструктор принимает необязательный целочисленный инициализатор.

class ctypes.c_wchar

Представляет тип данных C wchar_t, и интерпретирует значение как строку Unicode с одним символом. Конструктор принимает необязательный строковый инициализатор, длина строки должна составлять ровно один символ.

class ctypes.c_wchar_p

Представляет тип данных C wchar_t *, который должен быть указателем на завершаемую нулём строку широких символов. Конструктор принимает целочисленный адрес или строку.

class ctypes.c_bool

Представляет тип данных C bool (точнее, _Bool из C99). Его значение может быть True или False, и конструктор принимает любой объект, имеющий логическое значение.

class ctypes.HRESULT

Только Windows: Представляет значение HRESULT, которое содержит информацию об успехе или ошибке для вызова функции или метода.

class ctypes.py_object

Представляет тип данных C PyObject *. Вызов без аргументов создаёт указатель на NULL PyObject *.

Модуль ctypes.wintypes предоставляет ещё несколько других типов данных, специфичных для Windows, например, HWND, WPARAM, или DWORD. Также определены некоторые полезные структуры, например, MSG или RECT.

Структурированные типы данных

class ctypes.Union(*args, **kw)

Абстрактный базовый класс для объединений в родном порядке байтов.

class ctypes.BigEndianStructure(*args, **kw)

Абстрактный базовый класс для структур в порядке байтов big endian.

class ctypes.LittleEndianStructure(*args, **kw)

Абстрактный базовый класс для структур в порядке байтов little endian.

Структуры с неродным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.

class ctypes.Structure(*args, **kw)

Абстрактный базовый класс для структур в родном порядке байтов.

Конкретные типы структур и объединений должны создаваться путём наследования от одного из этих типов и, по крайней мере, должны определять переменную класса _fields_. ctypes создаст дескрипторы, которые позволяют читать и записывать поля путём прямого доступа к атрибутам. Это

_fields_

Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент — имя поля, второй элемент определяет тип поля; это может быть любой тип данных ctypes.

Для полей целого типа, таких как c_int, можно указать необязательный третий элемент. Он должен быть небольшим положительным целым числом, определяющим разрядность поля.

Имена полей должны быть уникальными в рамках одной структуры или объединения. Это не проверяется; при повторении имён можно получить доступ только к одному полю.

Можно определить переменную класса _fields_ после оператора класса, который определяет подкласс структуры; это позволяет создавать типы данных, которые напрямую или косвенно ссылаются на себя:

class List(Structure):
    pass
List._fields_ = [("pnext", POINTER(List)),
                 ...
                ]

Однако переменная класса _fields_ должна быть определена до первого использования типа (создания экземпляра, вызова sizeof() и т. д.). Позднее присвоение значений переменной класса _fields_ вызовет исключение AttributeError.

Можно определить подподклассы типов структур; они наследуют поля базового класса плюс поля, определённые в подподклассе (если таковые имеются) в переменной _fields_.

_pack_

Необязательное малое целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре. _pack_ должно быть уже определено, когда присваивается _fields_, в противном случае это не повлияет.

_anonymous_

Необязательная последовательность, которая перечисляет имена безымянных (анонимных) полей. _anonymous_ должно быть уже определено, когда присваивается _fields_, в противном случае это не повлияет.

Поля, перечисленные в этой переменной, должны быть полями типа структуры или объединения. ctypes создаст дескрипторы в типе структуры, которые позволят напрямую обращаться к вложенным полям без необходимости создавать экземпляр структуры или объединения.

Вот пример типа (Windows):

class _U(Union):
    _fields_ = [("lptdesc", POINTER(TYPEDESC)),
                ("lpadesc", POINTER(ARRAYDESC)),
                ("hreftype", HREFTYPE)]

class TYPEDESC(Structure):
    _anonymous_ = ("u",)
    _fields_ = [("u", _U),
                ("vt", VARTYPE)]

Структура TYPEDESC описывает тип данных COM, поле vt указывает, какое из полей объединения является допустимым. Так как поле u определено как анонимное поле, теперь можно получить доступ к членам напрямую из экземпляра TYPEDESC. td.lptdesc и td.u.lptdesc эквивалентны, но первый вариант быстрее, поскольку не требует создания временного экземпляра объединения:

td = TYPEDESC()
td.vt = VT_PTR
td.lptdesc = POINTER(some_type)
td.u.lptdesc = POINTER(some_type)

Можно определять подподклассы структур; они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная _fields_, поля, указанные в ней, добавляются к полям базового класса.

Конструкторы структур и объединений принимают как позиционные, так и именованные аргументы. Позиционные аргументы используются для инициализации полей членов в том же порядке, в котором они появляются в _fields_. Именованные аргументы в конструкторе интерпретируются как присваивания атрибутов, поэтому они инициализируют _fields_ с тем же именем или создают новые атрибуты для имён, отсутствующих в _fields_.

Массивы и указатели

class ctypes.Array(*args)

Абстрактный базовый класс для массивов.

Рекомендуемый способ создания конкретных типов массивов — умножение любого типа данных ctypes на положительное целое число. В качестве альтернативы вы можете создать подкласс этого типа и определить переменные класса _length_ и _type_. Элементы массива можно читать и записывать с помощью стандартных индексов и срезов; при чтении срезов полученный объект не является объектом Array.

_length_

Положительное целое число, определяющее количество элементов в массиве. Индексы вне диапазона приводят к исключению IndexError. Возвращается функцией len().

_type_

Указывает тип каждого элемента в массиве.

Конструкторы подклассов массивов принимают позиционные аргументы, используемые для инициализации элементов в порядке следования.

class ctypes._Pointer

Приватный, абстрактный базовый класс для указателей.

Конкретные типы указателей создаются путем вызова POINTER() с типом, на который будет указывать указатель; это делается автоматически функцией pointer().

Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартных индексов и срезов. Объекты указателей не имеют размера, поэтому len() вызовет исключение TypeError. Отрицательные индексы будут читать данные из памяти перед указателем (как в C), а индексы вне диапазона, вероятно, приведут к сбою с нарушением доступа (если вам повезет).

_type_

Указывает тип, на который указывает указатель.

contents

Возвращает объект, на который указывает указатель. Присваивание этому атрибуту изменяет указатель на указание на присвоенный объект.

© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.7/library/ctypes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API