Документация Python неофициальный перевод

ctypes.md

1811 строк · 130.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.2/library/ctypes.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 15.17. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) – библиотека для вызова внешних функций на Python89[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) – это библиотека вызова внешних функций для Python. Она предоставляет типы данных, совместимые с C, и позволяет вызывать функции из DLL или разделяемых библиотек. Её можно использовать для обёртывания этих библиотек на чистом Python.1011## 15.17.1. Руководство по ctypes1213Примечание: примеры кода в этом руководстве используют [`doctest`](https://python-all.ru/3.2/library/doctest.html#module-doctest), чтобы убедиться, что они действительно работают. Поскольку некоторые примеры ведут себя по-разному в Linux, Windows или Mac OS X, они содержат директивы doctest в комментариях.1415Примечание: некоторые примеры кода ссылаются на тип ctypes [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int). Этот тип является псевдонимом для типа [`c_long`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_long) в 32-битных системах. Так что не стоит путаться, если выводится [`c_long`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_long), когда вы ожидаете [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int) – на самом деле это один и тот же тип.1617### 15.17.1.1. Загрузка динамически подключаемых библиотек1819[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) exports the *cdll*, and on Windows *windll* and *oledll* objects, for loading dynamic link libraries.2021Библиотеки загружаются через обращение к ним как к атрибутам этих объектов. *cdll* загружает библиотеки, которые экспортируют функции с использованием стандартного соглашения о вызове `cdecl`, тогда как библиотеки *windll* вызывают функции с использованием соглашения о вызове `stdcall`. *oledll* также использует соглашение о вызове `stdcall` и предполагает, что функции возвращают код ошибки Windows `HRESULT`. Этот код ошибки используется для автоматического возбуждения исключения [`WindowsError`](https://python-all.ru/3.2/library/exceptions.html#WindowsError) при сбое вызова функции.2223Вот несколько примеров для Windows. `msvcrt` – это стандартная библиотека C от Microsoft. Она содержит большинство стандартных функций C и использует соглашение о вызовах cdecl.2425```python26>>> from ctypes import *27>>> print(windll.kernel32) 28<WinDLL 'kernel32', handle ... at ...>29>>> print(cdll.msvcrt) 30<CDLL 'msvcrt', handle ... at ...>31>>> libc = cdll.msvcrt 32>>>33```3435Windows автоматически добавляет обычный суффикс файла `.dll`.3637В Linux необходимо указывать имя файла *с расширением* для загрузки библиотеки, поэтому обращение через атрибуты не может использоваться для загрузки библиотек. Следует использовать либо метод `LoadLibrary()` загрузчиков DLL, либо загружать библиотеку, создавая экземпляр CDLL через вызов конструктора:3839```python40>>> cdll.LoadLibrary("libc.so.6") 41<CDLL 'libc.so.6', handle ... at ...>42>>> libc = CDLL("libc.so.6")     43>>> libc                         44<CDLL 'libc.so.6', handle ... at ...>45>>>46```4748### 15.17.1.2. Доступ к функциям из загруженных DLL4950Функции доступны как атрибуты объектов DLL:5152```python53>>> from ctypes import *54>>> libc.printf55<_FuncPtr object at 0x...>56>>> print(windll.kernel32.GetModuleHandleA) 57<_FuncPtr object at 0x...>58>>> print(windll.kernel32.MyOwnFunction) 59Traceback (most recent call last):60  File "<stdin>", line 1, in ?61  File "ctypes.py", line 239, in __getattr__62    func = _StdcallFuncPtr(name, self)63AttributeError: function 'MyOwnFunction' not found64>>>65```6667Обратите внимание, что системные DLL win32, такие как `kernel32` и `user32`, часто экспортируют как ANSI-, так и UNICODE-версии функций. Версия UNICODE экспортируется с добавлением `W` к имени, а версия ANSI – с добавлением `A`. Функция win32 `GetModuleHandle`, которая возвращает *дескриптор модуля* для заданного имени модуля, имеет следующий прототип на C, и макрос используется для предоставления одной из них как `GetModuleHandle` в зависимости от того, определён ли UNICODE:6869```python70/* ANSI version */71HMODULE GetModuleHandleA(LPCSTR lpModuleName);72/* UNICODE version */73HMODULE GetModuleHandleW(LPCWSTR lpModuleName);74```7576*windll* не пытается выбрать одну из них магическим образом; необходимо явно указывать нужную версию, обращаясь к `GetModuleHandleA` или `GetModuleHandleW`, и затем вызывать её с объектами bytes или string соответственно.7778Иногда DLL экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, например, `"??2@YAPAXI@Z"`. В этом случае для получения функции приходится использовать [`getattr()`](https://python-all.ru/3.2/library/functions.html#getattr):7980```python81>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") 82<_FuncPtr object at 0x...>83>>>84```8586В Windows некоторые DLL экспортируют функции не по имени, а по порядковому номеру. К таким функциям можно получить доступ, индексируя объект DLL по порядковому номеру:8788```python89>>> cdll.kernel32[1] 90<_FuncPtr object at 0x...>91>>> cdll.kernel32[0] 92Traceback (most recent call last):93  File "<stdin>", line 1, in ?94  File "ctypes.py", line 310, in __getitem__95    func = _StdcallFuncPtr(name, self)96AttributeError: function ordinal 0 not found97>>>98```99100### 15.17.1.3. Вызов функций101102Эти функции можно вызывать как любые другие вызываемые объекты Python. В этом примере используется функция `time()`, возвращающая системное время в секундах с начала эпохи Unix, и функция `GetModuleHandleA()`, возвращающая дескриптор модуля win32.103104В этом примере обе функции вызываются с нулевым указателем (в качестве нулевого указателя следует использовать `None`):105106```python107>>> print(libc.time(None)) 1081150640792109>>> print(hex(windll.kernel32.GetModuleHandleA(None))) 1100x1d000000111>>>112```113114[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) пытается защитить от вызова функций с неверным количеством аргументов или неверным соглашением о вызове. К сожалению, это работает только в Windows. Это делается путём проверки стека после возврата функции, так что, хотя и возбуждается ошибка, функция *была* вызвана:115116```python117>>> windll.kernel32.GetModuleHandleA() 118Traceback (most recent call last):119  File "<stdin>", line 1, in ?120ValueError: Procedure probably called with not enough arguments (4 bytes missing)121>>> windll.kernel32.GetModuleHandleA(0, 0) 122Traceback (most recent call last):123  File "<stdin>", line 1, in ?124ValueError: Procedure probably called with too many arguments (4 bytes in excess)125>>>126```127128То же исключение возбуждается при вызове функции с соглашением `stdcall` с использованием соглашения о вызове `cdecl`, или наоборот:129130```python131>>> cdll.kernel32.GetModuleHandleA(None) 132Traceback (most recent call last):133  File "<stdin>", line 1, in ?134ValueError: Procedure probably called with not enough arguments (4 bytes missing)135>>>136137>>> windll.msvcrt.printf(b"spam") 138Traceback (most recent call last):139  File "<stdin>", line 1, in ?140ValueError: Procedure probably called with too many arguments (4 bytes in excess)141>>>142```143144Чтобы узнать правильное соглашение о вызовах, необходимо заглянуть в C-заголовочный файл или документацию вызываемой функции.145146В Windows [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) использует структурированную обработку исключений win32 для предотвращения сбоев из-за ошибок защиты при вызовах функций с недопустимыми значениями аргументов:147148```python149>>> windll.kernel32.GetModuleHandleA(32) 150Traceback (most recent call last):151  File "<stdin>", line 1, in ?152WindowsError: exception: access violation reading 0x00000020153>>>154```155156Однако существует достаточно способов привести Python к краху с помощью [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes), так что в любом случае следует проявлять осторожность.157158`None`, целые числа, объекты bytes и (юникодные) строки – единственные нативные объекты Python, которые можно напрямую использовать в качестве параметров при вызовах этих функций. `None` передаётся как указатель C `NULL`, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (`char *` или `wchar_t *`). Целые числа Python передаются как тип C `int`, используемый по умолчанию на данной платформе, их значение маскируется, чтобы поместиться в тип C.159160Прежде чем перейти к вызову функций с другими типами параметров, необходимо узнать больше о типах данных [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes).161162### 15.17.1.4. Основные типы данных163164[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) определяет ряд примитивных типов данных, совместимых с C:165166| Тип ctypes | Тип C | Тип Python |167| --- | --- | --- |168| [`c_bool`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_bool) | `_Bool` | bool (1) |169| [`c_char`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char) | `char` | односимвольный объект bytes |170| [`c_wchar`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_wchar) | `wchar_t` | односимвольная строка |171| [`c_byte`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_byte) | `char` | int |172| [`c_ubyte`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ubyte) | `unsigned char` | int |173| [`c_short`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_short) | `short` | int |174| [`c_ushort`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ushort) | `unsigned short` | int |175| [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int) | `int` | int |176| [`c_uint`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_uint) | `unsigned int` | int |177| [`c_long`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_long) | `long` | int |178| [`c_ulong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ulong) | `unsigned long` | int |179| [`c_longlong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_longlong) | `__int64` или `long long` | int |180| [`c_ulonglong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ulonglong) | `unsigned __int64` или `unsigned long long` | int |181| [`c_size_t`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_size_t) | `size_t` | int |182| [`c_ssize_t`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ssize_t) | `ssize_t` или `Py_ssize_t` | int |183| [`c_float`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_float) | `float` | float |184| [`c_double`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_double) | `double` | float |185| [`c_longdouble`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_longdouble) | `long double` | float |186| [`c_char_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char_p) | `char *` (с завершающим NUL) | объект bytes или `None` |187| [`c_wchar_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_wchar_p) | `wchar_t *` (с завершающим NUL) | строка или `None` |188| [`c_void_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_void_p) | `void *` | int или `None` |1891901. Конструктор принимает любой объект с истинностным значением.191192Все эти типы можно создать, вызвав их с необязательным инициализатором правильного типа и значения:193194```python195>>> c_int()196c_long(0)197>>> c_wchar_p("Hello, World")198c_wchar_p('Hello, World')199>>> c_ushort(-3)200c_ushort(65533)201>>>202```203204Поскольку эти типы изменяемы, их значение можно изменить и впоследствии:205206```python207>>> i = c_int(42)208>>> print(i)209c_long(42)210>>> print(i.value)21142212>>> i.value = -99213>>> print(i.value)214-99215>>>216```217218Присваивание нового значения экземплярам типов-указателей [`c_char_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char_p), [`c_wchar_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_wchar_p) и [`c_void_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_void_p) изменяет *адрес памяти*, на который они указывают, *а не содержимое* блока памяти (конечно, нет, потому что объекты bytes в Python неизменяемы):219220```python221>>> s = "Hello, World"222>>> c_s = c_wchar_p(s)223>>> print(c_s)224c_wchar_p('Hello, World')225>>> c_s.value = "Hi, there"226>>> print(c_s)227c_wchar_p('Hi, there')228>>> print(s)                 # первый объект не изменился229Hello, World230>>>231```232233Однако следует быть осторожным и не передавать их функциям, ожидающим указатели на изменяемую память. Если нужны изменяемые блоки памяти, в ctypes есть функция [`create_string_buffer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.create_string_buffer), которая создаёт их разными способами. Получить доступ к текущему содержимому блока памяти (или изменить его) можно через свойство `raw` ; если требуется обращаться к нему как к строке с завершающим NUL, используйте свойство `value` :234235```python236>>> from ctypes import *237>>> p = create_string_buffer(3)            # создать буфер размером 3 байта, инициализированный нулевыми байтами238>>> print(sizeof(p), repr(p.raw))2393 b'\x00\x00\x00'240>>> p = create_string_buffer(b"Hello")     # создать буфер, содержащий строку, завершающуюся нулевым байтом241>>> print(sizeof(p), repr(p.raw))2426 b'Hello\x00'243>>> print(repr(p.value))244b'Hello'245>>> p = create_string_buffer(b"Hello", 10) # создать буфер на 10 байт246>>> print(sizeof(p), repr(p.raw))24710 b'Hello\x00\x00\x00\x00\x00'248>>> p.value = b"Hi"249>>> print(sizeof(p), repr(p.raw))25010 b'Hi\x00lo\x00\x00\x00\x00\x00'251>>>252```253254Функция [`create_string_buffer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.create_string_buffer) заменила функцию `c_buffer()` (которая всё ещё доступна как псевдоним), а также функцию `c_string()` из более ранних выпусков ctypes. Чтобы создать изменяемый блок памяти, содержащий символы Unicode типа C `wchar_t`, используйте функцию [`create_unicode_buffer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.create_unicode_buffer).255256### 15.17.1.5. Вызов функций, продолжение257258Обратите внимание, что printf выводит в реальный канал стандартного вывода, *а не* в [`sys.stdout`](https://python-all.ru/3.2/library/sys.html#sys.stdout), поэтому эти примеры будут работать только в командной строке, а не из *IDLE* или *PythonWin*:259260```python261>>> printf = libc.printf262>>> printf(b"Hello, %s\n", b"World!")263Hello, World!26414265>>> printf(b"Hello, %S\n", "World!")266Hello, World!26714268>>> printf(b"%d bottles of beer\n", 42)26942 bottles of beer27019271>>> printf(b"%f bottles of beer\n", 42.5)272Traceback (most recent call last):273  File "<stdin>", line 1, in ?274ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2275>>>276```277278Как уже упоминалось, все типы Python, кроме целых чисел, строк и объектов bytes, должны быть обёрнуты в соответствующий тип [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes), чтобы их можно было преобразовать в требуемый тип C:279280```python281>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))282An int 1234, a double 3.14000028331284>>>285```286287### 15.17.1.6. Вызов функций с собственными пользовательскими типами данных288289Вы также можете настроить преобразование аргументов [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes), чтобы экземпляры ваших собственных классов можно было использовать в качестве аргументов функций. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) ищет атрибут `_as_parameter_` и использует его в качестве аргумента функции. Разумеется, это должно быть целое число, строка или bytes:290291```python292>>> class Bottles:293...     def __init__(self, number):294...         self._as_parameter_ = number295...296>>> bottles = Bottles(42)297>>> printf(b"%d bottles of beer\n", bottles)29842 bottles of beer29919300>>>301```302303Если вы не хотите хранить данные экземпляра в переменной экземпляра `_as_parameter_`, можно определить [`свойство`](https://python-all.ru/3.2/library/functions.html#property), которое делает этот атрибут доступным по запросу.304305### 15.17.1.7. Указание требуемых типов аргументов (прототипы функций)306307Можно указать требуемые типы аргументов функций, экспортируемых из DLL, с помощью установки атрибута `argtypes`.308309`argtypes` должен быть последовательностью типов C (функция `printf`, вероятно, не самый удачный пример, потому что она принимает переменное количество параметров разных типов в зависимости от строки формата, но с другой стороны это довольно удобно для экспериментов с этой возможностью):310311```python312>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]313>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)314String 'Hi', Int 10, Double 2.20000031537316>>>317```318319Указание формата защищает от несовместимых типов аргументов (как прототип для функции C) и пытается преобразовать аргументы в допустимые типы:320321```python322>>> printf(b"%d %d %d", 1, 2, 3)323Traceback (most recent call last):324  File "<stdin>", line 1, in ?325ArgumentError: argument 2: exceptions.TypeError: wrong type326>>> printf(b"%s %d %f\n", b"X", 2, 3)327X 2 3.00000032813329>>>330```331332Если вы определили собственные классы, которые передаёте в вызовы функций, вам нужно реализовать метод класса `from_param()`, чтобы можно было использовать их в последовательности `argtypes`. Метод класса `from_param()` получает объект Python, переданный в вызов функции; он должен выполнить проверку типа или всё необходимое, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут `_as_parameter_` или то, что вы хотите передать в качестве аргумента функции C в данном случае. Опять же, результатом должно быть целое число, строка, bytes, экземпляр [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) или объект с атрибутом `_as_parameter_`.333334### 15.17.1.8. Типы возвращаемых значений335336По умолчанию считается, что функции возвращают тип C `int`. Другие типы возвращаемых значений можно указать, задав атрибут `restype` объекта функции.337338Вот более сложный пример: в нём используется функция `strchr`, которая ожидает указатель на строку и символ (char) и возвращает указатель на строку:339340```python341>>> strchr = libc.strchr342>>> strchr(b"abcdef", ord("d")) 3438059983344>>> strchr.restype = c_char_p   # c_char_p – указатель на строку345>>> strchr(b"abcdef", ord("d"))346b'def'347>>> print(strchr(b"abcdef", ord("x")))348None349>>>350```351352Если вы хотите избежать вызовов `ord("x")` выше, можно установить атрибут `argtypes`, и второй аргумент будет преобразован из объекта Python bytes, содержащего один символ, в C char:353354```python355>>> strchr.restype = c_char_p356>>> strchr.argtypes = [c_char_p, c_char]357>>> strchr(b"abcdef", b"d")358'def'359>>> strchr(b"abcdef", b"def")360Traceback (most recent call last):361  File "<stdin>", line 1, in ?362ArgumentError: argument 2: exceptions.TypeError: one character string expected363>>> print(strchr(b"abcdef", b"x"))364None365>>> strchr(b"abcdef", b"d")366'def'367>>>368```369370Также можно использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута `restype`, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с *целым числом*, которое возвращает C-функция, и результат этого вызова будет использоваться как результат вызова вашей функции. Это полезно для проверки возвращаемых значений ошибок и автоматического возбуждения исключения:371372```python373>>> GetModuleHandle = windll.kernel32.GetModuleHandleA 374>>> def ValidHandle(value):375...     if value == 0:376...         raise WinError()377...     return value378...379>>>380>>> GetModuleHandle.restype = ValidHandle 381>>> GetModuleHandle(None) 382486539264383>>> GetModuleHandle("something silly") 384Traceback (most recent call last):385  File "<stdin>", line 1, in ?386  File "<stdin>", line 3, in ValidHandle387WindowsError: [Errno 126] The specified module could not be found.388>>>389```390391`WinError` – это функция, которая вызывает Windows API `FormatMessage()` для получения строкового представления кода ошибки и *возвращает* исключение. `WinError` принимает необязательный параметр – код ошибки; если он не передан, функция вызывает [`GetLastError()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.GetLastError) для его получения.392393Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен через атрибут `errcheck`; подробности см. в справочном руководстве.394395### 15.17.1.9. Передача указателей (или: передача параметров по ссылке)396397Иногда функция C API ожидает в качестве параметра *указатель* на тип данных, обычно для записи в соответствующее место или если данные слишком велики для передачи по значению. Это также называется *передачей параметров по ссылке*.398399[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) предоставляет функцию [`byref()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.byref), которая используется для передачи параметров по ссылке. Того же эффекта можно достичь с помощью функции [`pointer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.pointer), хотя [`pointer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.pointer) выполняет гораздо больше работы, так как конструирует реальный объект-указатель, поэтому быстрее использовать [`byref()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.byref), если вам не нужен объект-указатель в самом Python:400401```python402>>> i = c_int()403>>> f = c_float()404>>> s = create_string_buffer(b'\000' * 32)405>>> print(i.value, f.value, repr(s.value))4060 0.0 b''407>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",408...             byref(i), byref(f), s)4093410>>> print(i.value, f.value, repr(s.value))4111 3.1400001049 b'Hello'412>>>413```414415### 15.17.1.10. Структуры и объединения416417Структуры и объединения должны наследоваться от базовых классов [`Structure`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure) и [`Union`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Union), которые определены в модуле [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes). Каждый подкласс должен определять атрибут `_fields_`. `_fields_` должен быть списком *2-кортежей*, содержащих *имя поля* и *тип поля*.418419Тип поля должен быть типом [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes), таким как [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int), или любым другим производным типом [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes): структура, объединение, массив, указатель.420421Вот простой пример структуры POINT, которая содержит два целых числа с именами *x* и *y*, а также показывает, как инициализировать структуру в конструкторе:422423```python424>>> from ctypes import *425>>> class POINT(Structure):426...     _fields_ = [("x", c_int),427...                 ("y", c_int)]428...429>>> point = POINT(10, 20)430>>> print(point.x, point.y)43110 20432>>> point = POINT(y=5)433>>> print(point.x, point.y)4340 5435>>> POINT(1, 2, 3)436Traceback (most recent call last):437  File "<stdin>", line 1, in ?438ValueError: too many initializers439>>>440```441442Однако можно создавать гораздо более сложные структуры. Структура может сама содержать другие структуры, используя структуру в качестве типа поля.443444Вот структура RECT, которая содержит две точки с именами *upperleft* и *lowerright*:445446```python447>>> class RECT(Structure):448...     _fields_ = [("upperleft", POINT),449...                 ("lowerright", POINT)]450...451>>> rc = RECT(point)452>>> print(rc.upperleft.x, rc.upperleft.y)4530 5454>>> print(rc.lowerright.x, rc.lowerright.y)4550 0456>>>457```458459Вложенные структуры также можно инициализировать в конструкторе несколькими способами:460461```python462>>> r = RECT(POINT(1, 2), POINT(3, 4))463>>> r = RECT((1, 2), (3, 4))464```465466полей [*descriptor*](https://python-all.ru/3.2/glossary.html#term-descriptor) можно получить из *класса*; они полезны для отладки, так как предоставляют полезную информацию:467468```python469>>> print(POINT.x)470<Field type=c_long, ofs=0, size=4>471>>> print(POINT.y)472<Field type=c_long, ofs=4, size=4>473>>>474```475476> **Предупреждение**477>478> [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя на 32-битной x86 это может работать, библиотека не гарантирует работу в общем случае. Объединения и структуры с битовыми полями всегда следует передавать в функции по указателю.479480### 15.17.1.11. Выравнивание структур/объединений и порядок байтов481482По умолчанию поля Structure и Union выравниваются так же, как это делает компилятор C. Можно переопределить это поведение, указав атрибут класса `_pack_` в определении подкласса. Он должен быть установлен в положительное целое число и задает максимальное выравнивание для полей. Это то же самое, что делает `#pragma pack(n)` в MSVC.483484[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) использует собственный порядок байтов для структур и объединений. Для создания структур с нестандартным порядком байтов можно использовать один из базовых классов: [`BigEndianStructure`](https://python-all.ru/3.2/library/ctypes.html#ctypes.BigEndianStructure), [`LittleEndianStructure`](https://python-all.ru/3.2/library/ctypes.html#ctypes.LittleEndianStructure), `BigEndianUnion` и `LittleEndianUnion`. Эти классы не могут содержать поля-указатели.485486### 15.17.1.12. Битовые поля в структурах и объединениях487488Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей; ширина битового поля задается третьим элементом в кортежах `_fields_`:489490```python491>>> class Int(Structure):492...     _fields_ = [("first_16", c_int, 16),493...                 ("second_16", c_int, 16)]494...495>>> print(Int.first_16)496<Field type=c_long, ofs=0:0, bits=16>497>>> print(Int.second_16)498<Field type=c_long, ofs=0:16, bits=16>499>>>500```501502### 15.17.1.13. Массивы503504Массивы – это последовательности, содержащие фиксированное количество экземпляров одного типа.505506Рекомендуемый способ создания типов массивов – умножение типа данных на положительное целое число:507508```python509TenPointsArrayType = POINT * 10510```511512Вот пример несколько искусственного типа данных – структуры, содержащей 4 точки POINT среди прочего:513514```python515>>> from ctypes import *516>>> class POINT(Structure):517...    _fields_ = ("x", c_int), ("y", c_int)518...519>>> class MyStruct(Structure):520...    _fields_ = [("a", c_int),521...                ("b", c_float),522...                ("point_array", POINT * 4)]523>>>524>>> print(len(MyStruct().point_array))5254526>>>527```528529Экземпляры создаются обычным способом – вызовом класса:530531```python532arr = TenPointsArrayType()533for pt in arr:534    print(pt.x, pt.y)535```536537Приведенный выше код выводит серию строк `0 0`, потому что содержимое массива инициализируется нулями.538539Можно также указать инициализаторы правильного типа:540541```python542>>> from ctypes import *543>>> TenIntegers = c_int * 10544>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)545>>> print(ii)546<c_long_Array_10 object at 0x...>547>>> for i in ii: print(i, end=" ")548...5491 2 3 4 5 6 7 8 9 10550>>>551```552553### 15.17.1.14. Указатели554555Экземпляры указателей создаются вызовом функции [`pointer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.pointer) для типа [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes):556557```python558>>> from ctypes import *559>>> i = c_int(42)560>>> pi = pointer(i)561>>>562```563564Экземпляры указателей имеют атрибут `contents`, который возвращает объект, на который указывает указатель – объект `i` из примера выше:565566```python567>>> pi.contents568c_long(42)569>>>570```571572Обратите внимание, что [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) не имеет OOR (original object return); каждый раз при получении атрибута создается новый эквивалентный объект:573574```python575>>> pi.contents is i576False577>>> pi.contents is pi.contents578False579>>>580```581582Присвоение другого экземпляра [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int) атрибуту contents указателя приведет к тому, что указатель будет указывать на область памяти, где этот экземпляр хранится:583584```python585>>> i = c_int(99)586>>> pi.contents = i587>>> pi.contents588c_long(99)589>>>590```591592Экземпляры указателей также можно индексировать целыми числами:593594```python595>>> pi[0]59699597>>>598```599600Присваивание целочисленному индексу изменяет значение, на которое указывает указатель:601602```python603>>> print(i)604c_long(99)605>>> pi[0] = 22606>>> print(i)607c_long(22)608>>>609```610611It is also possible to use indexes different from 0, but you must know what you’re doing, just as in C: You can access or change arbitrary memory locations. Generally you only use this feature if you receive a pointer from a C function, and you *know* that the pointer actually points to an array instead of a single item.612613За кулисами функция [`pointer()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.pointer) делает больше, чем просто создание экземпляров указателей; сначала она должна создать *типы* указателей. Это делается с помощью функции [`POINTER()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.POINTER), которая принимает любой тип [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) и возвращает новый тип:614615```python616>>> PI = POINTER(c_int)617>>> PI618<class 'ctypes.LP_c_long'>619>>> PI(42)620Traceback (most recent call last):621  File "<stdin>", line 1, in ?622TypeError: expected c_long instead of int623>>> PI(c_int(42))624<ctypes.LP_c_long object at 0x...>625>>>626```627628Вызов типа указателя без аргумента создает нулевой указатель `NULL`. Нулевые указатели `NULL` имеют булево значение `False`:629630```python631>>> null_ptr = POINTER(c_int)()632>>> print(bool(null_ptr))633False634>>>635```636637[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) проверяет на `NULL` при разыменовании указателей (но разыменование недопустимых ненулевых указателей `NULL` приведет к аварийному завершению Python):638639```python640>>> null_ptr[0]641Traceback (most recent call last):642    ....643ValueError: NULL pointer access644>>>645646>>> null_ptr[0] = 1234647Traceback (most recent call last):648    ....649ValueError: NULL pointer access650>>>651```652653### 15.17.1.15. Преобразования типов654655Обычно ctypes выполняет строгую проверку типов. Это означает, что если у вас есть `POINTER(c_int)` в списке `argtypes` функции или в качестве типа поля-члена в определении структуры, принимаются только экземпляры точно того же типа. Из этого правила есть некоторые исключения, когда ctypes принимает другие объекты. Например, можно передавать совместимые экземпляры массивов вместо типов указателей. Так, для `POINTER(c_int)` ctypes принимает массив c\_int:656657```python658>>> class Bar(Structure):659...     _fields_ = [("count", c_int), ("values", POINTER(c_int))]660...661>>> bar = Bar()662>>> bar.values = (c_int * 3)(1, 2, 3)663>>> bar.count = 3664>>> for i in range(bar.count):665...     print(bar.values[i])666...667166826693670>>>671```672673Кроме того, если аргумент функции явно объявлен как тип указателя (например, `POINTER(c_int)`) в `argtypes`, в функцию можно передать объект указываемого типа (в данном случае `c_int`). В этом случае ctypes автоматически применит необходимое преобразование [`byref()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.byref).674675Чтобы установить поле типа POINTER в `NULL`, можно присвоить `None`:676677```python678>>> bar.values = None679>>>680```681682Иногда встречаются экземпляры несовместимых типов. В C можно привести один тип к другому. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) предоставляет функцию [`cast()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.cast), которую можно использовать аналогично. Структура `Bar`, определенная выше, принимает указатели `POINTER(c_int)` или массивы [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int) для своего поля `values`, но не экземпляры других типов:683684```python685>>> bar.values = (c_byte * 4)()686Traceback (most recent call last):687  File "<stdin>", line 1, in ?688TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance689>>>690```691692Для таких случаев полезна функция [`cast()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.cast).693694Функцию [`cast()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.cast) можно использовать для приведения экземпляра ctypes к указателю на другой тип данных ctypes. [`cast()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.cast) принимает два параметра: объект ctypes, который является или может быть преобразован в указатель некоторого вида, и тип указателя ctypes. Она возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:695696```python697>>> a = (c_byte * 4)()698>>> cast(a, POINTER(c_int))699<ctypes.LP_c_long object at ...>700>>>701```702703Таким образом, [`cast()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.cast) можно использовать для присвоения полю `values` структуры `Bar`:704705```python706>>> bar = Bar()707>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))708>>> print(bar.values[0])7090710>>>711```712713### 15.17.1.16. Неполные типы714715*Неполные типы* – это структуры, объединения или массивы, члены которых ещё не определены. В C они задаются опережающими объявлениями, которые определяются позже:716717```python718struct cell; /* forward declaration */719720struct cell {721    char *name;722    struct cell *next;723};724```725726Прямой перевод в код ctypes выглядел бы так, но это не работает:727728```python729>>> class cell(Structure):730...     _fields_ = [("name", c_char_p),731...                 ("next", POINTER(cell))]732...733Traceback (most recent call last):734  File "<stdin>", line 1, in ?735  File "<stdin>", line 2, in cell736NameError: name 'cell' is not defined737>>>738```739740потому что новый класс `class cell` недоступен в самом определении класса. В [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) мы можем определить класс `cell` и установить атрибут `_fields_` позже, после определения класса:741742```python743>>> from ctypes import *744>>> class cell(Structure):745...     pass746...747>>> cell._fields_ = [("name", c_char_p),748...                  ("next", POINTER(cell))]749>>>750```751752Попробуем. Создадим два экземпляра `cell`, заставим их указывать друг на друга и, наконец, пройдем по цепочке указателей несколько раз:753754```python755>>> c1 = cell()756>>> c1.name = "foo"757>>> c2 = cell()758>>> c2.name = "bar"759>>> c1.next = pointer(c2)760>>> c2.next = pointer(c1)761>>> p = c1762>>> for i in range(8):763...     print(p.name, end=" ")764...     p = p.next[0]765...766foo bar foo bar foo bar foo bar767>>>768```769770### 15.17.1.17. Функции обратного вызова771772[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) позволяет создавать указатели на функции, вызываемые из C, на основе вызываемых объектов Python. Иногда их называют *функциями обратного вызова*.773774Сначала необходимо создать класс для функции обратного вызова. Этот класс знает соглашение вызова, тип возвращаемого значения, а также количество и типы аргументов, которые будет получать эта функция.775776Фабричная функция CFUNCTYPE создаёт типы для функций обратного вызова с использованием обычного соглашения вызова cdecl, а на Windows фабричная функция WINFUNCTYPE создаёт типы для функций обратного вызова с использованием соглашения вызова stdcall.777778Обе эти фабричные функции вызываются с типом результата в качестве первого аргумента, а ожидаемые типы аргументов функции обратного вызова передаются как остальные аргументы.779780Я приведу пример, в котором используется функция `qsort()` из стандартной библиотеки C; она сортирует элементы с помощью функции обратного вызова. `qsort()` будет использована для сортировки массива целых чисел:781782```python783>>> IntArray5 = c_int * 5784>>> ia = IntArray5(5, 1, 7, 33, 99)785>>> qsort = libc.qsort786>>> qsort.restype = None787>>>788```789790Функция `qsort()` должна вызываться с указателем на данные для сортировки, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения (обратный вызов). Затем обратный вызов будет вызван с двумя указателями на элементы; он должен возвращать отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.791792Итак, наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала создадим `тип` для функции обратного вызова:793794```python795>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))796>>>797```798799Для первой реализации функции обратного вызова мы просто выводим полученные аргументы и возвращаем 0 (итеративная разработка ;-)):800801```python802>>> def py_cmp_func(a, b):803...     print("py_cmp_func", a, b)804...     return 0805...806>>>807```808809Создайте вызываемый из C обратный вызов:810811```python812>>> cmp_func = CMPFUNC(py_cmp_func)813>>>814```815816И мы готовы начать:817818```python819>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) 820py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>821py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>822py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>823py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>824py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>825py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>826py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>827py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>828py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>829py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...>830>>>831```832833Мы знаем, как получить доступ к содержимому указателя, поэтому переопределим наш колбэк:834835```python836>>> def py_cmp_func(a, b):837...     print("py_cmp_func", a[0], b[0])838...     return 0839...840>>> cmp_func = CMPFUNC(py_cmp_func)841>>>842```843844Вот что мы получаем в Windows:845846```python847>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) 848py_cmp_func 7 1849py_cmp_func 33 1850py_cmp_func 99 1851py_cmp_func 5 1852py_cmp_func 7 5853py_cmp_func 33 5854py_cmp_func 99 5855py_cmp_func 7 99856py_cmp_func 33 99857py_cmp_func 7 33858>>>859```860861Забавно, что на linux функция сортировки, похоже, работает гораздо эффективнее – она делает меньше сравнений:862863```python864>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) 865py_cmp_func 5 1866py_cmp_func 33 99867py_cmp_func 7 33868py_cmp_func 5 7869py_cmp_func 1 7870>>>871```872873Ах, мы почти закончили! Последний шаг – собственно сравнить два элемента и вернуть полезный результат:874875```python876>>> def py_cmp_func(a, b):877...     print("py_cmp_func", a[0], b[0])878...     return a[0] - b[0]879...880>>>881```882883Финальный запуск в Windows:884885```python886>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) 887py_cmp_func 33 7888py_cmp_func 99 33889py_cmp_func 5 99890py_cmp_func 1 99891py_cmp_func 33 7892py_cmp_func 1 33893py_cmp_func 5 33894py_cmp_func 5 7895py_cmp_func 1 7896py_cmp_func 5 1897>>>898```899900и в Linux:901902```python903>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) 904py_cmp_func 5 1905py_cmp_func 33 99906py_cmp_func 7 33907py_cmp_func 1 7908py_cmp_func 5 7909>>>910```911912Довольно интересно, что функция `qsort()` в Windows требует больше сравнений, чем версия в linux!913914Как легко проверить, наш массив теперь отсортирован:915916```python917>>> for i in ia: print(i, end=" ")918...9191 5 7 33 99920>>>921```922923**Важное замечание для функций обратного вызова:**924925Сохраняйте ссылки на объекты CFUNCTYPE, пока они используются из кода C. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) этого не делает, а если не сохранить, они могут быть собраны сборщиком мусора, что приведет к краху программы при вызове колбэка.926927### 15.17.1.18. Доступ к значениям, экспортируемым из DLL928929Некоторые разделяемые библиотеки не только экспортируют функции, но и экспортируют переменные. Примером в самой библиотеке Python является `Py_OptimizeFlag` – целое число, принимающее значения 0, 1 или 2 в зависимости от флага [*-O*](https://python-all.ru/3.2/using/cmdline.html#cmdoption-O) или [*-OO*](https://python-all.ru/3.2/using/cmdline.html#cmdoption-OO), заданного при запуске.930931[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) может получать доступ к таким значениям с помощью методов класса `in_dll()` соответствующего типа. *pythonapi* – это предопределённый символ, предоставляющий доступ к C API Python:932933```python934>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")935>>> print(opt_flag)936c_long(0)937>>>938```939940Если бы интерпретатор был запущен с флагом [*-O*](https://python-all.ru/3.2/using/cmdline.html#cmdoption-O), пример вывел бы `c_long(1)`, или `c_long(2)`, если бы был указан [*-OO*](https://python-all.ru/3.2/using/cmdline.html#cmdoption-OO).941942Расширенный пример, который также демонстрирует использование указателей, обращается к указателю [`PyImport_FrozenModules`](https://python-all.ru/3.2/c-api/import.html#PyImport_FrozenModules), экспортируемому Python.943944Цитируя документацию для этого значения:945946> Этот указатель инициализируется так, чтобы указывать на массив записей947>948> `struct _frozen`949>950> , заканчивающийся записью, все члены которой равны951>952> *NULL*953>954> или нулю. Когда замороженный модуль импортируется, он ищется в этой таблице. Сторонний код может использовать это для предоставления динамически созданной коллекции замороженных модулей.955956Таким образом, манипуляции с этим указателем могут оказаться полезными. Чтобы не увеличивать размер примера, мы покажем только, как эту таблицу можно прочитать с помощью [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes):957958```python959>>> from ctypes import *960>>>961>>> class struct_frozen(Structure):962...     _fields_ = [("name", c_char_p),963...                 ("code", POINTER(c_ubyte)),964...                 ("size", c_int)]965...966>>>967```968969Мы определили тип данных `struct _frozen`, поэтому можем получить указатель на таблицу:970971```python972>>> FrozenTable = POINTER(struct_frozen)973>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")974>>>975```976977Поскольку `table` – это `указатель` на массив записей `struct_frozen`, мы можем выполнять итерацию по нему, но нужно убедиться, что цикл завершится, ведь у указателей нет размера. Рано или поздно это, вероятно, приведёт к ошибке доступа или чему-то подобному, так что лучше выйти из цикла, когда мы достигнем NULL-записи:978979```python980>>> for item in table:981...    print(item.name, item.size)982...    if item.name is None:983...        break984...985__hello__ 104986__phello__ -104987__phello__.spam 104988None 0989>>>990```991992То, что в стандартном Python есть frozen-модуль и frozen-пакет (обозначаемый отрицательным значением поля size), малоизвестно; это используется только для тестирования. Попробуйте, например, `import __hello__`.993994### 15.17.1.19. Сюрпризы995996В [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) есть несколько граничных случаев, где ожидаемое поведение может отличаться от фактического.997998Рассмотрим следующий пример:9991000```python1001>>> from ctypes import *1002>>> class POINT(Structure):1003...     _fields_ = ("x", c_int), ("y", c_int)1004...1005>>> class RECT(Structure):1006...     _fields_ = ("a", POINT), ("b", POINT)1007...1008>>> p1 = POINT(1, 2)1009>>> p2 = POINT(3, 4)1010>>> rc = RECT(p1, p2)1011>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)10121 2 3 41013>>> # теперь поменять точки местами1014>>> rc.a, rc.b = rc.b, rc.a1015>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)10163 4 3 41017>>>1018```10191020Хм. Мы, конечно, ожидали, что последний оператор выведет `3 4 1 2`. Что произошло? Вот шаги строки `rc.a, rc.b = rc.b, rc.a` выше:10211022```python1023>>> temp0, temp1 = rc.b, rc.a1024>>> rc.a = temp01025>>> rc.b = temp11026>>>1027```10281029Обратите внимание, что `temp0` и `temp1` – это объекты, всё ещё использующие внутренний буфер объекта `rc` выше. Таким образом, выполнение `rc.a = temp0` копирует содержимое буфера `temp0` в буфер `rc`. Это, в свою очередь, изменяет содержимое `temp1`. Поэтому последнее присваивание `rc.b = temp1` не даёт ожидаемого эффекта.10301031Имейте в виду, что получение подобъектов из Structure, Unions и Arrays не *копирует* подобъект, а возвращает объект-обертку, обращающийся к базовому буферу корневого объекта.10321033Еще один пример, который может вести себя не так, как ожидается:10341035```python1036>>> s = c_char_p()1037>>> s.value = "abc def ghi"1038>>> s.value1039'abc def ghi'1040>>> s.value is s.value1041False1042>>>1043```10441045Почему выводится `False`? Экземпляры ctypes – это объекты, содержащие блок памяти и несколько [*дескрипторов*](https://python-all.ru/3.2/glossary.html#term-descriptor) для доступа к содержимому памяти. При сохранении объекта Python в блоке памяти сохраняется не сам объект, а его `содержимое`. При каждом обращении к содержимому создаётся новый объект Python!10461047### 15.17.1.20. Типы данных переменного размера10481049[`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) предоставляет некоторую поддержку массивов и структур переменного размера.10501051Функция [`resize()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.resize) может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и запрошенный размер в байтах в качестве второго. Блок памяти нельзя сделать меньше, чем естественный блок памяти, заданный типом объекта; при попытке такого изменения возникает исключение [`ValueError`](https://python-all.ru/3.2/library/exceptions.html#ValueError):10521053```python1054>>> short_array = (c_short * 4)()1055>>> print(sizeof(short_array))105681057>>> resize(short_array, 4)1058Traceback (most recent call last):1059    ...1060ValueError: minimum size is 81061>>> resize(short_array, 32)1062>>> sizeof(short_array)1063321064>>> sizeof(type(short_array))106581066>>>1067```10681069Это хорошо и замечательно, но как получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку тип по-прежнему знает только о 4 элементах, при попытке доступа к другим элементам возникают ошибки:10701071```python1072>>> short_array[:]1073[0, 0, 0, 0]1074>>> short_array[7]1075Traceback (most recent call last):1076    ...1077IndexError: invalid index1078>>>1079```10801081Другой способ использования типов данных переменного размера с [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) – воспользоваться динамической природой Python и (пере)определять тип данных после того, как требуемый размер уже известен, для каждого конкретного случая.10821083## 15.17.2. Справочник по ctypes10841085### 15.17.2.1. Поиск разделяемых библиотек10861087При программировании на компилируемом языке к разделяемым библиотекам обращаются при компиляции/компоновке программы и при ее запуске.10881089Назначение функции `find_library()` – найти библиотеку способом, аналогичным тому, как это делает компилятор (на платформах с несколькими версиями разделяемой библиотеки должна загружаться самая новая), в то время как загрузчики библиотек ctypes действуют как при запуске программы и вызывают загрузчик времени выполнения напрямую.10901091Модуль `ctypes.util` предоставляет функцию, которая может помочь определить, какую библиотеку загрузить.10921093#### `ctypes.util.find_library(name)`10941095Пытается найти библиотеку и возвращает путь к ней. *name* – это имя библиотеки без префикса *lib*, суффикса `.so`, `.dylib` или номера версии (именно такая форма используется для опции компоновщика posix *-l*). Если библиотеку не удалось найти, возвращает `None`.10961097Точное поведение зависит от системы.10981099В Linux `find_library()` пытается запустить внешние программы (`/sbin/ldconfig`, `gcc` и `objdump`), чтобы найти файл библиотеки. Возвращается имя файла библиотеки. Вот несколько примеров:11001101```python1102>>> from ctypes.util import find_library1103>>> find_library("m")1104'libm.so.6'1105>>> find_library("c")1106'libc.so.6'1107>>> find_library("bz2")1108'libbz2.so.1.0'1109>>>1110```11111112В OS X `find_library()` пробует несколько предопределённых схем именования и путей для поиска библиотеки и возвращает полный путь в случае успеха:11131114```python1115>>> from ctypes.util import find_library1116>>> find_library("c")1117'/usr/lib/libc.dylib'1118>>> find_library("m")1119'/usr/lib/libm.dylib'1120>>> find_library("bz2")1121'/usr/lib/libbz2.dylib'1122>>> find_library("AGL")1123'/System/Library/Frameworks/AGL.framework/AGL'1124>>>1125```11261127В Windows `find_library()` выполняет поиск по системному пути поиска и возвращает полный путь, но поскольку нет предопределённой схемы именования, вызов вроде `find_library("c")` завершится ошибкой и вернёт `None`.11281129При обёртывании разделяемой библиотеки с помощью [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) *может быть* лучше определить имя разделяемой библиотеки на этапе разработки и жёстко прописать его в модуле-обёртке, а не использовать `find_library()` для поиска библиотеки во время выполнения.11301131### 15.17.2.2. Загрузка разделяемых библиотек11321133Существует несколько способов загрузить разделяемые библиотеки в процесс Python. Один из способов – создать экземпляр одного из следующих классов:11341135#### `class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)`11361137Экземпляры этого класса представляют загруженные разделяемые библиотеки. Функции в этих библиотеках используют стандартное соглашение о вызове C и считаются возвращающими `int`.11381139#### `class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)`11401141Только для Windows: экземпляры этого класса представляют загруженные разделяемые библиотеки; функции в этих библиотеках используют соглашение о вызове `stdcall` и, как предполагается, возвращают специфичный для Windows код [`HRESULT`](https://python-all.ru/3.2/library/ctypes.html#ctypes.HRESULT). Значения [`HRESULT`](https://python-all.ru/3.2/library/ctypes.html#ctypes.HRESULT) содержат информацию, указывающую, успешно ли завершился вызов функции или произошла ошибка, вместе с дополнительным кодом ошибки. Если возвращаемое значение сигнализирует об ошибке, автоматически возбуждается исключение [`WindowsError`](https://python-all.ru/3.2/library/exceptions.html#WindowsError).11421143#### `class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False)`11441145Только для Windows: экземпляры этого класса представляют загруженные разделяемые библиотеки, функции в этих библиотеках используют соглашение о вызове `stdcall` и по умолчанию предполагается, что они возвращают `int`.11461147На Windows CE используется только стандартное соглашение о вызове; для удобства [`WinDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.WinDLL) и [`OleDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.OleDLL) на этой платформе используют стандартное соглашение о вызове.11481149Глобальная блокировка интерпретатора Python [*global interpreter lock*](https://python-all.ru/3.2/glossary.html#term-global-interpreter-lock) освобождается перед вызовом любой функции, экспортируемой этими библиотеками, и снова захватывается после вызова.11501151#### `class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)`11521153Экземпляры этого класса ведут себя как экземпляры [`CDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.CDLL), за исключением того, что GIL Python *не* освобождается при вызове функции, а после выполнения функции проверяется флаг ошибок Python. Если флаг ошибок установлен, возбуждается исключение Python.11541155Таким образом, это полезно только для прямого вызова функций Python C API.11561157Все эти классы можно создать, вызвав их с хотя бы одним аргументом – именем пути разделяемой библиотеки. Если у вас уже есть дескриптор загруженной разделяемой библиотеки, его можно передать в качестве именованного параметра `handle`; в противном случае для загрузки библиотеки в процесс и получения её дескриптора используются функции базовой платформы `dlopen` или `LoadLibrary`.11581159Параметр *mode* можно использовать для указания способа загрузки библиотеки. Подробнее см. страницу руководства *dlopen(3)*; в Windows параметр *mode* игнорируется.11601161Параметр *use\_errno*, если установлен в True, включает механизм ctypes, позволяющий безопасно обращаться к системному номеру ошибки [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno). [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) хранит локальную для потока копию системной переменной [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno); если вызвать внешнюю функцию, созданную с `use_errno=True`, то значение [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno) перед вызовом функции заменяется на частную копию ctypes; то же происходит сразу после вызова функции.11621163Функция [`ctypes.get_errno()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.get_errno) возвращает значение частной копии ctypes, а функция [`ctypes.set_errno()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.set_errno) изменяет частную копию ctypes на новое значение и возвращает предыдущее значение.11641165Параметр *use\_last\_error*, если установлен в True, включает тот же механизм для кода ошибки Windows, который управляется функциями Windows API [`GetLastError()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.GetLastError) и `SetLastError()`; функции [`ctypes.get_last_error()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.get_last_error) и [`ctypes.set_last_error()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.set_last_error) используются для получения и изменения частной копии кода ошибки Windows в ctypes.11661167#### `ctypes.RTLD_GLOBAL`11681169Флаг, используемый в качестве параметра *mode*. На платформах, где этот флаг недоступен, он определён как целочисленный ноль.11701171#### `ctypes.RTLD_LOCAL`11721173Флаг, используемый в качестве параметра *mode*. На платформах, где он недоступен, он совпадает с *RTLD\_GLOBAL*.11741175#### `ctypes.DEFAULT_MODE`11761177Режим по умолчанию, используемый для загрузки разделяемых библиотек. На OSX 10.3 это *RTLD\_GLOBAL*, в противном случае он совпадает с *RTLD\_LOCAL*.11781179Экземпляры этих классов не имеют открытых методов, однако [`__getattr__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getattr__) и [`__getitem__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getitem__) обладают особым поведением: функции, экспортируемые общей библиотекой, могут быть доступны как атрибуты или по индексу. Обратите внимание, что оба [`__getattr__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getattr__) и [`__getitem__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getitem__) кешируют свой результат, поэтому повторные вызовы возвращают один и тот же объект.11801181Доступны следующие открытые атрибуты, их имена начинаются с подчёркивания, чтобы не конфликтовать с именами экспортируемых функций:11821183#### `PyDLL._handle`11841185Системный дескриптор, используемый для доступа к библиотеке.11861187#### `PyDLL._name`11881189Имя библиотеки, переданное в конструктор.11901191Разделяемые библиотеки также можно загружать с помощью одного из предварительно созданных объектов, которые являются экземплярами класса [`LibraryLoader`](https://python-all.ru/3.2/library/ctypes.html#ctypes.LibraryLoader), либо вызывая метод `LoadLibrary()`, либо получая библиотеку как атрибут экземпляра загрузчика.11921193#### `class ctypes.LibraryLoader(dlltype)`11941195Класс, который загружает разделяемые библиотеки. *dlltype* должен быть одним из типов [`CDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.CDLL), [`PyDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.PyDLL), [`WinDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.WinDLL) или [`OleDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.OleDLL).11961197[`__getattr__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__getattr__) обладает особым поведением: позволяет загрузить разделяемую библиотеку, обращаясь к ней как к атрибуту экземпляра загрузчика библиотек. Результат кэшируется, поэтому повторные обращения к атрибуту каждый раз возвращают ту же библиотеку.11981199#### `LoadLibrary(name)`12001201Загружает разделяемую библиотеку в процесс и возвращает её. Этот метод всегда возвращает новый экземпляр библиотеки.12021203Доступны следующие готовые загрузчики библиотек:12041205#### `ctypes.cdll`12061207Создаёт экземпляры [`CDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.CDLL).12081209#### `ctypes.windll`12101211Только для Windows: создаёт экземпляры [`WinDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.WinDLL).12121213#### `ctypes.oledll`12141215Только для Windows: создаёт экземпляры [`OleDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.OleDLL).12161217#### `ctypes.pydll`12181219Создаёт экземпляры [`PyDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.PyDLL).12201221Для прямого доступа к C Python API доступен готовый к использованию объект общей библиотеки Python:12221223#### `ctypes.pythonapi`12241225Экземпляр [`PyDLL`](https://python-all.ru/3.2/library/ctypes.html#ctypes.PyDLL), который предоставляет функции C API Python в качестве атрибутов. Обратите внимание, что все эти функции предполагаются возвращающими `int` (C int), что, конечно, не всегда верно, поэтому для их использования необходимо задать правильный атрибут `restype`.12261227### 15.17.2.3. Внешние функции12281229Как объяснялось в предыдущем разделе, внешние функции можно получить как атрибуты загруженных разделяемых библиотек. Созданные таким образом объекты функций по умолчанию принимают любое количество аргументов, принимают любые экземпляры данных ctypes в качестве аргументов и возвращают тип результата по умолчанию, указанный загрузчиком библиотеки. Они являются экземплярами закрытого класса:12301231#### `class ctypes._FuncPtr`12321233Базовый класс для внешних функций, вызываемых из C.12341235Экземпляры внешних функций также являются C-совместимыми типами данных; они представляют указатели на функции C.12361237Это поведение можно настроить, присвоив значения специальным атрибутам объекта внешней функции.12381239#### `restype`12401241Присваивает тип ctypes для указания типа результата внешней функции. Используйте `None` для `void` – функции, ничего не возвращающей.12421243Можно назначить вызываемый объект Python, который не является типом ctypes; в этом случае предполагается, что функция возвращает C `int`, и вызываемый объект будет вызван с этим целым числом, что позволяет выполнить дальнейшую обработку или проверку ошибок. Использование этого устарело; для более гибкой постобработки или проверки ошибок используйте тип данных ctypes как [`restype`](https://python-all.ru/3.2/library/ctypes.html#ctypes._FuncPtr.restype) и назначьте вызываемый объект атрибуту [`errcheck`](https://python-all.ru/3.2/library/ctypes.html#ctypes._FuncPtr.errcheck).12441245#### `argtypes`12461247Назначьте кортеж типов ctypes, чтобы указать типы аргументов, которые принимает функция. Функции, использующие соглашение о вызове `stdcall`, могут быть вызваны только с тем же количеством аргументов, что и длина этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные неуказанные аргументы.12481249Когда вызывается внешняя функция, каждый фактический аргумент передаётся методу класса `from_param()` элементов кортежа [`argtypes`](https://python-all.ru/3.2/library/ctypes.html#ctypes._FuncPtr.argtypes); этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элемент [`c_char_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char_p) в кортеже [`argtypes`](https://python-all.ru/3.2/library/ctypes.html#ctypes._FuncPtr.argtypes) преобразует строку, переданную как аргумент, в объект bytes, используя правила преобразования ctypes.12501251Новое: теперь можно помещать в argtypes элементы, которые не являются типами ctypes, но каждый элемент должен иметь метод `from_param()`, возвращающий значение, пригодное для использования в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определять адаптеры, которые могут адаптировать пользовательские объекты как параметры функции.12521253#### `errcheck`12541255Этому атрибуту присваивается функция Python или другой вызываемый объект. Вызываемый объект будет вызван с тремя и более аргументами:12561257#### `callable(result, func, arguments)`12581259*result* – это то, что возвращает внешняя функция, как указано атрибутом [`restype`](https://python-all.ru/3.2/library/ctypes.html#ctypes._FuncPtr.restype).12601261*func* – это сам объект внешней функции; это позволяет повторно использовать один и тот же вызываемый объект для проверки или постобработки результатов нескольких функций.12621263*arguments* – это кортеж, содержащий параметры, изначально переданные при вызове функции; это позволяет специализировать поведение в зависимости от использованных аргументов.12641265Объект, возвращаемый этой функцией, будет возвращён из вызова внешней функции, но он также может проверить значение результата и возбудить исключение, если вызов внешней функции завершился неудачей.12661267#### `exception ctypes.ArgumentError`12681269Это исключение возникает, когда вызов внешней функции не может преобразовать один из переданных аргументов.12701271### 15.17.2.4. Прототипы функций12721273Внешние функции также можно создать, инстанцируя прототипы функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове) без определения реализации. Фабричные функции необходимо вызывать с желаемым типом результата и типами аргументов функции.12741275#### `ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)`12761277Возвращаемый прототип функции создаёт функции, использующие стандартное соглашение о вызове C. Функция освобождает GIL во время вызова. Если *use\_errno* установлено в True, частная копия системной переменной [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno) в ctypes обменивается с реальным значением [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno) до и после вызова; *use\_last\_error* делает то же самое для кода ошибки Windows.12781279#### `ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)`12801281Только для Windows: возвращаемый прототип функции создаёт функции, использующие соглашение о вызове `stdcall`, за исключением Windows CE, где [`WINFUNCTYPE()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.WINFUNCTYPE) совпадает с [`CFUNCTYPE()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.CFUNCTYPE). Функция освобождает GIL во время вызова. *use\_errno* и *use\_last\_error* имеют тот же смысл, что и выше.12821283#### `ctypes.PYFUNCTYPE(restype, *argtypes)`12841285Созданный прототип функции создаёт функции, использующие соглашение о вызове Python. Функция *не* освобождает GIL во время вызова.12861287Прототипы функций, созданные этими фабричными функциями, могут быть созданы различными способами, в зависимости от типа и количества параметров в вызове:12881289> #### `prototype(address)`1290>1291> Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.1292>1293> #### `prototype(callable)`1294>1295> Создаёт вызываемую функцию C (функцию обратного вызова) из *вызываемого объекта* Python.1296>1297> #### `prototype(func_spec[, paramflags])`1298>1299> Возвращает внешнюю функцию, экспортируемую общей библиотекой. *func\_spec* должен быть кортежем из двух элементов `(name_or_ordinal, library)`. Первый элемент – это имя экспортируемой функции в виде строки или её порядковый номер (ординал) в виде небольшого целого числа. Второй элемент – это экземпляр общей библиотеки.1300>1301> #### `prototype(vtbl_index, name[, paramflags[, iid]])`1302>1303> Возвращает внешнюю функцию, которая будет вызывать метод COM. *vtbl\_index* – это индекс в таблице виртуальных функций, небольшое неотрицательное целое число. *name* – это имя метода COM. *iid* – необязательный указатель на идентификатор интерфейса, используемый в расширенном отчёте об ошибках.1304>1305> Методы COM используют специальное соглашение о вызове: они требуют указатель на интерфейс COM в качестве первого аргумента, в дополнение к тем параметрам, которые указаны в кортеже `argtypes`.1306>1307> Необязательный параметр *paramflags* создаёт обёртки внешних функций с гораздо большей функциональностью, чем описано выше.1308>1309> *paramflags* должен быть кортежем той же длины, что и `argtypes`.1310>1311> Каждый элемент этого кортежа содержит дополнительную информацию о параметре; он должен быть кортежем, содержащим один, два или три элемента.1312>1313> Первый элемент – целое число, содержащее комбинацию флагов направления для параметра:1314>1315> > **1**1316> >1317> > Задаёт входной параметр функции.1318> >1319> > **2**1320> >1321> > Выходной параметр. Внешняя функция заполняет значение.1322> >1323> > **4**1324> >1325> > Входной параметр, по умолчанию равный нулю.1326>1327> Необязательный второй элемент – имя параметра в виде строки. Если он указан, внешнюю функцию можно вызывать с именованными параметрами.1328>1329> Необязательный третий элемент – значение по умолчанию для этого параметра.13301331Этот пример демонстрирует, как обернуть функцию Windows `MessageBoxA` так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление C из заголовочного файла Windows выглядит так:13321333```python1334WINUSERAPI int WINAPI1335MessageBoxA(1336    HWND hWnd ,1337    LPCSTR lpText,1338    LPCSTR lpCaption,1339    UINT uType);1340```13411342Вот обёртка с помощью [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes):13431344```python1345>>> from ctypes import c_int, WINFUNCTYPE, windll1346>>> from ctypes.wintypes import HWND, LPCSTR, UINT1347>>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)1348>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)1349>>> MessageBox = prototype(("MessageBoxA", windll.user32), paramflags)1350>>>1351```13521353Теперь внешнюю функцию MessageBox можно вызывать следующими способами:13541355```python1356>>> MessageBox()1357>>> MessageBox(text="Spam, spam, spam")1358>>> MessageBox(flags=2, text="foo bar")1359>>>1360```13611362Второй пример демонстрирует выходные параметры. Функция win32 `GetWindowRect` получает размеры указанного окна, копируя их в структуру `RECT`, которую должен предоставить вызывающий код. Вот объявление C:13631364```python1365WINUSERAPI BOOL WINAPI1366GetWindowRect(1367     HWND hWnd,1368     LPRECT lpRect);1369```13701371Вот обёртка с помощью [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes):13721373```python1374>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError1375>>> from ctypes.wintypes import BOOL, HWND, RECT1376>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))1377>>> paramflags = (1, "hwnd"), (2, "lprect")1378>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)1379>>>1380```13811382Функции с выходными параметрами автоматически возвращают значение выходного параметра, если он один, или кортеж значений выходных параметров, если их несколько; таким образом, при вызове функция GetWindowRect теперь возвращает экземпляр RECT.13831384Выходные параметры можно комбинировать с протоколом `errcheck` для дальнейшей обработки вывода и проверки ошибок. Функция win32 API `GetWindowRect` возвращает `BOOL` для сигнализации об успехе или неудаче, поэтому эта функция могла бы выполнять проверку ошибок и возбуждать исключение при сбое вызова API:13851386```python1387>>> def errcheck(result, func, args):1388...     if not result:1389...         raise WinError()1390...     return args1391...1392>>> GetWindowRect.errcheck = errcheck1393>>>1394```13951396Если функция `errcheck` возвращает полученный кортеж аргументов без изменений, [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) продолжает обычную обработку выходных параметров. Если требуется вернуть кортеж координат окна вместо экземпляра `RECT`, можно извлечь поля в функции и вернуть их вместо этого; обычная обработка выполняться не будет.13971398```python1399>>> def errcheck(result, func, args):1400...     if not result:1401...         raise WinError()1402...     rc = args[1]1403...     return rc.left, rc.top, rc.bottom, rc.right1404...1405>>> GetWindowRect.errcheck = errcheck1406>>>1407```14081409### 15.17.2.5. Вспомогательные функции14101411#### `ctypes.addressof(obj)`14121413Возвращает адрес буфера памяти в виде целого числа. *obj* должен быть экземпляром типа ctypes.14141415#### `ctypes.alignment(obj_or_type)`14161417Возвращает требования к выравниванию для типа ctypes. *obj\_or\_type* должен быть типом ctypes или его экземпляром.14181419#### `ctypes.byref(obj[, offset])`14201421Возвращает легковесный указатель на *obj*, который должен быть экземпляром типа ctypes. Параметр *offset* по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему значению указателя.14221423`byref(obj, offset)` соответствует следующему коду на C:14241425```python1426(((char *)&obj) + offset)1427```14281429Возвращаемый объект можно использовать только как параметр вызова внешней функции. Он ведёт себя аналогично `pointer(obj)`, но создаётся намного быстрее.14301431#### `ctypes.cast(obj, type)`14321433Эта функция аналогична оператору приведения типов в C. Она возвращает новый экземпляр *type*, указывающий на тот же блок памяти, что и *obj*. *type* должен быть типом-указателем, а *obj* – объектом, который можно интерпретировать как указатель.14341435#### `ctypes.create_string_buffer(init_or_size, size=None)`14361437Эта функция создаёт изменяемый символьный буфер. Возвращаемый объект – это массив ctypes из [`c_char`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char).14381439*init\_or\_size* должен быть целым числом, задающим размер массива, или объектом bytes, который будет использоваться для инициализации элементов массива.14401441Если первым аргументом указан объект bytes, буфер создаётся на один элемент больше его длины, чтобы последним элементом массива был нулевой символ (NUL). Вторым аргументом можно передать целое число, которое позволяет задать размер массива, если длина bytes не должна использоваться.14421443#### `ctypes.create_unicode_buffer(init_or_size, size=None)`14441445Эта функция создаёт изменяемый буфер символов Unicode. Возвращаемый объект – это массив ctypes из [`c_wchar`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_wchar).14461447*init\_or\_size* должен быть целым числом, задающим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.14481449Если первым аргументом указана строка, буфер создаётся на один элемент больше её длины, чтобы последним элементом массива был нулевой символ (NUL). Вторым аргументом можно передать целое число, которое позволяет задать размер массива, если длина строки не должна использоваться.14501451#### `ctypes.DllCanUnloadNow()`14521453Только Windows: эта функция является хуком, который позволяет реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllCanUnloadNow, которую экспортирует DLL расширения \_ctypes.14541455#### `ctypes.DllGetClassObject()`14561457Только Windows: эта функция является хуком, который позволяет реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllGetClassObject, которую экспортирует DLL расширения `_ctypes`.14581459#### `ctypes.util.find_library(name)`14601461Пытается найти библиотеку и возвращает путь к ней. *name* – это имя библиотеки без префикса вроде `lib`, суффикса вроде `.so`, `.dylib` или номера версии (в такой форме имя используется для опции компоновщика в POSIX *-l*). Если библиотеку найти не удалось, возвращается `None`.14621463Точное поведение зависит от системы.14641465#### `ctypes.util.find_msvcrt()`14661467Только для Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки не удаётся определить, возвращается `None`.14681469Если необходимо освободить память, выделенную, например, модулем расширения с помощью вызова `free(void *)`, важно использовать функцию из той же библиотеки, которая выделила память.14701471#### `ctypes.FormatError([code])`14721473Только для Windows: возвращает текстовое описание кода ошибки *code*. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows API GetLastError.14741475#### `ctypes.GetLastError()`14761477Только для Windows: Возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция вызывает непосредственно функцию Windows *GetLastError()*; она не возвращает внутреннюю для ctypes копию кода ошибки.14781479#### `ctypes.get_errno()`14801481Возвращает текущее значение переменной [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno) (приватной копии ctypes системной переменной errno) в вызывающем потоке.14821483#### `ctypes.get_last_error()`14841485Только Windows: возвращает текущее значение переменной `LastError` (приватной копии ctypes системной переменной LastError) в вызывающем потоке.14861487#### `ctypes.memmove(dst, src, count)`14881489Аналогична стандартной библиотечной функции C memmove: копирует *count* байт из *src* в *dst*. *dst* и *src* должны быть целыми числами или экземплярами ctypes, которые можно преобразовать в указатели.14901491#### `ctypes.memset(dst, c, count)`14921493Аналогична стандартной библиотечной функции C memset: заполняет блок памяти по адресу *dst* *count* байтами значения *c*. *dst* должно быть целым числом, задающим адрес, или экземпляром ctypes.14941495#### `ctypes.POINTER(type)`14961497Эта фабричная функция создаёт и возвращает новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри, поэтому многократный вызов этой функции не требует больших затрат. *type* должен быть типом ctypes.14981499#### `ctypes.pointer(obj)`15001501Эта функция создаёт новый экземпляр указателя, указывающий на *obj*. Возвращаемый объект имеет тип `POINTER(type(obj))`.15021503Примечание: если нужно просто передать указатель на объект в вызов внешней функции, используйте `byref(obj)` – это намного быстрее.15041505#### `ctypes.resize(obj, size)`15061507Эта функция изменяет размер внутреннего буфера памяти объекта *obj*, который должен быть экземпляром типа ctypes. Невозможно сделать буфер меньше, чем собственный размер типа объекта, задаваемый выражением `sizeof(type(obj))`, но можно его увеличить.15081509#### `ctypes.set_errno(value)`15101511Устанавливает текущее значение приватной копии системной переменной [`errno`](https://python-all.ru/3.2/library/errno.html#module-errno) в вызывающем потоке в *value* и возвращает предыдущее значение.15121513#### `ctypes.set_last_error(value)`15141515Только для Windows: устанавливает текущее значение приватной копии системной переменной `LastError` в вызывающем потоке в *value* и возвращает предыдущее значение.15161517#### `ctypes.sizeof(obj_or_type)`15181519Возвращает размер в байтах буфера памяти ctypes-типа или экземпляра. Делает tо же самое, что и функция C `sizeof()`.15201521#### `ctypes.string_at(address, size=-1)`15221523Эта функция возвращает C-строку, начинающуюся по адресу памяти *address*, в виде объекта bytes. Если указан параметр size, он используется как размер; в противном случае строка считается завершающейся нулевым символом.15241525#### `ctypes.WinError(code=None, descr=None)`15261527Только для Windows: эта функция, вероятно, самая неудачно названная вещь в ctypes. Она создаёт экземпляр WindowsError. Если *code* не указан, вызывается `GetLastError` для определения кода ошибки. Если *descr* не указан, вызывается [`FormatError()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.FormatError) для получения текстового описания ошибки.15281529#### `ctypes.wstring_at(address, size=-1)`15301531Эта функция возвращает строку широких символов, начинающуюся по адресу памяти *address*, в виде строки. Если указан *size*, он используется как количество символов строки, в противном случае строка считается завершающейся нулевым символом.15321533### 15.17.2.6. Типы данных15341535#### `class ctypes._CData`15361537Этот непубличный класс является общим базовым классом для всех типов данных ctypes. Среди прочего, все экземпляры типов ctypes содержат блок памяти, который хранит данные, совместимые с C; адрес блока памяти возвращается вспомогательной функцией [`addressof()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.addressof). Ещё одна переменная экземпляра представлена как [`_objects`](https://python-all.ru/3.2/library/ctypes.html#ctypes._CData._objects); она содержит другие объекты Python, которые необходимо сохранять в памяти на случай, если блок памяти содержит указатели.15381539Общие методы типов данных ctypes, все они являются методами класса (если точнее, это методы [*метакласса*](https://python-all.ru/3.2/glossary.html#term-metaclass)):15401541#### `from_buffer(source[, offset])`15421543Этот метод возвращает экземпляр ctypes, который использует тот же буфер, что и объект *source*. Объект *source* должен поддерживать интерфейс буфера с возможностью записи. Необязательный параметр *offset* задаёт смещение в байтах внутри буфера источника; по умолчанию – ноль. Если буфер источника недостаточно велик, возникает исключение [`ValueError`](https://python-all.ru/3.2/library/exceptions.html#ValueError).15441545#### `from_buffer_copy(source[, offset])`15461547Этот метод создаёт экземпляр ctypes, копируя буфер из объекта *source*, который должен быть доступен для чтения. Необязательный параметр *offset* задаёт смещение в байтах внутри буфера источника; по умолчанию – ноль. Если буфер источника недостаточно велик, возникает исключение [`ValueError`](https://python-all.ru/3.2/library/exceptions.html#ValueError).15481549#### `from_address(address)`15501551Этот метод возвращает экземпляр типа ctypes, использующий память, указанную *address*, который должен быть целым числом.15521553#### `from_param(obj)`15541555Этот метод приводит *obj* к типу ctypes. Он вызывается с фактическим объектом, используемым в вызове внешней функции, когда тип присутствует в кортеже `argtypes` этой функции; он должен вернуть объект, который можно использовать в качестве параметра вызова функции.15561557Все типы данных ctypes имеют реализацию этого метода класса по умолчанию, которая обычно возвращает *obj*, если он является экземпляром этого типа. Некоторые типы также принимают другие объекты.15581559#### `in_dll(library, name)`15601561Этот метод возвращает экземпляр типа ctypes, экспортируемый общей библиотекой. *name* – это имя символа, экспортирующего данные; *library* – загруженная общая библиотека.15621563Общие переменные экземпляра типов данных ctypes:15641565#### `_b_base_`15661567Иногда экземпляры данных ctypes не владеют блоком памяти, который содержат, а вместо этого разделяют часть блока памяти базового объекта. Член только для чтения [`_b_base_`](https://python-all.ru/3.2/library/ctypes.html#ctypes._CData._b_base_) является корневым объектом ctypes, которому принадлежит блок памяти.15681569#### `_b_needsfree_`15701571Эта переменная только для чтения равна true, когда экземпляр данных ctypes сам выделил блок памяти, и false в противном случае.15721573#### `_objects`15741575Этот член может быть либо `None`, либо словарём, содержащим объекты Python, которые необходимо сохранять в памяти, чтобы содержимое блока памяти оставалось корректным. Этот объект предоставляется только для отладки; никогда не изменяйте содержимое этого словаря.15761577### 15.17.2.7. Фундаментальные типы данных15781579#### `class ctypes._SimpleCData`15801581Этот непубличный класс является базовым классом для всех фундаментальных типов данных ctypes. Он упоминается здесь, потому что содержит общие атрибуты фундаментальных типов данных ctypes. [`_SimpleCData`](https://python-all.ru/3.2/library/ctypes.html#ctypes._SimpleCData) является подклассом [`_CData`](https://python-all.ru/3.2/library/ctypes.html#ctypes._CData), поэтому наследует их методы и атрибуты. Типы данных ctypes, которые не являются указателями и не содержат указателей, теперь можно сериализовать.15821583Экземпляры имеют один атрибут:15841585#### `value`15861587Этот атрибут содержит фактическое значение экземпляра. Для целочисленных типов и типов указателей это целое число, для символьных типов – объект bytes из одного символа или строка, для символьных указателей – объект Python bytes или строка.15881589When the `value` attribute is retrieved from a ctypes instance, usually a new object is returned each time. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) does *not* implement original object return, always a new object is constructed. The same is true for all other ctypes object instances.15901591Базовые типы данных при возврате из вызовов внешних функций или, например, при получении полей структур или элементов массивов прозрачно преобразуются в нативные типы Python. Иными словами, если внешняя функция имеет `restype` [`c_char_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char_p), вы всегда получите объект Python bytes, *а не* экземпляр [`c_char_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char_p).15921593Подклассы базовых типов данных *не* наследуют это поведение. Так, если `restype` внешней функции является подклассом [`c_void_p`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_void_p), то из вызова функции вы получите экземпляр этого подкласса. Разумеется, значение указателя можно получить, обратившись к атрибуту `value`.15941595Вот фундаментальные типы данных ctypes:15961597#### `class ctypes.c_byte`15981599Представляет тип данных C `signed char` и интерпретирует значение как небольшое целое число. Конструктор принимает необязательный инициализатор целого типа; проверка переполнения не выполняется.16001601#### `class ctypes.c_char`16021603Представляет тип данных C `char` и интерпретирует значение как один символ. Конструктор принимает необязательный строковый инициализатор; длина строки должна быть ровно один символ.16041605#### `class ctypes.c_char_p`16061607Представляет тип данных C `char *`, когда он указывает на строку, завершающуюся нулевым символом. Для обычного указателя на символ, который может также указывать на бинарные данные, следует использовать `POINTER(c_char)`. Конструктор принимает целочисленный адрес или объект bytes.16081609#### `class ctypes.c_double`16101611Представляет тип данных C `double`. Конструктор принимает необязательный инициализатор с плавающей запятой.16121613#### `class ctypes.c_longdouble`16141615Представляет тип данных C `long double`. Конструктор принимает необязательный инициализатор с плавающей запятой. На платформах, где `sizeof(long double) == sizeof(double)`, он является псевдонимом [`c_double`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_double).16161617#### `class ctypes.c_float`16181619Представляет тип данных C `float`. Конструктор принимает необязательный инициализатор с плавающей запятой.16201621#### `class ctypes.c_int`16221623Представляет тип данных C `signed int`. Конструктор принимает необязательный инициализатор целого типа; проверка переполнения не выполняется. На платформах, где `sizeof(int) == sizeof(long)`, он является псевдонимом [`c_long`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_long).16241625#### `class ctypes.c_int8`16261627Представляет 8-битный тип данных C `signed int`. Обычно является псевдонимом [`c_byte`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_byte).16281629#### `class ctypes.c_int16`16301631Представляет 16-битный тип данных C `signed int`. Обычно является псевдонимом [`c_short`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_short).16321633#### `class ctypes.c_int32`16341635Представляет 32-битный тип данных C `signed int`. Обычно является псевдонимом [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int).16361637#### `class ctypes.c_int64`16381639Представляет 64-битный тип данных C `signed int`. Обычно является псевдонимом [`c_longlong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_longlong).16401641#### `class ctypes.c_long`16421643Представляет тип данных C `signed long`. Конструктор принимает необязательный инициализатор целого типа; проверка переполнения не выполняется.16441645#### `class ctypes.c_longlong`16461647Представляет тип данных C `signed long long`. Конструктор принимает необязательный инициализатор целого типа; проверка переполнения не выполняется.16481649#### `class ctypes.c_short`16501651Представляет тип данных C `signed short`. Конструктор принимает необязательный инициализатор целого типа; проверка переполнения не выполняется.16521653#### `class ctypes.c_size_t`16541655Представляет тип данных C `size_t`.16561657#### `class ctypes.c_ssize_t`16581659Представляет тип данных C `ssize_t`.16601661Новое в версии 3.2.16621663#### `class ctypes.c_ubyte`16641665Представляет тип данных C `unsigned char`, интерпретирует значение как целое число небольшого размера. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.16661667#### `class ctypes.c_uint`16681669Представляет тип данных C `unsigned int`. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где `sizeof(int) == sizeof(long)`, это псевдоним для [`c_ulong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ulong).16701671#### `class ctypes.c_uint8`16721673Представляет 8-битный тип данных C `unsigned int`. Обычно это псевдоним для [`c_ubyte`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ubyte).16741675#### `class ctypes.c_uint16`16761677Представляет 16-битный тип данных C `unsigned int`. Обычно это псевдоним для [`c_ushort`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ushort).16781679#### `class ctypes.c_uint32`16801681Представляет 32-битный тип данных C `unsigned int`. Обычно это псевдоним для [`c_uint`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_uint).16821683#### `class ctypes.c_uint64`16841685Представляет 64-битный тип данных C `unsigned int`. Обычно это псевдоним для [`c_ulonglong`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_ulonglong).16861687#### `class ctypes.c_ulong`16881689Представляет тип данных C `unsigned long`. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.16901691#### `class ctypes.c_ulonglong`16921693Представляет тип данных C `unsigned long long`. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.16941695#### `class ctypes.c_ushort`16961697Представляет тип данных C `unsigned short`. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.16981699#### `class ctypes.c_void_p`17001701Представляет тип C `void *`. Значение представляется как целое число. Конструктор принимает необязательный целочисленный инициализатор.17021703#### `class ctypes.c_wchar`17041705Представляет тип данных C `wchar_t`, интерпретирует значение как строку unicode из одного символа. Конструктор принимает необязательный строковый инициализатор; длина строки должна быть ровно один символ.17061707#### `class ctypes.c_wchar_p`17081709Представляет тип данных C `wchar_t *`, который должен быть указателем на завершающуюся нулём строку широких символов. Конструктор принимает целочисленный адрес или строку.17101711#### `class ctypes.c_bool`17121713Представляет тип данных C `bool` (точнее, `_Bool` из C99). Его значением может быть True или False, а конструктор принимает любой объект, имеющий логическое значение.17141715#### `class ctypes.HRESULT`17161717Только Windows: представляет значение `HRESULT`, содержащее информацию об успехе или ошибке вызова функции или метода.17181719#### `class ctypes.py_object`17201721Представляет тип данных C [`PyObject *`](https://python-all.ru/3.2/c-api/structures.html#PyObject). Вызов без аргумента создаёт указатель `NULL` [`PyObject *`](https://python-all.ru/3.2/c-api/structures.html#PyObject).17221723Модуль `ctypes.wintypes` предоставляет ряд других специфичных для Windows типов данных, например `HWND`, `WPARAM` или `DWORD`. Также определены некоторые полезные структуры, такие как `MSG` или `RECT`.17241725### 15.17.2.8. Структурные типы данных17261727#### `class ctypes.Union(*args, **kw)`17281729Абстрактный базовый класс для объединений в собственном порядке байт.17301731#### `class ctypes.BigEndianStructure(*args, **kw)`17321733Абстрактный базовый класс для структур в *big endian* порядке байтов.17341735#### `class ctypes.LittleEndianStructure(*args, **kw)`17361737Абстрактный базовый класс для структур в *little endian* порядке байтов.17381739Структуры с нестандартным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.17401741#### `class ctypes.Structure(*args, **kw)`17421743Абстрактный базовый класс для структур в *нативном* порядке байтов.17441745Конкретные типы структур и объединений должны создаваться через создание подкласса одного из этих tипов и как минимум определить переменную класса [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_). [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) создаст [*дескрипторы*](https://python-all.ru/3.2/glossary.html#term-descriptor), которые позволяют читать и записывать поля через прямой доступ к атрибутам. Это17461747#### `_fields_`17481749Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент – имя поля, второй элемент задает тип поля; это может быть любой тип данных ctypes.17501751Для полей целочисленных типов, таких как [`c_int`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_int), может быть указан третий необязательный элемент. Он должен быть небольшим положительным целым числом, определяющим разрядность поля.17521753Имена полей должны быть уникальными в пределах одной структуры или объединения. Это не проверяется, но при повторении имен доступно только одно поле.17541755Переменную класса [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_) можно определить *после* оператора class, который определяет подкласс Structure; это позволяет создавать типы данных, которые прямо или косвенно ссылаются сами на себя:17561757```python1758class List(Structure):1759    pass1760List._fields_ = [("pnext", POINTER(List)),1761                 ...1762                ]1763```17641765Однако переменная класса [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_) должна быть определена до первого использования типа (создания экземпляра, вызова [`sizeof()`](https://python-all.ru/3.2/library/ctypes.html#ctypes.sizeof) для него и т.д.). Последующие присваивания переменной класса [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_) вызовут AttributeError.17661767Конструкторы подклассов Structure и Union принимают как позиционные, так и именованные аргументы. Позиционные аргументы используются для инициализации полей в том же порядке, в котором они указаны в определении [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_), а именованные аргументы используются для инициализации полей с соответствующим именем.17681769Можно определять подподклассы типов структур; они наследуют поля базового класса плюс поля из [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_), определённые в подподклассе, если таковые имеются.17701771#### `_pack_`17721773Необязательное небольшое целое число, позволяющее переопределить выравнивание полей структуры в экземпляре. [`_pack_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._pack_) должен быть уже определён, когда присваивается [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_), иначе это не будет иметь эффекта.17741775#### `_anonymous_`17761777Необязательная последовательность, перечисляющая имена безымянных (анонимных) полей. [`_anonymous_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._anonymous_) должен быть уже определён, когда присваивается [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_), иначе это не будет иметь эффекта.17781779Поля, перечисленные в этой переменной, должны быть полями типа структуры или объединения. [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) создаст в типе структуры дескрипторы, которые позволяют напрямую обращаться к вложенным полям без необходимости создавать поле структуры или объединения.17801781Вот пример типа (Windows):17821783```python1784class _U(Union):1785    _fields_ = [("lptdesc", POINTER(TYPEDESC)),1786                ("lpadesc", POINTER(ARRAYDESC)),1787                ("hreftype", HREFTYPE)]17881789class TYPEDESC(Structure):1790    _anonymous_ = ("u",)1791    _fields_ = [("u", _U),1792                ("vt", VARTYPE)]1793```17941795Структура `TYPEDESC` описывает тип данных COM; поле `vt` определяет, какое из полей объединения является действительным. Поскольку поле `u` определено как анонимное, теперь можно обращаться к членам напрямую через экземпляр TYPEDESC. `td.lptdesc` и `td.u.lptdesc` эквивалентны, но первый вариант быстрее, так как не требует создания временного экземпляра объединения:17961797```python1798td = TYPEDESC()1799td.vt = VT_PTR1800td.lptdesc = POINTER(some_type)1801td.u.lptdesc = POINTER(some_type)1802```18031804Можно определять подподклассы структур; они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_), то поля, указанные в ней, добавляются к полям базового класса.18051806Конструкторы структур и объединений принимают как позиционные, так и именованные аргументы. Позиционные аргументы используются для инициализации полей-членов в том же порядке, в котором они появляются в [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_). Именованные аргументы в конструкторе интерпретируются как присваивания атрибутов, поэтому они инициализируют [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_) с тем же именем или создают новые атрибуты для имён, отсутствующих в [`_fields_`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure._fields_).18071808### 15.17.2.9. Массивы и указатели18091810Пока не написано – пожалуйста, обратитесь к разделам [*Pointers*](https://python-all.ru/3.2/library/ctypes.html#ctypes-pointers) и [*Arrays*](https://python-all.ru/3.2/library/ctypes.html#ctypes-arrays) в руководстве.1811