ctypes --- کتابخانهای از توابع خارجی برای پایتون¶
کد منبع: Lib/ctypes
ctypes یک کتابخانه برای توابع خارجی در پایتون است. این کتابخانه انواع دادهی سازگار با C را فراهم میکند و فراخوانی توابع در DLLها یا کتابخانههای مشترک را ممکن میسازد. میتوان از آن برای دربرگرفتن این کتابخانهها در پایتون خالص استفاده کرد.
این یک ماژول اختیاری است. اگر این ماژول در نسخه CPython شما موجود نیست، به مستندات توزیعکننده خود (یعنی هرکسی که پایتون را در اختیار شما قرار داده است) مراجعه کنید. اگر شما توزیعکننده هستید، نیازمندیهای ماژولهای اختیاری را ببینید.
هشدار
ctypes دسترسی سطح پایین به کتابخانههای بومی و حافظهی فرایند را فراهم میکند، سازوکارهای ایمنی پایتون را دور میزند و امکان اجرای کد بومی دلخواه را میدهد. استفادهی نادرست میتواند دادهها و اشیاء را تخریب کند، اطلاعات حساس را افشا کند، باعث فروپاشی شود یا بهگونهای دیگر فرایند در حال اجرا را به خطر بیندازد.
آموزش ctypes¶
نکته: برخی نمونهکدها به نوع c_int در ctypes ارجاع میدهند. در سکوهایی که sizeof(long) == sizeof(int) است، این یک نام مستعار برای c_long است. بنابراین، اگر در شرایطی که انتظار c_int را دارید، c_long چاپ شود، نباید دچار تردید شوید --- این دو در واقع یک نوع هستند.
بارگذاری کتابخانههای پیوند پویا¶
ctypes شیء cdll و در ویندوز اشیای windll و oledll را برای بارگذاری کتابخانههای پیوند پویا اکسپورت میکند.
شما کتابخانهها را با دسترسی به آنها بهعنوان ویژگیهای این اشیاء بارگذاری میکنید. cdll کتابخانههایی را بارگذاری میکند که توابع را با استفاده از قرارداد فراخوانی استاندارد cdecl اکسپورت میکنند، در حالی که کتابخانههای windll توابع را با قرارداد فراخوانی stdcall فراخوانی میکنند. oledll نیز از قرارداد فراخوانی stdcall استفاده میکند و فرض میکند که توابع یک کد خطای ویندوزی HRESULT برمیگردانند. این کد خطا برای پرتاب خودکار یک استثنای OSError در صورت شکست فراخوانی تابع استفاده میشود.
تغییر یافته در نسخهی 3.3: خطاهای ویندوز پیشتر موجب پرتاب WindowsError میشدند، که اکنون نام مستعاری از OSError است.
در اینجا چند مثال برای ویندوز آمده است. توجه داشته باشید که msvcrt کتابخانه استاندارد C مایکروسافت است که شامل بیشتر توابع استاندارد C میشود و از قرارداد فراخوانی cdecl استفاده میکند:
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
ویندوز بهطور خودکار پسوند معمول پرونده .dll را اضافه میکند.
توجه
دسترسی به کتابخانه استاندارد C از طریق cdll.msvcrt باعث استفاده از نسخهای قدیمی از این کتابخانه میشود که ممکن است با نسخهای که پایتون از آن استفاده میکند ناسازگار باشد. در صورت امکان، از قابلیتهای خود پایتون استفاده کنید؛ در غیر این صورت، ماژول msvcrt را ایمپورت و استفاده کنید.
سیستمهای دیگر برای بارگذاری یک کتابخانه به نام پرونده شامل پسوند نیاز دارند، بنابراین نمیتوان از دسترسی به ویژگی برای بارگذاری کتابخانهها استفاده کرد. یا باید از متد LoadLibrary() بارگذارهای dll استفاده شود، یا باید کتابخانه را با ایجاد یک نمونه از CDLL از طریق فراخوانی سازنده بارگذاری کنید.
برای مثال، در لینوکس:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
در macOS:
>>> cdll.LoadLibrary("libc.dylib")
<CDLL 'libc.dylib', handle ... at ...>
>>> libc = CDLL("libc.dylib")
>>> libc
<CDLL 'libc.dylib', handle ... at ...>
دسترسی به توابع از DLLهای بارگذاریشده¶
توابع بهعنوان ویژگیهای اشیای dll قابل دسترسی هستند:
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
توجه داشته باشید که DLLهای سیستمی win32 مانند kernel32 و user32 اغلب نسخههای ANSI و همچنین UNICODE یک تابع را اکسپورت میکنند. نسخه UNICODE با افزودن W به نام اکسپورت میشود، در حالی که نسخه ANSI با افزودن A به نام اکسپورت میشود. تابع GetModuleHandle در win32، که یک دسته ماژول (module handle) را برای نام ماژول دادهشده برمیگرداند، پیشنمونه C زیر را دارد، و از یک ماکرو استفاده میشود تا یکی از آنها را بسته به اینکه UNICODE تعریف شده باشد یا نه، بهعنوان GetModuleHandle در دسترس قرار دهد:
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll تلاش نمیکند یکی از آنها را بهصورت جادویی انتخاب کند، شما باید با مشخص کردن صریح GetModuleHandleA یا GetModuleHandleW به نسخهای که نیاز دارید دسترسی پیدا کنید و سپس آن را بهترتیب با اشیای بایتی یا رشتهای فراخوانی کنید.
گاهی اوقات، DLLها توابعی را با نامهایی اکسپورت میکنند که شناسههای معتبر پایتون نیستند، مانند "??2@YAPAXI@Z". در این صورت باید برای بازیابی تابع از getattr() استفاده کنید:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
در ویندوز، برخی dllها توابع را نه با نام، بلکه با شماره ترتیبی اکسپورت میکنند. میتوان با اندیسدهی به شیء dll با شماره ترتیبی، به این توابع دسترسی پیدا کرد:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
فراخوانی توابع¶
میتوانید این توابع را مانند هر شیء فراخوانیپذیر دیگری در پایتون فراخوانی کنید. این مثال از تابع rand() استفاده میکند که هیچ آرگومانی نمیگیرد و یک عدد صحیح شبهتصادفی برمیگرداند:
>>> print(libc.rand())
1804289383
در ویندوز، میتوانید تابع GetModuleHandleA() را فراخوانی کنید، که یک دسته برای ماژول win32 برمیگرداند (برای فراخوانی آن با اشارهگر NULL، None را بهعنوان تنها آرگومان ارسال کنید):
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError هنگامی پرتاب میشود که یک تابع stdcall را با قرارداد فراخوانی cdecl فراخوانی کنید، یا برعکس:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
برای اطلاع از قرارداد فراخوانی صحیح، باید پروندهی سرآیند C یا مستندات تابعی را که میخواهید فراخوانی کنید، بررسی کنید.
در ویندوز، ctypes از مدیریت استثنای ساختاریافته win32 برای جلوگیری از فروپاشی ناشی از خطاهای حفاظت عمومی هنگام فراخوانی توابع با مقادیر نامعتبر آرگومان استفاده میکند:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
ماژول faulthandler میتواند به اشکالزدایی فروپاشیها، مانند خطاهای قطعهبندی (segmentation faults) ناشی از فراخوانیهای نادرست کتابخانه C کمک کند.
None، اعداد صحیح، اشیای بایتی و رشتههای (یونیکدی) تنها اشیای بومی پایتون هستند که میتوانند مستقیماً بهعنوان پارامتر در این فراخوانیهای تابع استفاده شوند. None بهعنوان اشارهگر NULL در C ارسال میشود، اشیای بایتی و رشتهها بهعنوان اشارهگری به بلوک حافظهای که شامل دادههای آنهاست (char* یا wchar_t*) ارسال میشوند. اعداد صحیح پایتون بهعنوان نوع C int پیشفرض پلتفرم ارسال میشوند، مقدار آنها نقاب میشود تا در نوع C جای گیرد.
پیش از آنکه به فراخوانی توابع با سایر انواع پارامتر بپردازیم، باید دربارهی انواع دادهی ctypes بیشتر بیاموزیم.
انواع داده بنیادی¶
ctypes تعدادی از انواع دادهی اولیهی سازگار با C را تعریف میکند:
نوع ctypes |
نوع C |
نوع پایتون |
|
|---|---|---|---|
_Bool |
|
||
char |
|
|
|
|
|
|
|
char |
|
||
unsigned char |
|
||
short |
|
||
unsigned short |
|
||
int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
unsigned int |
|
||
|
* |
||
|
* |
||
|
* |
||
|
* |
||
long |
|
||
unsigned long |
|
||
long long |
|
||
unsigned long long |
|
||
|
* |
||
* |
|||
|
* |
||
float |
|
||
double |
|
||
long double |
|
||
char* (پایانیافته با NUL) |
|
|
|
wchar_t* (پایانیافته با NUL) |
|
|
|
void* |
|
|
|
|
|||
short int |
|
علاوه بر این، اگر محاسبات مختلط سازگار با IEC 60559 (پیوست G) در هر دو C و libffi پشتیبانی شود، انواع مختلط زیر در دسترس هستند:
نوع ctypes |
نوع C |
نوع پایتون |
|
|---|---|---|---|
float complex |
|
||
double complex |
|
||
long double complex |
|
همهی این نوعها را میتوان با فراخوانی آنها بههمراه یک مقدارده اولیهی اختیاری از نوع و مقدار صحیح ایجاد کرد:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
سازندههای انواع عددی، ورودی را با استفاده از __bool__()، __index__() (برای int)، __float__() یا __complex__() تبدیل میکنند. این بدان معناست که c_bool هر شیء دارای مقدار درستی را میپذیرد:
>>> empty_list = []
>>> c_bool(empty_list)
c_bool(False)
از آنجا که این نوعها تغییرپذیرند، میتوان مقدار آنها را نیز بعداً تغییر داد:
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
انتساب یک مقدار جدید به نمونههایی از انواع اشارهگر c_char_p، c_wchar_p و c_void_p، موقعیت حافظهای را که به آن اشاره میکنند تغییر میدهد، نه محتوای بلوک حافظه را (البته که نه، زیرا اشیاء رشتهای پایتون تغییرناپذیر هستند):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
با این حال، باید مراقب باشید که آنها را به توابعی که انتظار اشارهگرهایی به حافظه تغییرپذیر دارند، ارسال نکنید. اگر به بلوکهای حافظه تغییرپذیر نیاز دارید، ctypes دارای تابع create_string_buffer() است که این بلوکها را به روشهای مختلفی ایجاد میکند. میتوان به محتوای فعلی بلوک حافظه از طریق ویژگی raw دسترسی پیدا کرد (یا آن را تغییر داد)؛ اگر میخواهید بهصورت رشتهای ختمشده با NUL به آن دسترسی داشته باشید، از ویژگی value استفاده کنید:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
تابع create_string_buffer() جایگزین تابع قدیمی c_buffer() شده است (که همچنان بهعنوان یک نام مستعار در دسترس است). برای ایجاد یک بلوک حافظه تغییرپذیر حاوی نویسههای یونیکد از نوع C wchar_t، از تابع create_unicode_buffer() استفاده کنید.
فراخوانی توابع، ادامه¶
توجه داشته باشید که printf در کانال خروجی استاندارد واقعی چاپ میکند، نه در sys.stdout، بنابراین این مثالها فقط در اعلان خط فرمان کار میکنند، نه از داخل IDLE یا PythonWin:
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
>>>
همانطور که پیشتر ذکر شد، همه انواع پایتون بهجز اعداد صحیح، رشتهها و اشیای bytes باید در نوع متناظر ctypes خود قرار داده شوند تا بتوان آنها را به نوع داده C موردنیاز تبدیل کرد:
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
فراخوانی توابع چندآرگومانی (variadic functions)¶
در بسیاری از سکوها، فراخوانی توابع چندآرگومانی آرگومان از طریق ctypes دقیقاً مانند فراخوانی توابع با تعداد ثابت پارامتر است. در برخی سکوها، بهویژه ARM64 برای سکوهای اپل، قرارداد فراخوانی برای توابع چندآرگومانی آرگومان با قرارداد فراخوانی توابع معمولی متفاوت است.
در آن سکوها، لازم است ویژگی argtypes را برای آرگومانهای عادی و غیرچندآرگومانی (non-variadic) تابع تعیین کنید:
libc.printf.argtypes = [ctypes.c_char_p]
از آنجا که تعیین این ویژگی مانع قابلیت حمل نمیشود، توصیه میشود همیشه argtypes را برای تمام توابع چندآرگومانی آرگومان (variadic) مشخص کنید.
فراخوانی توابع با انواع داده سفارشی خودتان¶
همچنین میتوانید تبدیل آرگومان ctypes را سفارشیسازی کنید تا بتوان از نمونههای کلاسهای خودتان بهعنوان آرگومانهای تابع استفاده کرد. ctypes به دنبال ویژگی _as_parameter_ میگردد و از آن بهعنوان آرگومان تابع استفاده میکند. این ویژگی باید عدد صحیح، رشته، بایتها، نمونهای از ctypes، یا شیءای دارای ویژگی _as_parameter_ باشد:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
اگر نمیخواهید دادههای نمونه را در متغیر نمونهی _as_parameter_ ذخیره کنید، میتوانید یک @property تعریف کنید که ویژگی را در صورت درخواست در دسترس قرار میدهد.
مشخص کردن انواع آرگومانهای مورد نیاز (function prototypes)¶
میتوان انواع مورد نیاز برای آرگومانهای توابع اکسپورتشده از DLLها را با تنظیم ویژگی argtypes مشخص کرد.
argtypes باید دنبالهای از انواع داده C باشد (تابع printf() احتمالاً مثال خوبی در اینجا نیست، زیرا بسته به رشته قالب، تعداد متغیر و انواع مختلفی از پارامترها را میگیرد؛ از طرف دیگر، این موضوع برای آزمایش این قابلیت بسیار مفید است):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
مشخص کردن یک قالب، در برابر انواع آرگومان ناسازگار محافظت میکند (درست مانند پیشنمونه یک تابع C) و تلاش میکند آرگومانها را به انواع معتبر تبدیل کند:
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 2: TypeError: 'int' object cannot be interpreted as ctypes.c_char_p
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
اگر کلاسهای خودتان را تعریف کردهاید و آنها را به فراخوانیهای تابع ارسال میکنید، باید برای آنها یک متد کلاس from_param() پیادهسازی کنید تا بتوانید از آنها در دنباله argtypes استفاده کنید. متد کلاس from_param() شیء پایتونی ارسالشده به فراخوانی تابع را دریافت میکند؛ این متد باید بررسی نوع یا هر کاری را که برای اطمینان از قابلقبول بودن این شیء لازم است انجام دهد و سپس خود شیء، ویژگی _as_parameter_ آن، یا هر چیزی را که میخواهید در این حالت بهعنوان آرگومان تابع C ارسال کنید، برگرداند. باز هم، نتیجه باید یک عدد صحیح، رشته، بایتها، یک نمونه از ctypes، یا شیءای با ویژگی _as_parameter_ باشد.
انواع بازگشتی¶
بهطور پیشفرض فرض میشود که توابع نوع int در C را برمیگردانند. سایر انواع بازگشتی را میتوان با تنظیم ویژگی restype شیء تابع مشخص کرد.
پیشنمونهی C برای time() بهصورت time_t time(time_t *) است. از آنجا که time_t ممکن است نوعی متفاوت از نوع بازگشتی پیشفرض int داشته باشد، باید ویژگی restype را مشخص کنید:
>>> libc.time.restype = c_time_t
انواع آرگومانها را میتوان با استفاده از argtypes:: مشخص کرد:
>>> libc.time.argtypes = (POINTER(c_time_t),)
برای فراخوانی تابع با یک اشارهگر NULL بهعنوان اولین آرگومان، از None استفاده کنید:
>>> print(libc.time(None))
1150640792
در اینجا یک مثال پیشرفتهتر آمده است که از تابع strchr() استفاده میکند؛ این تابع یک اشارهگر به رشته و یک نویسه دریافت میکند و اشارهگری به یک رشته برمیگرداند:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
اگر میخواهید از فراخوانیهای ord("x") بالا اجتناب کنید، میتوانید ویژگی argtypes را تنظیم کنید، و آرگومان دوم از یک شیء بایت پایتون تکنویسهای به یک C char تبدیل میشود:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
b'def'
>>>
اگر تابع خارجی یک عدد صحیح برگرداند، همچنین میتوانید از یک شیء فراخوانیپذیر پایتون (برای مثال یک تابع یا یک کلاس) بهعنوان ویژگی restype استفاده کنید. آن شیء فراخوانیپذیر با عدد صحیحی که تابع C برمیگرداند، فراخوانی میشود و نتیجهی این فراخوانی بهعنوان نتیجهی فراخوانی تابع شما استفاده میشود. این کار برای بررسی مقادیر بازگشتی خطا و پرتاب خودکار استثنا مفید است:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError تابعی است که API FormatMessage() ویندوز را برای دریافت نمایش رشتهای یک کد خطا فراخوانی میکند و یک استثنا بازمیگرداند. WinError یک پارامتر اختیاری کد خطا میگیرد؛ اگر از آن استفاده نشود، برای دریافت آن GetLastError() را فراخوانی میکند.
لطفاً توجه داشته باشید که سازوکار بررسی خطای بسیار قدرتمندتری از طریق ویژگی errcheck در دسترس است؛ برای جزئیات، راهنمای مرجع را ببینید.
انتقال اشارهگرها (یا: انتقال پارامترها با ارجاع)¶
گاهی یک تابع API در C انتظار دارد یک اشارهگر به یک نوع داده بهعنوان پارامتر دریافت کند، احتمالاً برای نوشتن در مکان متناظر، یا اگر داده برای ارسال با مقدار بیش از حد بزرگ باشد. این بهعنوان ارسال پارامترها با ارجاع نیز شناخته میشود.
ctypes تابع byref() را اکسپورت میکند که برای ارسال پارامترها بهصورت ارجاعی استفاده میشود. میتوان همان اثر را با تابع pointer() به دست آورد، اگرچه pointer() کار بسیار بیشتری انجام میدهد، زیرا یک شیء اشارهگر واقعی میسازد؛ بنابراین اگر به خود شیء اشارهگر در پایتون نیاز ندارید، استفاده از byref() سریعتر است:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>
ساختارها و اجتماعها (unions)¶
ساختارها و اجتماعها باید از کلاسهای پایه Structure و Union که در ماژول ctypes تعریف شدهاند، ارثبری کنند. هر زیرکلاس باید ویژگی _fields_ را تعریف کند. _fields_ باید فهرستی از تاپلهای ۲تایی باشد که شامل یک نام فیلد و یک نوع فیلد هستند.
نوع فیلد باید یک نوع از ctypes مانند c_int یا هر نوع مشتقشده دیگری از ctypes باشد: ساختار، اجتماع، آرایه، اشارهگر.
در اینجا نمونهای ساده از ساختار POINT آمده است که شامل ۲ عدد صحیح به نامهای x و y است و همچنین نحوه مقداردهی اولیه یک ساختار در سازنده را نشان میدهد:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
با این حال، میتوانید ساختارهای بسیار پیچیدهتری بسازید. یک ساختار میتواند خود با استفاده از یک ساختار بهعنوان نوع فیلد، شامل ساختارهای دیگری باشد.
در اینجا یک ساختار RECT آمده است که شامل دو POINT به نامهای upperleft و lowerright است:
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
همچنین میتوان ساختارهای تودرتو را در سازنده به چند روش مقداردهی اولیه کرد:
>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))
میتوان توصیفگرهای فیلد (descriptor) را از کلاس بازیابی کرد؛ این توصیفگرها برای اشکالزدایی مفید هستند، زیرا میتوانند اطلاعات مفیدی ارائه دهند. CField را ببینید:
>>> POINT.x
<ctypes.CField 'x' type=c_int, ofs=0, size=4>
>>> POINT.y
<ctypes.CField 'y' type=c_int, ofs=4, size=4>
>>>
هشدار
ctypes از ارسال اجتماعها (unions) یا ساختارهایی که فیلدهای بیتی (bit-fields) دارند به توابع بهصورت مقداری پشتیبانی نمیکند. هرچند ممکن است این روش روی x86 ۳۲ بیتی کار کند، اما کارکرد آن در حالت کلی توسط کتابخانه تضمین نمیشود. اجتماعها و ساختارهای دارای فیلدهای بیتی باید همیشه بهصورت اشارهگر به توابع ارسال شوند.
چیدمان ساختار/اجتماع (union)، همترازی و ترتیب بایت¶
بهطور پیشفرض، فیلدهای Structure و Union به همان شیوهای چیدمان میشوند که کامپایلکننده C آنها را چیدمان میکند. با تعیین ویژگی کلاس _layout_ در تعریف زیرکلاس، میتوان این رفتار را بهطور کامل لغو کرد؛ برای جزئیات، مستندات این ویژگی را ببینید.
میتوانید حداکثر ترازبندی را برای فیلدها و/یا برای خود ساختار، بهترتیب با تنظیم ویژگیهای کلاس _pack_ و/یا _align_ مشخص کنید. برای جزئیات، مستندات ویژگیها را ببینید.
ctypes برای ساختارها و اجتماعها از ترتیب بایت بومی استفاده میکند. برای ساختن ساختارها با ترتیب بایت غیربومی، میتوانید از یکی از کلاسهای پایه BigEndianStructure، LittleEndianStructure، BigEndianUnion و LittleEndianUnion استفاده کنید. این کلاسها نمیتوانند شامل فیلدهای اشارهگر باشند.
فیلدهای بیتی در ساختارها و اجتماعها¶
ایجاد ساختارها و اجتماعهای حاوی فیلدهای بیتی امکانپذیر است. فیلدهای بیتی تنها برای فیلدهای عدد صحیح امکانپذیر هستند و پهنای بیتی بهعنوان سومین آیتم در تاپلهای _fields_ مشخص میشود:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<ctypes.CField 'first_16' type=c_int, ofs=0, bit_size=16, bit_offset=0>
>>> print(Int.second_16)
<ctypes.CField 'second_16' type=c_int, ofs=0, bit_size=16, bit_offset=16>
توجه داشته باشید که تخصیص و چیدمان میدانهای بیتی در حافظه در استاندارد C تعریف نشدهاند؛ پیادهسازی آنها وابسته به کامپایلر است. بهطور پیشفرض، پایتون تلاش خواهد کرد با رفتار یک کامپایلر «بومی» برای پلتفرم فعلی مطابقت کند. برای جزئیات درباره رفتار پیشفرض و نحوه تغییر آن، ویژگی _layout_ را ببینید.
آرایهها¶
آرایهها دنبالههایی هستند که شامل تعداد ثابتی نمونه از یک نوع میشوند.
روش توصیهشده برای ایجاد انواع آرایه، ضرب یک نوع داده در یک عدد صحیح مثبت است:
TenPointsArrayType = POINT * 10
در اینجا مثالی از یک نوع داده تا حدی مصنوعی آمده است، ساختاری که در میان سایر موارد شامل ۴ POINT است:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
نمونهها به روش معمول، با فراخوانی کلاس ایجاد میشوند:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
کد بالا دنبالهای از سطرهای 0 0 را چاپ میکند، زیرا محتوای آرایه با صفرها مقداردهی اولیه شده است.
همچنین میتوان مقداردههای اولیه از نوع درست را نیز تعیین کرد:
>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>
اشارهگرها¶
نمونههای اشارهگر با فراخوانی تابع pointer() بر روی یک نوع ctypes ایجاد میشوند:
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>
نمونههای اشارهگر دارای یک ویژگی contents هستند که شیء مورد اشارهی اشارهگر را بازمیگرداند، همان شیء i در بالا:
>>> pi.contents
c_long(42)
>>>
توجه داشته باشید که ctypes دارای بازگشت شیء اصلی (OOR) نیست، و هر بار که یک ویژگی را بازیابی میکنید، یک شیء جدید و معادل میسازد:
>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>
انتساب یک نمونه دیگر از c_int به ویژگی contents اشارهگر باعث میشود اشارهگر به مکان حافظهای اشاره کند که این در آن ذخیرهشده است:
>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>
نمونههای اشارهگر همچنین میتوانند با اعداد صحیح اندیسدهی شوند:
>>> pi[0]
99
>>>
انتساب به یک اندیس عدد صحیح، مقدار اشارهشده را تغییر میدهد:
>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>
همچنین میتوانید از اندیسهایی غیر از ۰ استفاده کنید، اما باید بدانید چه کاری انجام میدهید، درست مانند C: میتوانید به مکانهای دلخواه حافظه دسترسی داشته باشید یا آنها را تغییر دهید. بهطور کلی، فقط زمانی از این قابلیت استفاده میکنید که یک اشارهگر از یک تابع C دریافت کردهاید و میدانید که آن اشارهگر در واقع بهجای یک آیتم واحد، به یک آرایه اشاره میکند.
در پشت صحنه، تابع pointer() فراتر از صرف ایجاد نمونههای اشارهگر عمل میکند؛ ابتدا باید انواع اشارهگر را ایجاد کند. این کار با تابع POINTER() انجام میشود که هر نوعی از ctypes را میپذیرد و نوع جدیدی را برمیگرداند:
>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>
فراخوانی نوع اشارهگر بدون آرگومان، یک اشارهگر NULL ایجاد میکند. اشارهگرهای NULL مقدار بولی False دارند:
>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>
ctypes هنگام دسترسی به مقدار اشارهگرها، NULL را بررسی میکند (اما دسترسی به مقدار اشارهگرهای نامعتبر غیرNULL باعث فروپاشی پایتون میشود):
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
ایمنی نخ بدون GIL¶
از پایتون 3.13 به بعد، میتوان GIL را در ساخت نخآزاد (free-threaded build) غیرفعال کرد. در ctypes، خواندن و نوشتن همزمان به یک شیء واحد امن است، اما بین چندین شیء امن نیست:
>>> number = c_int(42) >>> pointer_a = pointer(number) >>> pointer_b = pointer(number)
در مثال بالا، اگر قفل سراسری مفسر (GIL) غیرفعال باشد، تنها در صورتی ایمن است که فقط یک شیء در یک زمان عملیات خواندن و نوشتن در آن نشانی را انجام دهد. بنابراین، pointer_a میتواند بین چندین نخ به اشتراک گذاشته شود و توسط چندین نخ در آن نوشته شود، اما تنها در صورتی که pointer_b نیز سعی در انجام همین کار نداشته باشد. اگر این موضوع مسئلهساز است، استفاده از threading.Lock را برای همگامسازی دسترسی به حافظه در نظر بگیرید:
>>> import threading >>> lock = threading.Lock() >>> # Thread 1 >>> with lock: ... pointer_a.contents = 24 >>> # Thread 2 >>> with lock: ... pointer_b.contents = 42
تبدیل نوعها¶
معمولاً ctypes بررسی سختگیرانهی نوع را انجام میدهد. این بدان معناست که اگر POINTER(c_int) را در فهرست argtypes یک تابع یا بهعنوان نوع یک فیلد عضو در تعریف ساختار داشته باشید، تنها نمونههایی که دقیقاً از همان نوع هستند پذیرفته میشوند. استثناهایی بر این قاعده وجود دارد که در آنها ctypes اشیای دیگر را میپذیرد. برای مثال، میتوانید نمونههای آرایهی سازگار را بهجای انواع اشارهگر ارسال کنید. بنابراین، برای POINTER(c_int)، ctypes آرایهای از c_int را میپذیرد:
>>> class Bar(Structure):
... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
... print(bar.values[i])
...
1
2
3
>>>
علاوه بر این، اگر یک آرگومان تابع بهصراحت بهعنوان یک نوع اشارهگر (مانند POINTER(c_int)) در argtypes اعلان شده باشد، میتوان یک شیء از نوع اشارهشده (در این مورد c_int) را به تابع ارسال کرد. در این حالت، ctypes تبدیل byref() لازم را بهطور خودکار اعمال میکند.
برای تنظیم یک فیلد از نوع POINTER به NULL، میتوانید None را انتساب دهید:
>>> bar.values = None
>>>
گاهی اوقات نمونههایی از انواع ناسازگار دارید. در C، میتوانید یک نوع را به نوع دیگری تبدیل کنید. ctypes تابع cast() را ارائه میدهد که میتوان از آن به همان روش استفاده کرد. ساختار Bar تعریفشده در بالا، اشارهگرهای POINTER(c_int) یا آرایههای c_int را برای فیلد values خود میپذیرد، اما نمونههایی از انواع دیگر را نمیپذیرد:
>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>
برای این موارد، تابع cast() مفید است.
از تابع cast() میتوان برای تبدیل یک نمونه ctypes به اشارهگری به یک نوع داده ctypes متفاوت استفاده کرد. cast() دو پارامتر میگیرد: یک شیء ctypes که اشارهگر است یا میتواند به اشارهگری از یک نوع تبدیل شود، و یک نوع اشارهگر ctypes. این تابع یک نمونه از آرگومان دوم را برمیگرداند که به همان بلوک حافظهی آرگومان اول ارجاع میدهد:
>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>
بنابراین، میتوان از cast() برای انتساب ساختار به فیلد values در Bar استفاده کرد:
>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
انواع ناقص¶
انواع ناقص ساختارها، اجتماعها یا آرایههایی هستند که اعضای آنها هنوز مشخص نشدهاند. در C، آنها با اعلانهای پیشرو (forward declarations) مشخص میشوند، که بعداً تعریف میشوند:
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
ترجمه مستقیم به کد ctypes به این صورت خواهد بود، اما کار نمیکند:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
زیرا class cell جدید در خود دستور کلاس در دسترس نیست. در ctypes، میتوانیم کلاس cell را تعریف کنیم و ویژگی _fields_ را بعداً، پس از دستور کلاس، تنظیم کنیم:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
بیایید آن را امتحان کنیم. دو نمونه از cell میسازیم و کاری میکنیم که آنها به یکدیگر اشاره کنند، و در نهایت زنجیرهی اشارهگرها را چند بار دنبال میکنیم:
>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
... print(p.name, end=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
توابع کالبک¶
ctypes امکان ایجاد اشارهگرهای تابع فراخوانیپذیر از C را از روی اشیاء فراخوانیپذیر پایتون فراهم میکند. به این موارد گاهی توابع کالبک گفته میشود.
ابتدا باید یک کلاس برای تابع کالبک ایجاد کنید. این کلاس قرارداد فراخوانی، نوع بازگشتی، و تعداد و انواع آرگومانهایی را که این تابع دریافت خواهد کرد، میداند.
تابع کارخانهای CFUNCTYPE()، انواعی برای توابع کالبک با استفاده از قرارداد فراخوانی cdecl ایجاد میکند. در ویندوز، تابع کارخانهای WINFUNCTYPE() انواعی برای توابع کالبک با استفاده از قرارداد فراخوانی stdcall ایجاد میکند.
هر دوی این توابع کارخانهای با نوع نتیجه بهعنوان اولین آرگومان و انواع آرگومانهای مورد انتظار برای توابع کالبک بهعنوان آرگومانهای باقیمانده فراخوانی میشوند.
در اینجا مثالی را ارائه میدهم که از تابع qsort() از کتابخانه استاندارد C استفاده میکند؛ این تابع برای مرتبسازی آیتمها با کمک یک تابع کالبک به کار میرود. از qsort() برای مرتبسازی آرایهای از اعداد صحیح استفاده خواهد شد:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort() باید با یک اشارهگر به دادهها برای مرتبسازی، تعداد آیتمهای آرایه داده، اندازه یک آیتم، و یک اشارهگر به تابع مقایسه، یعنی کالبک، فراخوانی شود. سپس کالبک با دو اشارهگر به آیتمها فراخوانی میشود و باید اگر آیتم اول کوچکتر از آیتم دوم باشد، یک عدد صحیح منفی، اگر برابر باشند، صفر، و در غیر این صورت یک عدد صحیح مثبت برگرداند.
بنابراین تابع کالبک ما اشارهگرهایی به اعداد صحیح دریافت میکند و باید یک عدد صحیح برگرداند. ابتدا type را برای تابع کالبک ایجاد میکنیم:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
برای شروع، در اینجا یک کالبک ساده آمده است که مقادیری را که به آن داده میشود نمایش میدهد:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
نتیجه:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
اکنون میتوانیم عملاً دو آیتم را مقایسه کنیم و نتیجهای مفید برگردانیم:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
همانطور که بهراحتی میتوانیم بررسی کنیم، آرایه ما اکنون مرتب است:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
میتوان از کارخانههای تابع بهعنوان کارخانههای دکوراتور استفاده کرد، بنابراین میتوانیم به این صورت بنویسیم:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
توجه
اطمینان حاصل کنید که تا زمانی که اشیای CFUNCTYPE() از کد C استفاده میشوند، ارجاعها به آنها را نگه میدارید. ctypes این کار را نمیکند، و اگر شما این کار را نکنید، ممکن است آنها زبالهروبی شوند و هنگام انجام یک کالبک، باعث فروپاشی برنامهی شما شوند.
همچنین توجه داشته باشید که اگر تابع کالبک در نخی فراخوانی شود که خارج از کنترل پایتون ایجاد شده است (مثلاً توسط کد خارجی که کالبک را فراخوانی میکند)، ctypes در هر فراخوانی یک نخ پایتون ساختگی جدید ایجاد میکند. این رفتار برای بیشتر اهداف صحیح است، اما به این معناست که مقادیر ذخیرهشده با threading.local در میان کالبکهای مختلف باقی نمیمانند، حتی اگر آن فراخوانیها از همان نخ C انجام شوند.
دسترسی به مقادیر اکسپورتشده از DLLها¶
برخی کتابخانههای اشتراکی نهتنها توابع، بلکه متغیرها را نیز اکسپورت میکنند. نمونهای در خود کتابخانه پایتون Py_Version است، شماره نسخه رانتایم پایتون که در یک عدد صحیح ثابت کدگذاریشده است.
ctypes میتواند با متدهای کلاس in_dll() مربوط به نوع، به مقادیری مانند این دسترسی پیدا کند. pythonapi نمادی از پیش تعریفشده است که دسترسی به API C پایتون را فراهم میکند:
>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> print(hex(version.value))
0x30c00a0
یک مثال گسترده که همچنین کاربرد اشارهگرها را نشان میدهد، به اشارهگر PyImport_FrozenModules اکسپورتشده توسط پایتون دسترسی پیدا میکند.
با نقلقول از مستندات برای آن مقدار:
این اشارهگر بهگونهای مقداردهی اولیه شده است که به آرایهای از رکوردهای
_frozenاشاره کند؛ این آرایه با رکوردی که تمام اعضای آنNULLیا صفر هستند، پایان مییابد. هنگامی که یک ماژول فریز (frozen module) ایمپورت میشود، در این جدول جستجو میشود. کد شخص ثالث میتواند با دستکاری این اشارهگر، مجموعهای از ماژولهای فریز را که بهصورت پویا ایجاد شده است فراهم کند.
بنابراین دستکاری این اشارهگر حتی میتواند مفید باشد. برای محدود کردن اندازهی مثال، فقط نشان میدهیم که چگونه میتوان این جدول را با ctypes خواند:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int),
... ("get_code", POINTER(c_ubyte)), # Function pointer
... ]
...
>>>
ما نوع داده _frozen را تعریف کردهایم، بنابراین میتوانیم اشارهگر به جدول را دریافت کنیم:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>
از آنجا که table یک اشارهگر (pointer) به آرایهای از رکوردهای struct_frozen است، میتوانیم آن را پیمایش کنیم، اما فقط باید مطمئن شویم که حلقه ما خاتمه مییابد، زیرا اشارهگرها اندازه ندارند. دیر یا زود احتمالاً با نقض دسترسی یا چیزی مشابه فرومیپاشند، بنابراین بهتر است هنگامی که به ورودی NULL میرسیم، از حلقه خارج شویم:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
zipimport 12345
>>>
این واقعیت که پایتون استاندارد یک ماژول فریز (frozen module) و یک بسته فریز (frozen package) دارد (که با منفی بودن عضو size مشخص میشود) چندان شناختهشده نیست؛ این موارد فقط برای آزمایش استفاده میشوند. برای مثال، آن را با import __hello__ امتحان کنید.
شگفتیها¶
در ctypes چند حالت مرزی وجود دارد که ممکن است انتظار چیزی غیر از آنچه واقعاً رخ میدهد داشته باشید.
مثال زیر را در نظر بگیرید:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
هم. قطعاً انتظار داشتیم آخرین دستور 3 4 1 2 را چاپ کند. چه اتفاقی افتاد؟ مراحل خط rc.a, rc.b = rc.b, rc.a در بالا به شرح زیر است:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
توجه داشته باشید که temp0 و temp1 اشیایی هستند که هنوز از بافر درونی شیء rc در بالا استفاده میکنند. بنابراین اجرای rc.a = temp0 محتوای بافر temp0 را به بافر rc رونوشت میکند. این کار به نوبه خود، محتوای temp1 را تغییر میکند. بنابراین، آخرین انتساب rc.b = temp1، اثر مورد انتظار را ندارد.
به خاطر داشته باشید که بازیابی زیرشیءها از Structure، Unions و Arrays، زیرشیء را کپی نمیکند، بلکه شیء پوششیای را بازیابی میکند که به بافر زیرین شیء ریشه دسترسی دارد.
مثال دیگری که ممکن است رفتاری متفاوت از آنچه انتظار میرود داشته باشد، این است:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
توجه
مقدار اشیاء نمونهسازیشده از c_char_p فقط میتواند روی بایتها یا اعداد صحیح تنظیم شود.
چرا False چاپ میشود؟ نمونههای ctypes اشیایی هستند که شامل یک بلوک حافظه بههمراه چند descriptor برای دسترسی به محتوای حافظه هستند. ذخیره کردن یک شیء پایتون در بلوک حافظه، خود شیء را ذخیره نمیکند، بلکه contents آن شیء ذخیره میشود. دسترسی دوباره به محتوا، هر بار یک شیء پایتون جدید میسازد!
انواع داده با اندازه متغیر¶
ctypes پشتیبانی تا حدی برای آرایهها و ساختارهای با اندازهی متغیر فراهم میکند.
میتوان از تابع resize() برای تغییر اندازه بافر حافظه یک شیء ctypes موجود استفاده کرد. این تابع شیء را بهعنوان آرگومان اول و اندازه درخواستی بر حسب بایت را بهعنوان آرگومان دوم میگیرد. نمیتوان بلوک حافظه را کوچکتر از بلوک حافظه طبیعی تعیینشده توسط نوع شیء کرد؛ در صورت تلاش برای این کار، استثنای ValueError پرتاب میشود:
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
این خوب و مناسب است، اما چگونه میتوان به المانهای اضافی موجود در این آرایه دسترسی داشت؟ از آنجا که نوع هنوز تنها از ۴ المان آگاه است، هنگام دسترسی به سایر المانها با خطا مواجه میشویم:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
راه دیگر برای استفاده از انواع داده با اندازهی متغیر با ctypes این است که از ماهیت پویا پایتون استفاده کنید و نوع داده را پس از اینکه اندازهی موردنیاز از قبل مشخص شد، بهصورت مورد به مورد (باز)تعریف کنید.
مرجع ctypes¶
توابع خارجی¶
همانطور که در بخش پیشین توضیح داده شد، توابع خارجی بهعنوان ویژگیهای کتابخانههای اشتراکی بارگذاریشده قابل دسترسی هستند. اشیای تابعی که به این روش ایجاد میشوند، بهطور پیشفرض هر تعداد آرگومان را میپذیرند، هر نمونه دادهای از ctypes را بهعنوان آرگومان میپذیرند و نوع نتیجه پیشفرضی که توسط بارگذار کتابخانه مشخص شده است را برمیگردانند.
آنها نمونههایی از یک کلاس محلی خصوصی _FuncPtr هستند (در ctypes در معرض نیست) که از کلاس خصوصی _CFuncPtr ارث میبرد:
>>> import ctypes
>>> lib = ctypes.CDLL(None)
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
True
>>> lib._FuncPtr is ctypes._CFuncPtr
False
- class ctypes._CFuncPtr¶
کلاس پایه برای توابع خارجی فراخوانیپذیر در C.
نمونههای توابع خارجی نیز انواع داده سازگار با C هستند؛ این نمونهها اشارهگرهای توابع C را نشان میدهند.
این رفتار را میتوان با انتساب به ویژگیهای خاص شیء تابع خارجی سفارشی کرد.
- restype¶
برای مشخص کردن نوع نتیجه تابع خارجی، یک نوع ctypes را انتساب کنید. برای void، تابعی که چیزی را برنمیگرداند، از
Noneاستفاده کنید.میتوان یک شیء پایتونی فراخوانیپذیر را که یک نوع ctypes نیست، انتساب داد؛ در این صورت فرض میشود که تابع یک int در C را برمیگرداند و شیء فراخوانیپذیر با این عدد صحیح فراخوانی میشود تا امکان پردازش بیشتر یا بررسی خطا فراهم شود. استفاده از این روش منسوخ است؛ برای پسپردازش یا بررسی خطای انعطافپذیرتر، از یک نوع داده ctypes بهعنوان
restypeاستفاده کنید و یک شیء فراخوانیپذیر را به ویژگیerrcheckانتساب دهید.
- argtypes¶
برای مشخص کردن انواع آرگومانهایی که تابع میپذیرد، یک تاپل از انواع ctypes را اختصاص دهید. توابعی که از قرارداد فراخوانی
stdcallاستفاده میکنند، فقط میتوانند با همان تعداد آرگومانی که برابر با طول این تاپل است، فراخوانی شوند؛ توابعی که از قرارداد فراخوانی C استفاده میکنند، آرگومانهای اضافی و مشخصنشده را نیز میپذیرند.هنگامی که یک تابع خارجی فراخوانی میشود، هر آرگومان واقعی به متد کلاس
from_param()آیتمهای موجود در تاپلargtypesارسال میشود؛ این متد امکان سازگار کردن آرگومان واقعی با شیءای را فراهم میکند که تابع خارجی میپذیرد. برای مثال، یک آیتمc_char_pدر تاپلargtypes، رشتهای را که بهعنوان آرگومان ارسالشده است، با استفاده از قوانین تبدیل ctypes به یک شیء bytes تبدیل میکند.جدید: اکنون میتوان آیتمهایی را در argtypes قرار داد که از انواع ctypes نیستند، اما هر آیتم باید یک متد
from_param()داشته باشد که مقداری قابلاستفاده بهعنوان آرگومان (عدد صحیح، رشته، نمونه ctypes) برمیگرداند. این امکان تعریف آداپتورهایی را میدهد که میتوانند اشیاء سفارشی را بهعنوان پارامترهای تابع سازگار کنند.
- errcheck¶
یک تابع پایتون یا یک شیء فراخوانیپذیر دیگر را به این ویژگی اختصاص دهید. این شیء قابل فراخوانی با سه آرگومان یا بیشتر فراخوانی میشود:
- callable(result, func, arguments)
result چیزی است که تابع خارجی برمیگرداند، همانطور که توسط ویژگی
restypeمشخص شده است.func خودِ شیء تابع خارجی است، این امکان را میدهد که از همان شیء فراخوانیپذیر برای بررسی یا پسپردازش نتایج چندین تابع مجدداً استفاده شود.
arguments یک تاپل حاوی پارامترهایی است که در ابتدا به فراخوانی تابع ارسالشدهاند؛ این امر امکان ویژهسازی رفتار بر اساس آرگومانهای استفادهشده را فراهم میکند.
شیءای که این تابع برمیگرداند، از فراخوانی تابع خارجی برگردانده خواهد شد، اما همچنین میتواند مقدار نتیجه را بررسی کند و در صورت ناموفق بودن فراخوانی تابع خارجی، یک استثنا پرتاب کند.
در ویندوز، هنگامی که یک فراخوانی تابع خارجی یک استثنای سیستمی را پرتاب میکند (برای مثال، بهدلیل نقض دسترسی)، آن استثنا گرفته میشود و با یک استثنای پایتون مناسب جایگزین میگردد. علاوه بر این، یک رویداد حسابرسی ctypes.set_exception با آرگومان code پرتاب میشود که به یک قلاب حسابرسی اجازه میدهد استثنا را با استثنای خود جایگزین کند.
برخی روشهای فراخوانی توابع خارجی و همچنین برخی از توابع این ماژول ممکن است رویداد حسابرسی ctypes.call_function را با آرگومانهای function pointer و arguments پرتاب کنند.
پیشنمونههای تابع¶
همچنین میتوان توابع خارجی را با نمونهسازی از پیشنمونههای تابع (function prototypes) ایجاد کرد. پیشنمونههای تابع مشابه پیشنمونههای تابع در C هستند؛ آنها یک تابع را (نوع بازگشتی، انواع آرگومانها، قرارداد فراخوانی) بدون تعریف پیادهسازی توصیف میکنند. توابع کارخانه باید با نوع نتیجه دلخواه و انواع آرگومانهای تابع فراخوانی شوند و میتوانند بهعنوان کارخانههای دکوراتور استفاده شوند و به همین عنوان، از طریق سینتکس @wrapper به توابع اعمال شوند. برای نمونهها، توابع کالبک را ببینید.
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
پیشنمونه تابع برگرداندهشده، توابعی را ایجاد میکند که از قرارداد فراخوانی استاندارد C استفاده میکنند. تابع در حین فراخوانی، GIL را آزاد میکند. اگر use_errno روی true تنظیم شود، نسخهی خصوصی ctypes از متغیر سیستمی
errnoپیش و پس از فراخوانی با مقدار واقعیerrnoمبادله میشود؛ use_last_error نیز همین کار را برای کد خطای ویندوز انجام میدهد.
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
پیشنمونه تابع برگشتی، توابعی را ایجاد میکند که از قرارداد فراخوانی
stdcallاستفاده میکنند. تابع در حین فراخوانی، GIL را آزاد خواهد کرد. use_errno و use_last_error همان معنای بالا را دارند.دسترسپذیری: Windows
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
پیشنمونه تابع برگرداندهشده، توابعی ایجاد میکند که از قرارداد فراخوانی پایتون استفاده میکنند. تابع در حین فراخوانی، GIL را آزاد نخواهد کرد.
میتوان پیشنمونههای تابع ایجادشده توسط این توابع کارخانهای را به روشهای مختلف نمونهسازی کرد، بسته به نوع و تعداد پارامترها در فراخوانی:
- prototype(address)
یک تابع خارجی را در آدرس مشخصشده برمیگرداند؛ این آدرس باید یک عدد صحیح باشد.
- prototype(callable)
یک تابع فراخوانیپذیر در C (یک تابع کالبک) از یک callable در پایتون ایجاد کنید.
- prototype(func_spec[, paramflags])
یک تابع خارجی اکسپورتشده از یک کتابخانه اشتراکی را بازمیگرداند. func_spec باید یک تاپل دوتایی به صورت
(name_or_ordinal, library)باشد. آیتم اول، نام تابع اکسپورتشده به صورت رشته، یا شماره ترتیبی تابع اکسپورتشده به صورت عدد صحیح کوچک است. آیتم دوم، نمونهای از کتابخانه اشتراکی است.
- prototype(vtbl_index, name[, paramflags[, iid]])
یک تابع خارجی را برمیگرداند که یک متد COM را فراخوانی خواهد کرد. vtbl_index اندیس در جدول توابع مجازی است، یک عدد صحیح نامنفی کوچک. name نام متد COM است. iid یک اشارهگر اختیاری به شناسه رابط است که در گزارش خطای گسترشیافته استفاده میشود.
اگر iid تعییننشده باشد، در صورت شکست فراخوانی متد COM، استثنای
OSErrorپرتاب میشود. اگر iid تعیینشده باشد، در عوض استثنایCOMErrorپرتاب میشود.متدهای COM از یک قرارداد فراخوانی خاص استفاده میکنند: آنها علاوه بر پارامترهایی که در تاپل
argtypesمشخص شدهاند، به یک اشارهگر به رابط COM بهعنوان اولین آرگومان نیاز دارند.دسترسپذیری: Windows
پارامتر اختیاری paramflags پوششیهای تابع خارجی را با امکانات بسیار بیشتری نسبت به قابلیتهای توصیفشده در بالا ایجاد میکند.
paramflags باید یک تاپلهمطول با argtypes باشد.
هر آیتم در این تاپل شامل اطلاعات بیشتری درباره یک پارامتر است و باید یک تاپل شامل یک، دو یا سه آیتم باشد.
اولین آیتم یک عدد صحیح است که شامل ترکیبی از پرچمهای جهت برای پارامتر است:
- 1
یک پارامتر ورودی برای تابع مشخص میکند.
- 2
پارامتر خروجی. تابع خارجی مقداری را پر میکند.
- 4
پارامتر ورودی که مقدار پیشفرض آن عدد صحیح صفر است.
آیتم دوم که اختیاری است، نام پارامتر بهصورت رشته است. اگر این مورد تعیین شود، میتوان تابع خارجی را با پارامترهای نامدار فراخوانی کرد.
آیتم سوم اختیاری، مقدار پیشفرض این پارامتر است.
مثال زیر نشان میدهد که چگونه میتوان تابع MessageBoxW ویندوز را پوشش داد تا از پارامترهای پیشفرض و آرگومانهای نامدار پشتیبانی کند. اعلان C از پروندهی سرآیند ویندوز به این صورت است:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
در اینجا پوششی با ctypes آمده است:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
اکنون میتوان تابع خارجی MessageBox را به این روشها فراخوانی کرد:
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
مثال دوم پارامترهای خروجی را نشان میدهد. تابع GetWindowRect در win32، ابعاد یک پنجره مشخص را با کپی کردن آنها در ساختار RECT که فراخواننده باید آن را فراهم کند، به دست میآورد. اعلان C به صورت زیر است:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
در اینجا پوششی با ctypes آمده است:
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>
توابعی که پارامترهای خروجی دارند، بهطور خودکار اگر فقط یک پارامتر خروجی وجود داشته باشد مقدار آن را برمیگردانند و اگر بیش از یکی وجود داشته باشد، تاپلی شامل مقادیر پارامترهای خروجی را برمیگردانند؛ بنابراین تابع GetWindowRect اکنون هنگام فراخوانی یک نمونه از RECT برمیگرداند.
میتوان پارامترهای خروجی را با پروتکل errcheck ترکیب کرد تا پردازش بیشتر خروجی و بررسی خطا انجام شود. تابع API مربوط به win32 به نام GetWindowRect یک BOOL برمیگرداند تا موفقیت یا شکست را نشان دهد، بنابراین این تابع میتواند بررسی خطا را انجام دهد و در صورت ناموفق بودن فراخوانی API، یک استثنا پرتاب کند:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
اگر تابع errcheck تاپل آرگومانهایی را که دریافت میکند بدون تغییر برگرداند، ctypes به پردازش عادی خود روی پارامترهای خروجی ادامه میدهد. اگر بخواهید بهجای یک نمونه RECT، یک تاپل از مختصات پنجره برگردانید، میتوانید فیلدها را در تابع بازیابی کنید و آنها را بهجای آن برگردانید؛ پردازش عادی دیگر انجام نخواهد شد:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... rc = args[1]
... return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>
توابع کاربردی¶
- ctypes.addressof(obj)¶
نشانی بافر حافظه را بهصورت عدد صحیح برمیگرداند. obj باید نمونهای از یک نوع ctypes باشد.
یک رویداد حسابرسی
ctypes.addressofرا با آرگومانobjپرتاب میکند.
- ctypes.alignment(obj_or_type)¶
الزامات همترازی یک نوع ctypes را برمیگرداند. obj_or_type باید یک نوع یا نمونه از ctypes باشد.
- ctypes.byref(obj[, offset])¶
یک اشارهگر سبکوزن به obj برمیگرداند، که باید نمونهای از یک نوع ctypes باشد. offset بهطور پیشفرض صفر است و باید یک عدد صحیح باشد که به مقدار اشارهگر داخلی افزوده میشود.
byref(obj, offset)معادل این کد C است:(((char *)&obj) + offset)
شیء برگرداندهشده تنها میتواند بهعنوان پارامتر فراخوانی تابع خارجی استفاده شود. رفتار آن مشابه
pointer(obj)است، اما ساخت آن بسیار سریعتر است.
- ctypes.CopyComPointer(src, dst)¶
یک اشارهگر COM را از src به dst کپی میکند و مقدار
HRESULTمختص ویندوز را برمیگرداند.اگر src برابر
NULLنباشد، متدAddRefآن فراخوانی میشود و شمار ارجاع را افزایش میدهد.در مقابل، شمار ارجاع dst پیش از انتساب مقدار جدید کاهش نمییابد. مگر اینکه dst برابر
NULLباشد، فراخواننده مسئول کاهش شمار ارجاع با فراخوانی متدReleaseآن در صورت لزوم است.دسترسپذیری: Windows
اضافه شده در نسخهی 3.14.
- ctypes.cast(obj, type)¶
این تابع مشابه عملگر تبدیل نوع (cast) در C است. این تابع نمونهی جدیدی از type را بازمیگرداند که به همان بلوک حافظهای اشاره میکند که obj به آن اشاره دارد. type باید یک نوع اشارهگر باشد و obj باید شیءای باشد که بتوان آن را بهعنوان یک اشارهگر تفسیر کرد.
- ctypes.create_string_buffer(init, size=None)¶
- ctypes.create_string_buffer(size)
این تابع یک بافر نویسهای تغییرپذیر ایجاد میکند. شیء برگرداندهشده یک آرایه ctypes از
c_charاست.اگر size داده شود (و
Noneنباشد)، باید یکintباشد. این مقدار، اندازهی آرایهی بازگشتی را مشخص میکند.اگر آرگومان init داده شود، باید از نوع
bytesباشد. از آن برای مقداردهی اولیه آیتمهای آرایه استفاده میشود. بایتهایی که به این روش مقداردهی اولیه نشدهاند، روی صفر (NUL) تنظیم میشوند.اگر size داده نشود (یا اگر
Noneباشد)، بافر به اندازه یک عنصر بزرگتر از init ساخته میشود و عملاً یک پایانگر NUL به آن اضافه میشود.اگر هر دو آرگومان داده شده باشند، size نباید کمتر از
len(init)باشد.هشدار
اگر size برابر با
len(init)باشد، یک پایاندهندهی NUL افزوده نمیشود. با چنین بافری بهعنوان یک رشتهی C رفتار نکنید.برای مثال:
>>> bytes(create_string_buffer(2)) b'\x00\x00' >>> bytes(create_string_buffer(b'ab')) b'ab\x00' >>> bytes(create_string_buffer(b'ab', 2)) b'ab' >>> bytes(create_string_buffer(b'ab', 4)) b'ab\x00\x00' >>> bytes(create_string_buffer(b'abcdef', 2)) Traceback (most recent call last): ... ValueError: byte string too long
یک رویداد حسابرسی
ctypes.create_string_bufferرا با آرگومانهایinitوsizeپرتاب میکند.
- ctypes.create_unicode_buffer(init, size=None)¶
- ctypes.create_unicode_buffer(size)
این تابع یک بافر تغییرپذیر از نویسههای یونیکد ایجاد میکند. شیء برگرداندهشده یک آرایه ctypes از
c_wcharاست.این تابع همان آرگومانهایی را میپذیرد که
create_string_buffer()میپذیرد، بهجز اینکه init باید یک رشته باشد و size تعدادc_wcharرا میشمارد.یک رویداد حسابرسی
ctypes.create_unicode_bufferرا با آرگومانهایinitوsizeپرتاب میکند.
- ctypes.DllCanUnloadNow()¶
این تابع یک قلاباست که امکان پیادهسازی سرورهای COM درونفرایندی با ctypes را فراهم میکند. این تابع از تابع DllCanUnloadNow فراخوانی میشود که dll افزونهای _ctypes آن را اکسپورت میکند.
دسترسپذیری: Windows
- ctypes.DllGetClassObject()¶
این تابع یک قلاباست که امکان پیادهسازی سرورهای COM درونفرایندی را با ctypes فراهم میکند. این تابع از تابع DllGetClassObject فراخوانی میشود که dll افزونه
_ctypesآن را اکسپورت میکند.دسترسپذیری: Windows
- ctypes.util.find_library(name)¶
تلاش میکند کتابخانهای را بیابد و یک مسیر نام برگرداند. name نام کتابخانه بدون هیچ پیشوندی مانند
lib، پسوندی مانند.soیا.dylib، یا شمارهی نسخه است (این قالب برای گزینهی پیونددهندهی POSIX یعنی-lبه کار میرود). اگر هیچ کتابخانهای یافت نشود،Noneبرگردانده میشود.عملکرد دقیق، وابسته به سیستم است.
برای مستندات کامل، یافتن کتابخانههای مشترک را ببینید.
- ctypes.util.find_msvcrt()¶
نام پرونده کتابخانه رانتایم VC مورد استفاده پایتون و ماژولهای توسعه را برمیگرداند. اگر نام کتابخانه قابل تعیین نباشد،
Noneبرگردانده میشود.اگر نیاز دارید حافظهای را آزاد کنید، برای مثال حافظهای که توسط یک ماژول توسعه تخصیص داده شده است، با فراخوانی
free(void *)، مهم است که از تابع در همان کتابخانهای استفاده کنید که آن حافظه را تخصیص داده است.دسترسپذیری: Windows
- ctypes.util.dllist()¶
تلاش میکند فهرستی از مسیرهای کتابخانههای اشتراکی بارگذاریشده در فرایند جاری را ارائه کند. این مسیرها به هیچ وجه بههنجار یا پردازش نمیشوند. اگر APIهای پلتفرم زیرین شکست بخورند، این تابع ممکن است
OSErrorرا پرتاب کند. عملکرد دقیق آن وابسته به سیستم است.در بیشتر پلتفرمها، اولین عنصر فهرست نمایانگر پرونده اجرایی جاری است. ممکن است یک رشته خالی باشد.
دسترسپذیری: Windows, macOS, iOS, glibc, BSD libc, musl
اضافه شده در نسخهی 3.14.
- ctypes.FormatError([code])¶
یک شرح متنی از کد خطای code را برمیگرداند. اگر کد خطایی مشخص نشده باشد، با فراخوانی تابع API ویندوز
GetLastError()از آخرین کد خطا استفاده میشود.دسترسپذیری: Windows
- ctypes.GetLastError()¶
آخرین کد خطایی که ویندوز در نخ فراخواننده تنظیم کرده است را بازمیگرداند. این تابع مستقیماً تابع
GetLastError()ویندوز را فراخوانی میکند و نسخهی خصوصی ctypes از کد خطا را بازنمیگرداند.دسترسپذیری: Windows
- ctypes.get_errno()¶
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی
errnoرا در نخ فراخوان برمیگرداند.یک رویداد حسابرسی
ctypes.get_errnoرا بدون آرگومان پرتاب میکند.
- ctypes.get_last_error()¶
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی
LastErrorرا در نخ فراخوان برمیگرداند.دسترسپذیری: Windows
یک رویداد حسابرسی
ctypes.get_last_errorرا بدون آرگومان پرتاب میکند.
- ctypes.memmove(dst, src, count)¶
مشابه تابع memmove در کتابخانه استاندارد C: count بایت را از src به dst کپی میکند. dst و src باید اعداد صحیح یا نمونههای ctypes باشند که بتوان آنها را به اشارهگرها تبدیل کرد.
- ctypes.memset(dst, c, count)¶
مشابه تابع کتابخانهای استاندارد memset در C: بلوک حافظه در نشانی dst را با count بایت به مقدار c پر میکند. dst باید یک عدد صحیح مشخصکننده یک نشانی، یا نمونهای از ctypes باشد.
- ctypes.POINTER(type, /)¶
یک نوع اشارهگر ctypes را ایجاد میکند یا بازمیگرداند. انواع اشارهگر بهصورت داخلی در نهانگاه ذخیره و دوباره استفاده میشوند، بنابراین فراخوانی مکرر این تابع کمهزینه است. type باید یک نوع ctypes باشد.
نوع اشارهگر حاصل در ویژگی
__pointer_type__از type بهعنوان نهانگاه ذخیره میشود. میتوان این ویژگی را پیش از نخستین فراخوانیPOINTERتنظیم کرد تا یک نوع اشارهگر سفارشی تنظیم شود. با این حال، انجام این کار توصیه نمیشود: ایجاد دستی یک نوع اشارهگر مناسب بدون تکیه بر جزئیات پیادهسازی که ممکن است در نسخههای آینده پایتون تغییر کنند، دشوار است.
- ctypes.pointer(obj, /)¶
یک نمونه اشارهگر جدید، که به obj اشاره دارد، ایجاد کنید. شیء برگرداندهشده از نوع
POINTER(type(obj))است.نکته: اگر فقط میخواهید یک اشارهگر به یک شیء را به یک فراخوانی تابع خارجی ارسال کنید، باید از
byref(obj)استفاده کنید که بسیار سریعتر است.
- ctypes.resize(obj, size)¶
این تابع بافر حافظهی داخلی obj را تغییر اندازه میدهد؛ obj باید نمونهای از یک نوع ctypes باشد. کوچک کردن بافر به کمتر از اندازهی بومی نوع شیء، همانطور که با
sizeof(type(obj))مشخص میشود، امکانپذیر نیست، اما بزرگ کردن بافر امکانپذیر است.
- ctypes.set_errno(value)¶
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی
errnoدر نخ فراخواننده را برابر value قرار میدهد و مقدار قبلی را برمیگرداند.یک رویداد حسابرسی
ctypes.set_errnoرا با آرگومانerrnoپرتاب میکند.
- ctypes.set_last_error(value)¶
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی
LastErrorرا در نخ فراخوان به value تنظیم میکند و مقدار قبلی را برمیگرداند.دسترسپذیری: Windows
یک رویداد حسابرسی
ctypes.set_last_errorرا با آرگومانerrorپرتاب میکند.
- ctypes.sizeof(obj_or_type)¶
اندازهی یک نوع ctypes یا بافر حافظهی یک نمونه را بر حسب بایت برمیگرداند. همانند عملگر
sizeofدر C عمل میکند.
- ctypes.string_at(ptr, size=-1)¶
رشته بایتی در void *ptr را برمیگرداند. اگر size مشخص شده باشد، بهعنوان اندازه استفاده میشود، در غیر این صورت فرض میشود که رشته با صفر خاتمه یافته است.
یک رویداد حسابرسی
ctypes.string_atرا با آرگومانهایptrوsizeپرتاب میکند.
- ctypes.WinError(code=None, descr=None)¶
یک نمونه از
OSErrorایجاد میکند. اگر code مشخص نشده باشد، برای تعیین کد خطاGetLastError()فراخوانی میشود. اگر descr مشخص نشده باشد، برای دریافت شرح متنی خطاFormatError()فراخوانی میشود.دسترسپذیری: Windows
تغییر یافته در نسخهی 3.3: پیشتر نمونهای از
WindowsErrorایجاد میشد، که اکنون نام مستعاری برایOSErrorاست.
- ctypes.wstring_at(ptr, size=-1)¶
رشته نویسههای پهن در void *ptr را برمیگرداند. اگر size تعیین شده باشد، از آن به عنوان تعداد نویسههای رشته استفاده میشود، در غیر این صورت فرض میشود که رشته با صفر پایان مییابد.
یک رویداد حسابرسی
ctypes.wstring_atرا با آرگومانهایptrوsizeپرتاب میکند.
- ctypes.memoryview_at(ptr, size, readonly=False)¶
یک شیء
memoryviewبه طول size برمیگرداند که به حافظهای که از void *ptr شروع میشود ارجاع دارد.اگر readonly مقدار true باشد، نمیتوان از شیء
memoryviewبرگرداندهشده برای تغییر حافظهی زیرین استفاده کرد. (تغییراتی که به روشهای دیگر ایجاد شوند، همچنان در شیء برگرداندهشده بازتاب خواهند داشت.)این تابع مشابه
string_at()است، با این تفاوت کلیدی که کپیای از حافظهی مشخصشده نمیسازد. این تابع جایگزینی همارز از نظر معنایی (اما کارآمدتر) برایmemoryview((c_byte * size).from_address(ptr))است. (در حالی کهfrom_address()فقط اعداد صحیح را میپذیرد، ptr همچنین میتواند بهعنوان یک شیءctypes.POINTERیاbyref()نیز داده شود.)رویداد حسابرسی
ctypes.memoryview_atرا با آرگومانهایaddress،sizeوreadonlyپرتاب میکند.اضافه شده در نسخهی 3.14.
انواع داده¶
- class ctypes._CData¶
این کلاس غیرعمومی، کلاس پایه مشترک تمام انواع داده ctypes است. در میان سایر موارد، تمام نمونههای نوع ctypes شامل یک بلوک حافظه هستند که دادههای سازگار با C را نگه میدارد؛ آدرس بلوک حافظه توسط تابع کمکی
addressof()برگردانده میشود. متغیر نمونه دیگری نیز بهعنوان_objectsدر معرض قرار گرفته است؛ این متغیر شامل اشیاء پایتون دیگری است که در صورتی که بلوک حافظه حاوی اشارهگرها باشد، باید زنده نگه داشته شوند.متدهای مشترک انواع داده ctypes، همگی متدهای کلاس هستند (بهطور دقیق، آنها متدهای فراکلاس هستند):
- from_buffer(source[, offset])¶
این متد یک نمونه ctypes برمیگرداند که بافر شیء source را به اشتراک میگذارد. شیء source باید از رابط بافر قابلنوشتن پشتیبانی کند. پارامتر اختیاری offset یک آفست درون بافر source را بر حسب بایت مشخص میکند؛ مقدار پیشفرض ۰ است. اگر بافر source بهقدر کافی بزرگ نباشد، یک
ValueErrorپرتاب میشود.یک رویداد حسابرسی
ctypes.cdata/bufferرا با آرگومانهایpointer،size،offsetپرتاب میکند.
- from_buffer_copy(source[, offset])¶
این متد یک نمونه ctypes ایجاد میکند و بافر را از بافر شیء source کپی میکند؛ بافر شیء source باید خواندنی باشد. پارامتر اختیاری offset، یک آفست در بافر منبع را بر حسب بایت مشخص میکند؛ مقدار پیشفرض ۰ است. اگر بافر منبع بهاندازه کافی بزرگ نباشد، یک
ValueErrorپرتاب میشود.یک رویداد حسابرسی
ctypes.cdata/bufferرا با آرگومانهایpointer،size،offsetپرتاب میکند.
- from_address(address)¶
این متد یک نمونهی نوع ctypes را با استفاده از حافظهی مشخصشده توسط address برمیگرداند؛ address باید یک عدد صحیح باشد.
این متد، و سایر متدهایی که بهطور غیرمستقیم این متد را فراخوانی میکنند، یک رویداد حسابرسی
ctypes.cdataرا با آرگومانaddressپرتاب میکنند.
- from_param(obj)¶
این متد obj را با یک نوع ctypes تطبیق میدهد. هنگامی که نوع در تاپل
argtypesتابع خارجی وجود داشته باشد، این متد با شیء واقعی استفادهشده در یک فراخوانی تابع خارجی فراخوانی میشود؛ این متد باید یک شیء برگرداند که بتوان از آن بهعنوان پارامتر فراخوانی تابع استفاده کرد.تمام انواع داده ctypes یک پیادهسازی پیشفرض از این classmethod دارند که بهطور معمول obj را، در صورتی که نمونهای از آن نوع باشد، برمیگرداند. برخی از انواع، اشیاء دیگر را نیز میپذیرند.
- in_dll(library, name)¶
این متد یک نمونه از نوع ctypes را بازمیگرداند که توسط یک کتابخانه مشترک اکسپورت شده است. name نام نمادی است که دادهها را اکسپورت میکند، و library کتابخانه مشترک بارگذاریشده است.
متغیرهای کلاس مشترک انواع داده ctypes:
- __pointer_type__¶
نوع اشارهگری که با فراخوانی
POINTER()برای نوع دادهی متناظر در ctypes ایجاد شده است. اگر نوع اشارهگر هنوز ایجاد نشده باشد، این ویژگی وجود ندارد.اضافه شده در نسخهی 3.14.
متغیرهای نمونه مشترک انواع داده ctypes:
- _b_base_¶
گاهی نمونههای داده ctypes مالک بلوک حافظهای که در بر دارند، نیستند؛ بلکه بخشی از بلوک حافظه یک شیء پایه را به اشتراک میگذارند. عضو فقطخواندنی
_b_base_شیء ریشه ctypes است که مالک بلوک حافظه میباشد.
- _b_needsfree_¶
این متغیر فقطخواندنی زمانی درست است که نمونه داده ctypes خودش بلوک حافظه را تخصیص داده باشد، در غیر این صورت نادرست است.
- _objects¶
این عضو یا
Noneاست یا یک دیکشنری حاوی اشیای پایتون که باید زنده نگه داشته شوند تا محتویات بلوک حافظه معتبر باقی بماند. این شیء فقط برای اشکالزدایی در دسترس است؛ هرگز محتویات این دیکشنری را تغییر ندهید.
انواع داده بنیادی¶
- class ctypes._SimpleCData¶
این کلاس غیرعمومی، کلاس پایهی همهی انواع دادهی بنیادی ctypes است. این کلاس در اینجا ذکر شده است زیرا شامل ویژگیهای مشترک انواع دادهی بنیادی ctypes است.
_SimpleCDataیک زیرکلاس از_CDataاست، بنابراین متدها و ویژگیهای آنها را به ارث میبرد. اکنون میتوان انواع دادهی ctypes را که اشارهگر نیستند و شامل اشارهگرها نمیشوند، پیکل کرد.نمونهها یک ویژگی دارند:
- value¶
این ویژگی حاوی مقدار واقعی نمونه است. برای انواع عدد صحیح و اشارهگر، این مقدار یک عدد صحیح است، برای انواع نویسه، یک شیء bytes یا رشتهی تکنویسهای است، و برای انواع اشارهگر به نویسه، یک شیء bytes یا رشتهی پایتون است.
هنگامی که ویژگی
valueاز یک نمونه ctypes بازیابی میشود، معمولاً هر بار یک شیء جدید برگردانده میشود.ctypesبازگشت شیء اصلی را پیادهسازی نمیکند، بلکه همیشه یک شیء جدید ساخته میشود. همین موضوع برای تمام نمونههای دیگر اشیاء ctypes نیز صادق است.
هر زیرکلاس دارای یک ویژگی کلاس است:
- _type_¶
ویژگی کلاس که حاوی یک کد نوع داخلی بهصورت یک رشته تکنویسهای است. برای یک خلاصه، به انواع داده بنیادی مراجعه کنید.
انواعی که در خلاصه با * علامتگذاری شدهاند ممکن است (یا همواره) نامهای مستعار یک زیرکلاس دیگر از
_SimpleCDataباشند و لزوماً از کد نوع فهرستشده استفاده نخواهند کرد. برای مثال، اگر انواع Cِ long، long long و time_t در سکو یکسان باشند، آنگاهc_long،c_longlongوc_time_tهمگی به یک کلاس واحد، یعنیc_long، ارجاع میدهند که کد_type_آن'l'است. کد'L'استفاده نخواهد شد.
انواع داده بنیادی، هنگامی که بهعنوان نتایج فراخوانی تابع خارجی برگردانده میشوند، یا مثلاً هنگام بازیابی اعضای فیلدهای ساختار یا آیتمهای آرایه، بهطور شفاف به انواع بومی پایتون تبدیل میشوند. به عبارت دیگر، اگر یک تابع خارجی دارای restype برابر با c_char_p باشد، همیشه یک شیء bytes پایتون دریافت خواهید کرد، نه یک نمونه c_char_p.
زیرکلاسهای انواع داده بنیادی این رفتار را به ارث نمیبرند. بنابراین، اگر restype یک تابع خارجی زیرکلاسی از c_void_p باشد، از فراخوانی تابع یک نمونه از این زیرکلاس دریافت خواهید کرد. البته میتوانید با دسترسی به ویژگی value مقدار اشارهگر را به دست آورید.
اینها انواع دادهی بنیادی ctypes هستند:
- class ctypes.c_byte¶
نوع دادهی signed char در C را نشان میدهد و مقدار را بهعنوان عدد صحیح کوچک تفسیر میکند. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچگونه بررسی سرریزی انجام نمیشود.
- class ctypes.c_char¶
نشاندهندهی نوع دادهی char در C است و مقدار را بهعنوان یک نویسه تفسیر میکند. سازنده یک مقدارده اولیهی رشتهای اختیاری را میپذیرد، طول رشته باید دقیقاً ۱ نویسه باشد.
- class ctypes.c_char_p¶
نمایانگر نوع دادهی C char* است، زمانی که به یک رشتهی پایانیافته با صفر اشاره میکند. برای یک اشارهگر نویسهای عمومی که ممکن است به دادههای دودویی نیز اشاره کند، باید از
POINTER(c_char)استفاده شود. سازنده یک نشانی بهصورت عدد صحیح یا یک شیء bytes را میپذیرد.
- class ctypes.c_double¶
نشاندهندهی نوع دادهی double در C است. سازنده یک مقدار اولیهی float اختیاری را میپذیرد.
- class ctypes.c_longdouble¶
نمایانگر نوع دادهی C long double است. سازنده یک مقدار اولیه float اختیاری را میپذیرد. در سکوهایی که
sizeof(long double) == sizeof(double)باشد، این نام مستعاری برایc_doubleاست.
- class ctypes.c_float¶
نوع دادهی float در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع float را میپذیرد.
- class ctypes.c_double_complex¶
در صورت موجود بودن، نشاندهندهی نوع دادهی double complex در C است. سازنده یک مقداردهی اولیهی اختیاری از
complexرا میپذیرد.اضافه شده در نسخهی 3.14.
- class ctypes.c_float_complex¶
نوع دادهی float complex در C را در صورت موجود بودن نشان میدهد. سازنده، یک مقدارده اولیه اختیاری از نوع
complexرا میپذیرد.اضافه شده در نسخهی 3.14.
- class ctypes.c_longdouble_complex¶
نشاندهندهی نوع دادهی long double complex در C است، در صورت موجود بودن. سازنده یک مقداردهی اولیهی اختیاری
complexرا میپذیرد.اضافه شده در نسخهی 3.14.
- class ctypes.c_int¶
نشاندهندهی نوع دادهی signed int در C است. سازنده یک مقدار اولیهی اختیاری عدد صحیح را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود. در سکوهایی که
sizeof(int) == sizeof(long)باشد، این یک نام مستعار برایc_longاست.
- class ctypes.c_int8¶
نشاندهندهی نوع دادهی signed int ۸ بیتی C است. این یک نام مستعار برای
c_byteاست.
- class ctypes.c_int16¶
نشاندهندهی نوع دادهی signed int ۱۶ بیتی در C است. معمولاً نام مستعاری برای
c_shortاست.
- class ctypes.c_int32¶
نشاندهندهی نوع دادهی signed int ۳۲ بیتی C است. معمولاً نام مستعاری برای
c_intاست.
- class ctypes.c_int64¶
نشاندهندهی نوع دادهی signed int ۶۴ بیتی C است. معمولاً نام مستعاری برای
c_longlongاست.
- class ctypes.c_long¶
نشاندهنده نوع داده signed long در C است. سازنده یک مقدار اولیه عدد صحیح اختیاری را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود.
- class ctypes.c_longlong¶
نشاندهندهی نوع دادهی C signed long long است. سازندهی آن یک مقدار اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود. در سکوهایی که در آنها
sizeof(long long) == sizeof(long)باشد، این نوع نام مستعاری برایc_longاست.
- class ctypes.c_short¶
نشاندهندهی نوع دادهی signed short در C است. سازنده یک مقداردهی اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
- class ctypes.c_size_t¶
نشاندهندهی نوع دادهی
size_tدر C است. معمولاً نام مستعاری برای نوع دیگری از عدد صحیح بدون علامت است.
- class ctypes.c_ssize_t¶
نشاندهندهی نوع دادهی
Py_ssize_tاست. این یک نسخهی علامتدار ازsize_tاست؛ یعنی نوعssize_tدر POSIX. معمولاً نام مستعاری برای یک نوع عدد صحیح دیگر است.اضافه شده در نسخهی 3.2.
- class ctypes.c_time_t¶
نشاندهندهی نوع دادهی
time_tدر C است. معمولاً نام مستعاری برای یک نوع عدد صحیح دیگر است.اضافه شده در نسخهی 3.12.
- class ctypes.c_ubyte¶
نشاندهندهی نوع دادهی unsigned char در C است و مقدار را بهعنوان عدد صحیح کوچک تفسیر میکند. سازنده یک مقدارده اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
- class ctypes.c_uint¶
نشاندهندهی نوع دادهی unsigned int در C است. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود. در سکوهایی که
sizeof(int) == sizeof(long)باشد، نام مستعاری برایc_ulongاست.
- class ctypes.c_uint8¶
نشاندهندهی نوع دادهی ۸بیتی unsigned int در C است. این یک نام مستعار برای
c_ubyteاست.
- class ctypes.c_uint16¶
نشاندهندهی نوع دادهی unsigned int ۱۶ بیتی در C است. معمولاً نام مستعاری برای
c_ushortاست.
- class ctypes.c_uint32¶
نشاندهندهی نوع دادهی ۳۲ بیتی unsigned int در C است. معمولاً نام مستعاری برای
c_uintاست.
- class ctypes.c_uint64¶
نشاندهندهی نوع دادهی unsigned int ۶۴ بیتی در C است. معمولاً نام مستعاری برای
c_ulonglongاست.
- class ctypes.c_ulong¶
نوع دادهی unsigned long در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
- class ctypes.c_ulonglong¶
نشاندهندهی نوع دادهی C unsigned long long است. سازنده یک مقدار اولیهی عدد صحیح اختیاری را میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود. در سکوهای که
sizeof(long long) == sizeof(long)باشد، نام مستعاری برایc_longاست.
- class ctypes.c_ushort¶
نوع دادهی unsigned short در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود.
- class ctypes.c_void_p¶
نشاندهندهی نوع void* در C است. مقدار بهصورت عدد صحیح نشان داده میشود. سازنده یک مقدار اولیهی اختیاری عدد صحیح را میپذیرد.
- class ctypes.c_wchar¶
نشاندهندهی نوع دادهی
wchar_tدر C است و مقدار را بهعنوان یک رشتهی یونیکد تکنویسهای تفسیر میکند. سازنده یک مقدار اولیهی رشتهای اختیاری میپذیرد؛ طول رشته باید دقیقاً ۱ نویسه باشد.
- class ctypes.c_wchar_p¶
نشاندهندهی نوع دادهی C wchar_t* است، که باید اشارهگری به رشتهای از نویسههای پهن خاتمهیافته با صفر باشد. سازنده یک نشانی عدد صحیحی یا یک رشته را میپذیرد.
- class ctypes.c_bool¶
نشاندهندهی نوع دادهی bool در C (دقیقتر، _Bool از C99) است. مقدار آن میتواند
TrueیاFalseباشد و سازندهی آن هر شیءای را که مقدار درستی داشته باشد میپذیرد.
- class ctypes.HRESULT¶
بیانگر یک مقدار
HRESULTاست، که حاوی اطلاعات موفقیت یا خطا برای فراخوانی یک تابع یا متد است.دسترسپذیری: Windows
- class ctypes.py_object¶
نشاندهندهی نوع دادهی PyObject* در C است. فراخوانی این بدون آرگومان، یک اشارهگر
NULLاز نوع PyObject* ایجاد میکند.تغییر یافته در نسخهی 3.14:
py_objectاکنون یک generic type است.
ماژول ctypes.wintypes تعداد زیادی از انواع داده دیگر مختص ویندوز را فراهم میکند، برای مثال HWND، WPARAM، VARIANT_BOOL یا DWORD. برخی ساختارهای مفید مانند MSG یا RECT نیز تعریف شدهاند.
انواع داده ساختاریافته¶
- class ctypes.Union(*args, **kw)¶
کلاس پایه انتزاعی برای اجتماعها (unions) در ترتیب بایت بومی.
اجتماعها (Unions) ویژگیها و رفتار مشترکی با ساختارها دارند؛ برای جزئیات، مستندات
Structureرا ببینید.
- class ctypes.BigEndianUnion(*args, **kw)¶
کلاس پایه انتزاعی برای اجتماعها با ترتیب بایت بزرگاندیان.
اضافه شده در نسخهی 3.11.
- class ctypes.LittleEndianUnion(*args, **kw)¶
کلاس پایه انتزاعی برای اجتماعها (union) با ترتیب بایتی کوچکاندیان (little endian).
اضافه شده در نسخهی 3.11.
- class ctypes.BigEndianStructure(*args, **kw)¶
کلاس پایهی انتزاعی برای ساختارهایی با ترتیب بایت بزرگاندیان (big endian).
- class ctypes.LittleEndianStructure(*args, **kw)¶
کلاس پایه انتزاعی برای ساختارهایی با ترتیب بایت little endian.
ساختارها و اجتماعهایی که ترتیب بایت غیربومی دارند، نمیتوانند شامل فیلدهایی از نوع اشارهگر یا هر نوع داده دیگری باشند که حاوی فیلدهایی از نوع اشارهگر باشد.
- class ctypes.Structure(*args, **kw)¶
کلاس پایه انتزاعی برای ساختارها با ترتیب بایت بومی.
انواع عینی ساختار و اجتماع (union) باید با زیرکلاسسازی از یکی از این انواع ایجاد شوند، و حداقل یک متغیر کلاس
_fields_را تعریف کنند.ctypesتوصیفگرها (descriptors) را ایجاد میکند که امکان خواندن و نوشتن فیلدها را از طریق دسترسی مستقیم به ویژگیها فراهم میکنند. اینها- _fields_¶
دنبالهای که فیلدهای ساختار را تعریف میکند. آیتمها باید تاپلهای دوتایی یا سهتایی باشند. اولین آیتم، نام فیلد است؛ دومین آیتم، نوع فیلد را مشخص میکند و میتواند هر نوع دادهای از ctypes باشد.
برای فیلدهایی از نوع عدد صحیح مانند
c_int، میتوان یک آیتم سوم اختیاری نیز ارائه کرد. این آیتم باید یک عدد صحیح مثبت کوچک باشد که عرض بیت فیلد را تعریف میکند.نام فیلدها باید در یک ساختار یا union یکتا باشند. این موضوع بررسی نمیشود؛ وقتی نامها تکراری باشند، تنها به یک فیلد میتوان دسترسی پیدا کرد.
میتوانید متغیر کلاس
_fields_را پس از دستور class که زیرکلاس Structure را تعریف میکند، تعریف کنید؛ این کار امکان ایجاد انواع دادهای را فراهم میکند که بهطور مستقیم یا غیرمستقیم به خودشان ارجاع میدهند:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
متغیر کلاس
_fields_را فقط میتوان یکبار تنظیم کرد. انتسابهای بعدی یکAttributeErrorرا پرتاب خواهند کرد.علاوه بر این، متغیر کلاس
_fields_باید پیش از اولین استفاده از نوع ساختار یا اجتماع (union) تعریف شود: ایجاد یک نمونه یا زیرکلاس، فراخوانیsizeof()روی آن، و غیره. انتسابهای بعدی به_fields_باعث پرتاب یکAttributeErrorمیشوند. اگر_fields_پیش از چنین استفادهای تنظیم نشده باشد، ساختار یا اجتماع هیچ فیلد مختص به خود نخواهد داشت، گویی_fields_خالی بوده است.زیرزیرکلاسهای انواع ساختاری، فیلدهای کلاس پایه بههمراه
_fields_تعریفشده در زیرزیرکلاس را، در صورت وجود، به ارث میبرند.
- _pack_¶
یک عدد صحیح کوچک اختیاری که امکان بازنویسی همترازی فیلدهای ساختار در نمونه را فراهم میکند.
این فقط برای چیدمان حافظه سازگار با MSVC پیادهسازی شده است (به
_layout_مراجعه کنید).تنظیم
_pack_به ۰ مانند این است که اصلاً آن را تنظیم نکرده باشید. در غیر این صورت، مقدار باید توان مثبتی از ۲ باشد. این اثر معادل#pragma pack(N)در C است، با این تفاوت کهctypesممکن است n بزرگتری را نسبت به آنچه کامپایلر میپذیرد مجاز بداند.هنگامی که
_fields_انتساب مییابد،_pack_باید از پیش تعریف شده باشد، در غیر این صورت هیچ اثری نخواهد داشت.منسوخ شده از نسخهی 3.14, در نسخهی 3.19 حذف خواهد شد: به دلایل تاریخی، اگر
_pack_غیرصفر باشد، چیدمان سازگار با MSVC بهطور پیشفرض استفاده خواهد شد. در سکوهای غیرویندوزی، این پیشفرض منسوخ شده است و قرار است در پایتون 3.19 به یک خطا تبدیل شود. اگر این حالت مورد نظر است،_layout_را بهصراحت روی'ms'تنظیم کنید.
- _align_¶
یک عدد صحیح کوچک اختیاری که امکان افزایش همترازی ساختار را هنگام بستهبندی یا واگشایی به/از حافظه فراهم میکند.
مقدار نباید منفی باشد. اثر آن معادل
__attribute__((aligned(N)))در GCC یا#pragma align(N)در MSVC است، با این تفاوت کهctypesممکن است مقادیری را بپذیرد که کامپایلر آنها را رد میکند._align_فقط میتواند الزامات همترازی یک ساختار را افزایش دهد. تنظیم آن روی ۰ یا ۱ هیچ تأثیری ندارد.استفاده از مقادیری که توانهای ۲ نیستند، توصیه نمیشود و ممکن است به رفتار غیرمنتظرهای منجر شود.
_align_باید هنگامی که_fields_انتساب داده میشود از قبل تعریفشده باشد، در غیر این صورت بیاثر خواهد بود.اضافه شده در نسخهی 3.13.
- _layout_¶
یک رشته اختیاری برای نامگذاری چیدمان struct/union. در حال حاضر میتوان آن را بهصورت زیر تنظیم کرد:
"ms": چیدمان استفادهشده توسط کامپایلر مایکروسافت (MSVC). در GCC و Clang، این چیدمان را میتوان با__attribute__((ms_struct))انتخاب کرد."gcc-sysv": چیدمان استفادهشده توسط GCC با مدل داده System V یا «شبیه SysV»، همانطور که در Linux و macOS استفاده میشود. در این چیدمان،_pack_باید تنظیمنشده یا صفر باشد.
اگر بهصراحت تنظیم نشده باشد،
ctypesاز پیشفرضی استفاده خواهد کرد که با قراردادهای پلتفرم مطابقت دارد. این پیشفرض ممکن است در نسخههای آینده پایتون تغییر کند (برای مثال، وقتی پشتیبانی رسمی از یک پلتفرم جدید اضافه شود، یا وقتی تفاوتی میان پلتفرمهای مشابه یافت شود). در حال حاضر پیشفرض به این صورت خواهد بود:در ویندوز:
"ms"هنگامی که
_pack_تعیین شده باشد:"ms". (این مورد منسوخ شده است؛ مستندات_pack_را ببینید.)در غیر این صورت:
"gcc-sysv"
هنگامی که
_fields_انتساب مییابد،_layout_باید از قبل تعریف شده باشد، در غیر این صورت هیچ اثری نخواهد داشت.اضافه شده در نسخهی 3.14.
- _anonymous_¶
یک دنباله اختیاری که نامهای فیلدهای بینام (ناشناس) را فهرست میکند.
_anonymous_باید هنگامی که_fields_مقداردهی میشود، از پیش تعریف شده باشد، در غیر این صورت هیچ اثری نخواهد داشت.فیلدهای فهرستشده در این متغیر باید فیلدهایی با نوع ساختار یا union باشند.
ctypesتوصیفگرهایی را در نوع ساختار ایجاد میکند که امکان دسترسی مستقیم به فیلدهای تودرتو را بدون نیاز به ایجاد فیلد ساختار یا union فراهم میکنند.در اینجا یک نمونه نوع (ویندوز):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
ساختار
TYPEDESCیک نوع داده COM را توصیف میکند، فیلدvtمشخص میکند که کدامیک از فیلدهای union معتبر است. از آنجا که فیلدuبهعنوان فیلد ناشناس تعریف شده است، اکنون امکان دسترسی مستقیم به اعضا از روی نمونه TYPEDESC وجود دارد.td.lptdescوtd.u.lptdescمعادل هستند، اما مورد اول سریعتر است، زیرا نیازی به ایجاد یک نمونه union موقت ندارد:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
میتوان زیرزیرکلاسهایی از ساختارها تعریف کرد؛ آنها فیلدهای کلاس پایه را به ارث میبرند. اگر تعریف زیرکلاس دارای یک متغیر
_fields_جداگانه باشد، فیلدهای مشخصشده در آن به فیلدهای کلاس پایه افزوده میشوند.سازندههای ساختار و اجتماع، هم آرگومانهای جایگاهی و هم آرگومانهای کلیدواژهای را میپذیرند. آرگومانهای جایگاهی برای مقداردهی اولیهی فیلدهای عضو به همان ترتیبی که در
_fields_ظاهر میشوند، استفاده میشوند. آرگومانهای کلیدواژهای در سازنده بهعنوان انتساب ویژگی تفسیر میشوند، بنابراین فیلدهای_fields_را با همان نام مقداردهی اولیه میکنند، یا برای نامهایی که در_fields_وجود ندارند، ویژگیهای جدیدی ایجاد میکنند.
- class ctypes.CField(*args, **kw)¶
توصیفگر فیلدهای
StructureوUnion. برای مثال:>>> class Color(Structure): ... _fields_ = ( ... ('red', c_uint8), ... ('green', c_uint8), ... ('blue', c_uint8), ... ('intense', c_bool, 1), ... ('blinking', c_bool, 1), ... ) ... >>> Color.red <ctypes.CField 'red' type=c_ubyte, ofs=0, size=1> >>> Color.green.type <class 'ctypes.c_ubyte'> >>> Color.blue.byte_offset 2 >>> Color.intense <ctypes.CField 'intense' type=c_bool, ofs=3, bit_size=1, bit_offset=0> >>> Color.blinking.bit_offset 1
تمام ویژگیها فقطخواندنی هستند.
اشیای
CFieldاز طریق_fields_ایجاد میشوند؛ این کلاس را مستقیماً نمونهسازی نکنید.اضافه شده در نسخهی 3.14: پیشتر، توصیفگرها فقط ویژگیهای
offsetوsizeو یک نمایش رشتهای خوانا داشتند؛ کلاسCFieldبهطور مستقیم در دسترس نبود.- name¶
نام فیلد، بهصورت یک رشته.
- type¶
نوع فیلد، بهصورت یک کلاس ctypes.
- offset¶
- byte_offset¶
آفست فیلد، بر حسب بایت.
برای فیلدهای بیتی (bitfields)، این آفست واحد ذخیرهسازی زیربنایی همتراز با بایت است؛ به
bit_offsetمراجعه کنید.
- byte_size¶
اندازهی فیلد، بر حسب بایت.
برای بیتفیلدها، این اندازهی واحد ذخیرهسازی زیربنایی است. معمولاً هماندازهی نوع بیتفیلد است.
- size¶
برای فیلدهای غیربیتی، معادل
byte_sizeاست.برای فیلدهای بیتی (bitfields)، این شامل یک مقدار بستهبندیشده بیتی سازگار با نسخههای پیشین است که
bit_sizeوbit_offsetرا ترکیب میکند. ترجیحاً بهجای آن از ویژگیهای صریح استفاده کنید.
- is_bitfield¶
اگر این یک فیلد بیتی (bitfield) باشد، True است.
- bit_offset¶
- bit_size¶
موقعیت یک فیلد بیتی (bitfield) در واحد ذخیرهسازی آن، یعنی در
byte_sizeبایت از حافظه که ازbyte_offsetآغاز میشود.برای بهدست آوردن مقدار فیلد، واحد ذخیرهسازی را بهعنوان یک عدد صحیح بخوانید، آن را به اندازهی
bit_offsetشیفت چپ دهید وbit_sizeبیت کماهمیت را بگیرید.برای فیلدهای غیربیتی،
bit_offsetصفر است وbit_sizeبرابر است باbyte_size * 8.
- is_anonymous¶
True اگر این فیلد ناشناس باشد، یعنی شامل زیرفیلدهای تودرتویی است که باید در ساختار یا union حاوی آن ادغام شوند.
آرایهها و اشارهگرها¶
- class ctypes.Array(*args)¶
کلاس پایه انتزاعی برای آرایهها.
راه توصیهشده برای ایجاد انواع آرایهی مشخص، ضرب کردن هر نوع دادهای از
ctypesدر یک عدد صحیح نامنفی است. بهعنوان جایگزین، میتوانید یک زیرکلاس از این نوع بسازید و متغیرهای کلاس_length_و_type_را تعریف کنید. عناصر آرایه را میتوان با استفاده از دسترسیهای استاندارد زیرنویسی و اسلایسی خواند و نوشت؛ در خواندنهای اسلایسی، شیء حاصل، خود یکArrayنیست.آرایهها نسبت به نوع عناصر خود عام هستند.
- _length_¶
یک عدد صحیح مثبت که تعداد عناصر آرایه را مشخص میکند. زیرنویسهای خارج از محدوده منجر به
IndexErrorمیشوند. توسطlen()برگردانده میشود.
- _type_¶
نوع هر عنصر در آرایه را مشخص میکند.
سازندههای زیرکلاس آرایه، آرگومانهای جایگاهی را میپذیرند که برای مقداردهی اولیهی عناصر بهترتیب استفاده میشوند.
- ctypes.ARRAY(type, length)¶
یک آرایه ایجاد میکند. معادل
type * lengthاست، که در آن type یک نوع داده ازctypesو length یک عدد صحیح است.منسوخسازی نرم <Soft deprecated> از نسخهی 3.14: به نفع ضرب.
- class ctypes._Pointer¶
کلاس پایه خصوصی و انتزاعی برای اشارهگرها.
انواع اشارهگر مشخص با فراخوانی
POINTER()با نوعی که به آن اشاره خواهد شد ایجاد میشوند؛ این کار بهطور خودکار توسطpointer()انجام میشود.اگر یک اشارهگر به یک آرایه اشاره کند، میتوان عناصر آن را با استفاده از دسترسیهای استاندارد زیرنویسی و اسلایسی خواند و نوشت. اشیاء اشارهگر اندازهای ندارند، بنابراین
len()TypeErrorرا پرتاب خواهد کرد. زیرنویسهای منفی حافظهی پیش از اشارهگر را میخوانند (همانطور که در C نیز چنین است)، و اندیسهای خارج از محدوده احتمالاً با نقض دسترسی (access violation) دچار فروپاشی خواهند شد (اگر خوششانس باشید).- _type_¶
نوعی را که به آن اشاره شده است مشخص میکند.
- contents¶
شیءای را که اشارهگر به آن اشاره میکند، برمیگرداند. انتساب به این ویژگی، اشارهگر را طوری تغییر میدهد که به شیء انتسابیافته اشاره کند.
استثناها¶
- exception ctypes.ArgumentError¶
این استثنا زمانی پرتاب میشود که یک فراخوانی تابع خارجی نتواند یکی از آرگومانهای ارسالشده را تبدیل کند.
- exception ctypes.COMError(hresult, text, details)¶
این استثنا زمانی پرتاب میشود که فراخوانی یک متد COM ناموفق باشد.
- hresult¶
مقدار عدد صحیحی که کد خطا را نشان میدهد.
- text¶
پیام خطا.
- details¶
۵-تاییِ
(descr, source, helpfile, helpcontext, progid).descr توضیح متنی است. source
ProgIDوابسته به زبان برای کلاس یا برنامهای است که خطا را پرتاب کرده است. helpfile مسیر پرونده راهنما است. helpcontext شناسه زمینه راهنما است. progidProgIDرابطی است که خطا را تعریف کرده است.
دسترسپذیری: Windows
اضافه شده در نسخهی 3.14.