GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing python.github.io — ctypes --- کتابخانهای از توابع خارجی برای پایتون
|
نوع 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 |
|
تغییر یافته در نسخهی 3.15: انواع F، D و G در _type_ با Zf، Zd و Zg جایگزین شدهاند.
همهی این نوعها را میتوان با فراخوانی آنها بههمراه یک مقدارده اولیهی اختیاری از نوع و مقدار صحیح ایجاد کرد:
>>> 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 >>>
در بسیاری از سکوها، فراخوانی توابع چندآرگومانی آرگومان از طریق 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 تعریف کنید که ویژگی را در صورت درخواست در دسترس قرار میدهد.
میتوان انواع مورد نیاز برای آرگومانهای توابع اکسپورتشده از 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 >>>
تنظیم ویژگی argtypes همچنین باعث میشود که اگر آرگومانهای بسیار کمی ارسال شوند، ctypes استثنای TypeError را بالا بیاورد:
>>> printf(b"%s %d %f\n", b"X", 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: this function takes at least 4 arguments (3 given)
توابعی که از قرارداد فراخوانی stdcall استفاده میکنند، دقیقاً به همان تعداد آرگومان نیاز دارند که در ویژگی argtypes آیتم وجود دارد. توابعی که از قرارداد فراخوانی cdecl استفاده میکنند، برای پشتیبانی از توابع چندآرگومانی، آرگومانهای اضافی را نیز میپذیرند. اعلان باید با امضای واقعی تابع مطابقت داشته باشد؛ ctypes نمیتواند این موضوع را تأیید کند.
اگر کلاسهای خودتان را تعریف کردهاید و آنها را به فراخوانیهای تابع ارسال میکنید، باید برای آنها یک متد کلاس 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' >>>
ساختارها و اجتماعها باید از کلاسهای پایه 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 ۳۲ بیتی کار کند، اما کارکرد آن در حالت کلی توسط کتابخانه تضمین نمیشود. اجتماعها و ساختارهای دارای فیلدهای بیتی باید همیشه بهصورت اشارهگر به توابع ارسال شوند.
بهطور پیشفرض، فیلدهای Structure و Union به همان شیوهای چیدمان میشوند که کامپایلکننده C آنها را چیدمان میکند. با تعیین ویژگی کلاس _layout_ در تعریف زیرکلاس، میتوان این رفتار را بهطور کامل لغو کرد؛ برای جزئیات، مستندات این ویژگی را ببینید.
The _align_ class attribute specifies the minimum alignment
for the structure itself.
See the attribute documentation for details.
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 دریافت کردهاید و میدانید که آن اشارهگر در واقع بهجای یک آیتم واحد، به یک آرایه اشاره میکند.
هشدار
از آنجا که اشیاء اشارهگر از زیرنویسی پشتیبانی میکنند، به طور ضمنی از پیمایش پشتیبانی میکنند. مگر اینکه این کار به صورت کنترلشده انجام شود، مانند فراخوانی دستی next() بر روی یک پیمایشگر pointer()، این کار معمولاً منجر به حلقههای بینهایت یا فروپاشی میشود، زیرا ctypes راهی برای دانستن زمان توقف پیمایش ندارد. به عبارت دیگر، یک پیمایشگر pointer به طور بینهایت حافظهی تصادفی تولید میکند.
در پشت صحنه، تابع 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 >>>
از پایتون 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 انجام شوند.
برخی کتابخانههای اشتراکی نهتنها توابع، بلکه متغیرها را نیز اکسپورت میکنند. نمونهای در خود کتابخانه پایتون Py_Version است، شماره نسخه رانتایم پایتون که در یک عدد صحیح ثابت کدگذاریشده است.
ctypes میتواند با متدهای کلاس in_dll() مربوط به نوع، به مقادیری مانند این دسترسی پیدا کند. pythonapi نمادی از پیش تعریفشده است که دسترسی به API C پایتون را فراهم میکند:
>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version") >>> print(hex(version.value)) 0x30c00a0
An extended example which also demonstrates the use of pointers accesses the
_PyImport_FrozenBootstrap pointer exported by Python. It points to an array
of _frozen records describing frozen modules used during startup,
terminated by one whose members are all NULL or zero. This is similar to
the PyImport_FrozenModules table, which third-party code can use to
provide additional frozen modules.
در اینجا پوششی با 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 packages are indicated by a nonzero is_package member. Python also
includes frozen modules and packages used for testing, such as __hello__
and __phello__, in a separate table, _PyImport_FrozenTest.
در 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 این است که از ماهیت پویا پایتون استفاده کنید و نوع داده را پس از اینکه اندازهی موردنیاز از قبل مشخص شد، بهصورت مورد به مورد (باز)تعریف کنید.
چندین روش برای بارگذاری کتابخانههای مشترک در فرایند پایتون وجود دارد. یکی از روشها، ایجاد نمونهای از CDLL یا یکی از زیرکلاسهای آن است:
نمایانگر یک کتابخانه اشتراکی بارگذاریشده است.
توابع این کتابخانه از قرارداد فراخوانی استاندارد C استفاده میکنند و فرض میشود که int را برمیگردانند. global interpreter lock پایتون پیش از فراخوانی هر تابع اکسپورتشده توسط این کتابخانهها آزاد میشود و پس از آن دوباره گرفته میشود. برای رفتار متفاوت توابع، از یک زیرکلاس استفاده کنید: OleDLL، WinDLL یا PyDLL.
اگر یک handle موجود برای یک کتابخانه اشتراکی از پیش بارگذاریشده دارید، میتوان آن را بهعنوان آرگومان handle برای دربرگرفتن کتابخانه بازشده در قالب یک شیء جدید CDLL ارسال کرد. در این حالت، name فقط برای تنظیم ویژگی _name استفاده میشود، اما ممکن است تنظیم و/یا اعتبارسنجی شود.
اگر handle برابر با None باشد، تابع dlopen(3) یا LoadLibraryExW پلتفرم زیرین برای بارگذاری کتابخانه در فرایند و به دست آوردن دستهای به آن استفاده میشود.
name مسیر کتابخانه اشتراکی برای باز کردن است. اگر name شامل جداکننده مسیر نباشد، کتابخانه به شیوهای وابسته به پلتفرم یافت میشود.
در ویندوز، پسوند .DLL ممکن است وجود نداشته باشد. (برای جزئیات، مستندات LoadLibraryExW را ببینید.) سایر پیشوندها و پسوندهای مختص پلتفرم (بهعنوانمثال، lib، .so، .dylib یا شمارههای نسخه) باید در name موجود باشند؛ آنها بهطور خودکار اضافه نمیشوند. برای اطلاعات بیشتر یافتن کتابخانههای مشترک را ببینید.
در سیستمهای غیر ویندوزی، name میتواند None باشد. در این صورت، dlopen() با NULL فراخوانی میشود، که برنامهی اصلی را به عنوان یک «کتابخانه» باز میکند. (برخی سیستمها اگر name خالی باشد همین کار را انجام میدهند؛ None/NULL قابلیت حمل بالاتری دارد.)
جزئیات پیادهسازی CPython
از آنجا که CPython به libc پیوند داده شده است، اغلب از یک name با مقدار None برای دسترسی به کتابخانه استاندارد C استفاده میشود:
>>> printf = ctypes.CDLL(None).printf >>> printf.argtypes = [ctypes.c_char_p] >>> printf(b"hello\n") hello 6
برای دسترسی به Python C API، ترجیحاً از ctypes.pythonapi استفاده کنید که در همه پلتفرمها کار میکند.
از پارامتر mode میتوان برای مشخص کردن نحوه بارگذاری کتابخانه استفاده کرد. برای جزئیات، به صفحه راهنمای dlopen(3) مراجعه کنید. در ویندوز، mode نادیده گرفته میشود. در سیستمهای POSIX، RTLD_NOW همیشه اضافه میشود و قابل پیکربندی نیست.
پارامتر use_errno، هنگامی که روی درست تنظیم شود، مکانیزمی از ctypes را فعال میکند که اجازه میدهد به شماره خطای errno سیستمی به روشی امن دسترسی پیدا کنید. ماژول ctypes یک کپی نخمحلی از متغیر errno سیستم را نگهداری میکند؛ اگر توابع خارجی ایجادشده با use_errno=True را فراخوانی کنید، آنگاه مقدار errno قبل از فراخوانی تابع با کپی خصوصی ctypes جابهجا میشود، و همین اتفاق بلافاصله پس از فراخوانی تابع نیز میافتد. این مکانیزم فقط برای کتابخانههایی کار میکند که از همان کتابخانهی زمان اجرای C (C runtime) خود مفسر استفاده میکنند. کتابخانهای که به یک زمان اجرای C متفاوت (مثلاً یک CRT متفاوت در ویندوز) لینک شده باشد، errno مخصوص به خود را دارد که ctypes نمیتواند آن را مشاهده کند.
تابع ctypes.get_errno() مقدار نسخه خصوصی ctypes را برمیگرداند، و تابع ctypes.set_errno() نسخه خصوصی ctypes را به یک مقدار جدید تغییر میدهد و مقدار پیشین را برمیگرداند.
پارامتر use_last_error، هنگامی که روی true تنظیم شود، همان سازوکار را برای کد خطای ویندوز، که توسط توابع API ویندوز GetLastError() و SetLastError() مدیریت میشود، فعال میکند؛ از ctypes.get_last_error() و ctypes.set_last_error() برای درخواست و تغییر نسخهی خصوصی ctypes از کد خطای ویندوز استفاده میشود.
پارامتر winmode در ویندوز برای مشخص کردن نحوهی بارگذاری کتابخانه استفاده میشود (زیرا mode نادیده گرفته میشود). این پارامتر هر مقداری را که برای پرچمهای API ویندوز در LoadLibraryExW معتبر باشد میپذیرد. هنگامی که این پارامتر حذف شود، پیشفرض استفاده از پرچمهایی است که امنترین بارگذاری DLL را به همراه دارند، و این امر از مشکلاتی نظیر ربایش DLL جلوگیری میکند. ارسال مسیر کامل به DLL امنترین راه برای اطمینان از بارگذاری کتابخانه و وابستگیهای درست است.
در ویندوز، ایجاد یک نمونه از CDLL ممکن است حتی اگر نام DLL وجود داشته باشد شکست بخورد. هنگامی که یک DLL وابسته به DLL بارگذاریشده پیدا نشود، یک خطای OSError با پیام "[WinError 126] The specified module could not be found". پرتاب میشود. این پیام خطا حاوی نام DLL مفقود نیست، زیرا API ویندوز این اطلاعات را برنمیگرداند و این موضوع، تشخیص این خطا را دشوار میکند. برای رفع این خطا و تعیین اینکه کدام DLL پیدا نشده است، باید فهرست DLLهای وابسته را پیدا کنید و با استفاده از ابزارهای اشکالزدایی و ردگیری ویندوز مشخص کنید کدامیک پیدا نشده است.
همچنین ببینید
ابزار DUMPBIN مایکروسافت -- ابزاری برای یافتن وابستههای DLL.
تغییر یافته در نسخهی 3.8: پارامتر winmode اضافه شد.
تغییر یافته در نسخهی 3.12: پارامتر name اکنون میتواند یک path-like object باشد.
نمونههای این کلاس هیچ متد عمومی ندارند. میتوان به توابع اکسپورتشده توسط کتابخانه مشترک بهصورت صفت یا از طریق اندیس دسترسی داشت. لطفاً توجه داشته باشید که دسترسی به تابع از طریق یک صفت، نتیجه را در نهانگاه ذخیره میکند و بنابراین دسترسی مکرر به آن هر بار همان شیء را برمیگرداند. از سوی دیگر، دسترسی به آن از طریق یک اندیس هر بار یک شیء جدید برمیگرداند:
>>> from ctypes import CDLL >>> libc = CDLL("libc.so.6") # On Linux >>> libc.time == libc.time True >>> libc['time'] == libc['time'] False
ویژگیهای عمومی زیر در دسترس هستند. نام آنها با یک زیرخط آغاز میشود تا با نام توابع اکسپورتشده تداخل نداشته باشد:
دستهی سیستمی که برای دسترسی به کتابخانه استفاده میشود.
نام کتابخانهای که در سازنده ارسال میشود.
برای اطلاعات مشترک، CDLL، ابرکلاس را ببینید.
توابع این کتابخانه از قرارداد فراخوانی stdcall استفاده میکنند، و فرض میشود کد HRESULT مخصوص ویندوز را برمیگردانند. مقادیر HRESULT حاوی اطلاعاتی هستند که مشخص میکنند آیا فراخوانی تابع ناموفق بوده است یا موفق، به همراه کد خطای اضافی. اگر مقدار بازگشتی نشاندهندهی شکست باشد، یک OSError بهطور خودکار پرتاب میشود.
دسترسپذیری: Windows
تغییر یافته در نسخهی 3.3: پیشتر WindowsError پرتاب میشد، که اکنون نام مستعاری از OSError است.
برای اطلاعات مشترک، CDLL، ابرکلاس را ببینید.
توابع در این کتابخانهها از قرارداد فراخوانی stdcall استفاده میکنند، و فرض میشود که بهطور پیشفرض int برمیگردانند.
دسترسپذیری: Windows
برای اطلاعات مشترک، CDLL، ابرکلاس را ببینید.
هنگامی که توابع این کتابخانه فراخوانی میشوند، GIL پایتون در حین فراخوانی تابع آزاد نمیشود، و پس از اجرای تابع، پرچم خطای پایتون بررسی میشود. اگر پرچم خطا تنظیم شده باشد، یک استثنای پایتون پرتاب میشود.
بنابراین، این تنها برای فراخوانی مستقیم توابع Python C API مفید است.
پرچمی که بهعنوان پارامتر mode استفاده میشود. در سکوهای فاقد این پرچم، بهصورت عدد صحیح صفر تعریف شده است.
پرچمی برای استفاده بهعنوان پارامتر mode. در سکوهایی که این در دسترس نیست، همانند RTLD_GLOBAL است.
حالت پیشفرضی که برای بارگذاری کتابخانههای اشتراکی به کار میرود. در OSX 10.3، این RTLD_GLOBAL است، در غیر این صورت همانند RTLD_LOCAL است.
همچنین میتوان کتابخانههای اشتراکی را با استفاده از یکی از اشیاء از پیش ساختهشده، که نمونههایی از کلاس LibraryLoader هستند، بارگذاری کرد؛ خواه با فراخوانی متد LoadLibrary()، خواه با بازیابی کتابخانه بهعنوان ویژگیِ نمونهی بارگذار.
کلاسی که کتابخانههای اشتراکی را بارگذاری میکند. dlltype باید یکی از انواع CDLL، PyDLL، WinDLL یا OleDLL باشد.
__getattr__() رفتار ویژهای دارد: این متد امکان بارگذاری یک کتابخانه مشترک را با دسترسی به آن بهعنوان یک ویژگی از یک نمونه بارگذار کتابخانه فراهم میکند. نتیجه در نهانگاه ذخیره میشود، بنابراین دسترسیهای مکرر به ویژگی، هر بار همان کتابخانه را برمیگردانند.
یک کتابخانه اشتراکی را در فرایند بارگذاری میکند و آن را برمیگرداند. این متد همیشه یک نمونه جدید از کتابخانه را برمیگرداند.
این بارگذارهای کتابخانهای از پیش ساختهشده در دسترس هستند:
- ctypes.windll¶
نمونههای
WinDLLرا ایجاد میکند.دسترسپذیری: Windows
- ctypes.oledll¶
نمونههای
OleDLLرا ایجاد میکند.دسترسپذیری: Windows
- ctypes.pythonapi¶
نمونهای از
PyDLLکه توابع Python C API را بهعنوان ویژگیها در دسترس قرار میدهد. توجه داشته باشید که فرض میشود همهی این توابع مقداری از نوع C int را برمیگردانند، که البته همیشه درست نیست، بنابراین برای استفاده از این توابع باید ویژگیrestypeصحیح را اختصاص دهید.نکته
اگر مفسر پایتون بهصورت ایستا پیوند داده شده باشد، این مقدار ممکن است
Noneباشد.
بارگذاری یک کتابخانه از طریق هر یک از این اشیاء، یک رویداد حسابرسی ctypes.dlopen را با آرگومان رشتهای name، نام استفادهشده برای بارگذاری کتابخانه، پرتاب میکند.
دسترسی به یک تابع در یک کتابخانه بارگذاریشده، باعث پرتاب یک رویداد حسابرسی ctypes.dlsym با آرگومانهای library (شیء کتابخانه) و name (نام نماد بهصورت یک رشته یا عدد صحیح) میشود.
در مواردی که به جای شیء، تنها دستهی کتابخانه در دسترس باشد، دسترسی به یک تابع، رویداد حسابرسی ctypes.dlsym/handle را با آرگومانهای handle (دستهی خام کتابخانه) و name پرتاب میکند.
هنگام برنامهنویسی به یک زبان کامپایلشده، کتابخانههای مشترک هنگام کامپایل/پیوند دادن یک برنامه و همچنین هنگام اجرای برنامه مورد دسترسی قرار میگیرند. برنامهنویس یک نام کوتاه مشخص میکند؛ سپس کامپایلر C، پیونددهنده و بارگذار کتابخانهی پویای رانتایم به روشهای مختص سیستم تعامل میکنند تا نام پروندهی کتابخانه را برای بارگذاری پیدا کنند.
در حالی که نگاشت از نامهای کوتاه به نام پروندهها بهطور سازگار توسط پلتفرمها در معرض دید قرار نمیگیرد، ماژول ctypes.util تابعی به نام find_library() فراهم میکند که تلاش میکند آن را مطابقت دهد. با این حال، از آنجا که نگرانیهای مربوط به سازگاری رو به عقب تنظیم رفتار آن را برای پلتفرمها و پیکربندیهای جدید دشوار میکند، این تابع بهطور نرم منسوخشده است.
اگر در حال پوششدهی یک کتابخانهی مشترک با ctypes هستید، به جای استفاده از find_library() برای یافتن کتابخانه در زمان اجرای برنامه (رانتایم)، تعیین نام کتابخانهی مشترک را در زمان توسعه در نظر بگیرید و آن را به صورت ثابت (سختکد) در ماژول پوششی قرار دهید. همچنین اضافه کردن یک گزینهی پیکربندی یا متغیر محیطی را برای اجازه دادن به کاربران جهت انتخاب کتابخانهی مورد استفاده در نظر بگیرید، و سپس شاید از find_library() به عنوان پیشفرض یا جایگزین استفاده کنید.
تلاش برای یافتن یک کتابخانه و بازگرداندن یک مسرنام.
name نام «کوتاه» کتابخانه بدون هیچ پیشوندی مانند lib، پسوندی مانند .so، .dylib یا شمارهی نسخه است. (این شکلی است که برای گزینهی پیونددهندهی posix یعنی -l استفاده میشود.) نتیجه در قالبی مناسب برای ارسال به CDLL است.
اگر هیچ کتابخانهای پیدا نشود، None را بازمیگرداند.
عملکرد دقیق به سیستم وابسته است و تضمین نمیشود که با رفتار کامپایلر، پیونددهنده و بارگذار مورد استفاده برای (یا توسط) پایتون مطابقت داشته باشد. توصیه میشود فقط از این تابع به عنوان پیشفرض یا جایگزین استفاده کنید،
منسوخسازی نرم <Soft deprecated> از نسخهی 3.15: این تابع برای استفاده در مواردی که کار میکند حفظ شده است، اما انتظار نمیرود برای پلتفرمها و پیکربندیهای اضافی بهروزرسانی شود.
در لینوکس، find_library() تلاش میکند برنامههای خارجی (/sbin/ldconfig، gcc، objdump و ld) را اجرا کند تا پروندهی کتابخانه را بیابد. اگر خروجی این برنامهها با پیونددهندهی پویای مورد استفادهی پایتون مطابقت نداشته باشد، ممکن است نتیجهی این تابع گمراهکننده باشد.
تغییر یافته در نسخهی 3.6: در لینوکس، هنگام جستوجوی کتابخانهها، اگر کتابخانهای به هیچ روش دیگری یافت نشود، مقدار متغیر محیطی LD_LIBRARY_PATH استفاده میشود.
در اینجا چند مثال آمده است:
>>> from ctypes.util import find_library >>> find_library("m") 'libm.so.6' >>> find_library("c") 'libc.so.6' >>> find_library("bz2") 'libbz2.so.1.0' >>>
در مکاواس و اندروید، find_library() از الگوها و مسیرهای نامگذاری استاندارد سیستم برای یافتن کتابخانه استفاده میکند و در صورت موفقیت، یک مسیرنام کامل را برمیگرداند:
>>> from ctypes.util import find_library >>> find_library("c") '/usr/lib/libc.dylib' >>> find_library("m") '/usr/lib/libm.dylib' >>> find_library("bz2") '/usr/lib/libbz2.dylib' >>> find_library("AGL") '/System/Library/Frameworks/AGL.framework/AGL' >>>
در ویندوز، find_library() مسیر جستجوی سیستم را جستجو میکند و مسیرنام کامل را برمیگرداند، اما از آنجا که هیچ الگوی نامگذاری از پیش تعریفشدهای وجود ندارد، فراخوانیای مانند find_library("c") با شکست مواجه شده و None را برمیگرداند.
نام پروندهی کتابخانهی رانتایم VC مورد استفادهی پایتون و ماژولهای توسعهای را برمیگرداند.
اگر نام کتابخانه قابل تشخیص نباشد، مقدار None برگردانده میشود. به طور ویژه، این اتفاق برای نسخههای جدیدتر کتابخانهی رانتایم VC رخ میدهد که مستقیماً قابل بارگذاری نیستند.
اگر نیاز دارید حافظهای را آزاد کنید، برای مثال حافظهای که توسط یک ماژول توسعه تخصیص داده شده است، با فراخوانی free(void *)، مهم است که از تابع در همان کتابخانهای استفاده کنید که آن حافظه را تخصیص داده است.
دسترسپذیری: Windows
هنگام نوشتن کدی که به کد بارگذاریشده از کتابخانههای اشتراکی متکی است، دانستن اینکه کدام کتابخانههای اشتراکی از قبل در فرایند جاری بارگذاری شدهاند، میتواند مفید باشد.
ماژول ctypes.util تابع dllist() را فراهم میکند، که APIهای متفاوتی را که پلتفرمهای گوناگون ارائه میدهند فراخوانی میکند تا به تعیین اینکه کدام کتابخانههای اشتراکی از قبل در فرایند جاری بارگذاری شدهاند کمک کند.
خروجی دقیق این تابع به سیستم وابسته است. در بیشتر پلتفرمها، نخستین آیتم این فهرست خود فرایند جاری را نشان میدهد، که ممکن است یک رشته خالی باشد. برای مثال، در لینوکس مبتنی بر glibc، ممکن است مقدار بازگشتی به شکل زیر باشد:
>>> from ctypes.util import dllist >>> dllist() ['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ]
همانطور که در بخش پیشین توضیح داده شد، توابع خارجی بهعنوان ویژگیهای کتابخانههای اشتراکی بارگذاریشده قابل دسترسی هستند. اشیای تابعی که به این روش ایجاد میشوند، بهطور پیشفرض هر تعداد آرگومان را میپذیرند، هر نمونه دادهای از ctypes را بهعنوان آرگومان میپذیرند و نوع نتیجه پیشفرضی که توسط بارگذار کتابخانه مشخص شده است را برمیگردانند.
آنها نمونههایی از یک کلاس محلی خصوصی _FuncPtr هستند (در ctypes در معرض نیست) که از کلاس خصوصی _CFuncPtr ارث میبرد:
>>> import ctypes >>> lib = ctypes.CDLL(None) >>> issubclass(lib._FuncPtr, ctypes._CFuncPtr) True >>> lib._FuncPtr is ctypes._CFuncPtr False
کلاس پایه برای توابع خارجی فراخوانیپذیر در C.
نمونههای توابع خارجی نیز انواع داده سازگار با C هستند؛ این نمونهها اشارهگرهای توابع C را نشان میدهند.
این رفتار را میتوان با انتساب به ویژگیهای خاص شیء تابع خارجی سفارشی کرد.
برای مشخص کردن نوع نتیجه تابع خارجی، یک نوع ctypes را انتساب کنید. برای void، تابعی که چیزی را برنمیگرداند، از None استفاده کنید.
میتوان یک شیء پایتونی فراخوانیپذیر را که یک نوع ctypes نیست، انتساب داد؛ در این صورت فرض میشود که تابع یک int در C را برمیگرداند و شیء فراخوانیپذیر با این عدد صحیح فراخوانی میشود تا امکان پردازش بیشتر یا بررسی خطا فراهم شود. استفاده از این روش منسوخ است؛ برای پسپردازش یا بررسی خطای انعطافپذیرتر، از یک نوع داده ctypes بهعنوان restype استفاده کنید و یک شیء فراخوانیپذیر را به ویژگی errcheck انتساب دهید.
یک تاپل از انواع دادهی ctypes را اختصاص دهید تا انواع آرگومانهایی که تابع میپذیرد مشخص شوند. اگر تابع با آرگومانهای کمتری نسبت به طول این تاپل فراخوانی شود، استثنای TypeError بالا آورده میشود. توابعی که از قرارداد فراخوانی stdcall استفاده میکنند نیز اگر با آرگومانهای بیشتری فراخوانی شوند استثنای TypeError را بالا میآورند؛ توابعی که از قرارداد فراخوانی cdecl استفاده میکنند، آرگومانهای اضافی و مشخصنشده را نیز میپذیرند.
هنگامی که یک تابع خارجی فراخوانی میشود، هر آرگومان واقعی به متد کلاس from_param() آیتمهای موجود در تاپل argtypes ارسال میشود؛ این متد امکان سازگار کردن آرگومان واقعی با شیءای را فراهم میکند که تابع خارجی میپذیرد. برای مثال، یک آیتم c_char_p در تاپل argtypes، رشتهای را که بهعنوان آرگومان ارسالشده است، با استفاده از قوانین تبدیل ctypes به یک شیء bytes تبدیل میکند.
جدید: اکنون میتوان آیتمهایی را در argtypes قرار داد که از انواع ctypes نیستند، اما هر آیتم باید یک متد from_param() داشته باشد که مقداری قابلاستفاده بهعنوان آرگومان (عدد صحیح، رشته، نمونه ctypes) برمیگرداند. این امکان تعریف آداپتورهایی را میدهد که میتوانند اشیاء سفارشی را بهعنوان پارامترهای تابع سازگار کنند.
یک تابع پایتون یا یک شیء فراخوانیپذیر دیگر را به این ویژگی اختصاص دهید. این شیء قابل فراخوانی با سه آرگومان یا بیشتر فراخوانی میشود:
result چیزی است که تابع خارجی برمیگرداند، همانطور که توسط ویژگی restype مشخص شده است.
func خودِ شیء تابع خارجی است، این امکان را میدهد که از همان شیء فراخوانیپذیر برای بررسی یا پسپردازش نتایج چندین تابع مجدداً استفاده شود.
پارامتر arguments یک تاپل است که شامل پارامترهای ارسالشده به فراخوانی تابع، از جمله هر پارامتر خروجی که ctypes برای فراخوانی ایجاد کرده است، میشود؛ این امکان را فراهم میکند تا رفتار تابع روی آرگومانهای استفادهشده تخصصی شود.
اگر شیء فراخوانیپذیر همان تاپل arguments دریافتی را بدون تغییر برگرداند، ctypes پردازش عادی نتیجهی خود را ادامه میدهد، که شامل بازگرداندن پارامترهای خروجی است (به پیشنمونههای تابع مراجعه کنید). هر شیء دیگری که توسط شیء فراخوانیپذیر برگردانده شود، به جای فراخوانی تابع خارجی بازگردانده میشود. شیء فراخوانیپذیر همچنین میتواند مقدار نتیجه را بررسی کرده و در صورت ناموفق بودن فراخوانی تابع خارجی، یک استثنا بالا بیاورد.
برای مثال، این شیء فراخوانیپذیر زمانی که تابع خارجی با یک مقدار بازگشتی منفی سیگنال شکست میدهد، یک استثنا بالا میآورد و در غیر این صورت نتیجه را به ctypes واگذار میکند:
def errcheck(result, func, arguments): if result < 0: raise RuntimeError(f"call failed with error {result}") return arguments
این مورد به جای آن، نتیجه را جایگزین میکند و یک int زبان C را که به عنوان مقدار بولی استفاده شده است، به یک bool تبدیل میکند:
def errcheck(result, func, arguments): return result != 0
در ویندوز، هنگامی که یک فراخوانی تابع خارجی یک استثنای سیستمی را پرتاب میکند (برای مثال، بهدلیل نقض دسترسی)، آن استثنا گرفته میشود و با یک استثنای پایتون مناسب جایگزین میگردد. علاوه بر این، یک رویداد حسابرسی ctypes.set_exception با آرگومان code پرتاب میشود که به یک قلاب حسابرسی اجازه میدهد استثنا را با استثنای خود جایگزین کند.
برخی روشهای فراخوانی توابع خارجی و همچنین برخی از توابع این ماژول ممکن است رویداد حسابرسی ctypes.call_function را با آرگومانهای function pointer و arguments پرتاب کنند.
همچنین میتوان توابع خارجی را با نمونهسازی از پیشنمونههای تابع (function prototypes) ایجاد کرد. پیشنمونههای تابع مشابه پیشنمونههای تابع در C هستند؛ آنها یک تابع را (نوع بازگشتی، انواع آرگومانها، قرارداد فراخوانی) بدون تعریف پیادهسازی توصیف میکنند. توابع کارخانه باید با نوع نتیجه دلخواه و انواع آرگومانهای تابع فراخوانی شوند و میتوانند بهعنوان کارخانههای دکوراتور استفاده شوند و به همین عنوان، از طریق سینتکس @wrapper به توابع اعمال شوند. برای نمونهها، توابع کالبک را ببینید.
پیشنمونه تابع برگرداندهشده، توابعی را ایجاد میکند که از قرارداد فراخوانی استاندارد C استفاده میکنند. تابع در حین فراخوانی، GIL را آزاد میکند. اگر use_errno روی true تنظیم شود، نسخهی خصوصی ctypes از متغیر سیستمی errno پیش و پس از فراخوانی با مقدار واقعی errno مبادله میشود؛ use_last_error نیز همین کار را برای کد خطای ویندوز انجام میدهد.
پیشنمونه تابع برگشتی، توابعی را ایجاد میکند که از قرارداد فراخوانی stdcall استفاده میکنند. تابع در حین فراخوانی، GIL را آزاد خواهد کرد. use_errno و use_last_error همان معنای بالا را دارند.
دسترسپذیری: Windows
پیشنمونهی تابع بازگرداندهشده توابعی را ایجاد میکند که از قرارداد فراخوانی پایتون استفاده میکنند. برخلاف CFUNCTYPE() و WINFUNCTYPE()، کتابخانهی ctypes پیش از فراخوانی تابع خارجی، GIL را آزاد نمیکند، بنابراین تابع ممکن است با خیال راحت از API زبان C پایتون استفاده کند. توجه داشته باشید که این موضوع مانع از آزاد شدن GIL در حین فراخوانی نمیشود.
میتوان پیشنمونههای تابع ایجادشده توسط این توابع کارخانهای را به روشهای مختلف نمونهسازی کرد، بسته به نوع و تعداد پارامترها در فراخوانی:
یک تابع خارجی را در آدرس مشخصشده برمیگرداند؛ این آدرس باید یک عدد صحیح باشد.
یک تابع فراخوانیپذیر در C (یک تابع کالبک) از یک callable در پایتون ایجاد کنید.
یک تابع خارجی اکسپورتشده از یک کتابخانه اشتراکی را بازمیگرداند. func_spec باید یک تاپل دوتایی به صورت (name_or_ordinal, library) باشد. آیتم اول، نام تابع اکسپورتشده به صورت رشته، یا شماره ترتیبی تابع اکسپورتشده به صورت عدد صحیح کوچک است. آیتم دوم، نمونهای از کتابخانه اشتراکی است.
یک تابع خارجی را برمیگرداند که یک متد 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 >>>
نشانی بافر حافظه را بهصورت عدد صحیح برمیگرداند. obj باید نمونهای از یک نوع ctypes باشد.
یک رویداد حسابرسی ctypes.addressof را با آرگومان obj پرتاب میکند.
الزامات همترازی یک نوع ctypes را برمیگرداند. obj_or_type باید یک نوع یا نمونه از ctypes باشد.
یک اشارهگر سبکوزن به obj برمیگرداند، که باید نمونهای از یک نوع ctypes باشد. offset بهطور پیشفرض صفر است و باید یک عدد صحیح باشد که به مقدار اشارهگر داخلی افزوده میشود.
byref(obj, offset) معادل این کد C است:
(((char *)&obj) + offset)
شیء برگرداندهشده تنها میتواند بهعنوان پارامتر فراخوانی تابع خارجی استفاده شود. رفتار آن مشابه pointer(obj) است، اما ساخت آن بسیار سریعتر است.
یک اشارهگر COM را از src به dst کپی میکند و مقدار HRESULT مختص ویندوز را برمیگرداند.
اگر src برابر NULL نباشد، متد AddRef آن فراخوانی میشود و شمار ارجاع را افزایش میدهد.
در مقابل، شمار ارجاع dst پیش از انتساب مقدار جدید کاهش نمییابد. مگر اینکه dst برابر NULL باشد، فراخواننده مسئول کاهش شمار ارجاع با فراخوانی متد Release آن در صورت لزوم است.
دسترسپذیری: Windows
اضافه شده در نسخهی 3.14.
این تابع مشابه عملگر تبدیل نوع (cast) در C است. این تابع نمونهی جدیدی از type را بازمیگرداند که به همان بلوک حافظهای اشاره میکند که obj به آن اشاره دارد. type باید یک نوع اشارهگر باشد و obj باید شیءای باشد که بتوان آن را بهعنوان یک اشارهگر تفسیر کرد.
این تابع یک بافر نویسهای تغییرپذیر ایجاد میکند. شیء برگرداندهشده یک آرایه 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 از c_wchar است.
این تابع همان آرگومانهایی را میپذیرد که create_string_buffer() میپذیرد، بهجز اینکه init باید یک رشته باشد و size تعداد c_wchar را میشمارد.
یک رویداد حسابرسی ctypes.create_unicode_buffer را با آرگومانهای init و size پرتاب میکند.
این تابع یک قلاباست که امکان پیادهسازی سرورهای COM درونفرایندی با ctypes را فراهم میکند. این تابع از تابع DllCanUnloadNow فراخوانی میشود که dll افزونهای _ctypes آن را اکسپورت میکند.
دسترسپذیری: Windows
این تابع یک قلاباست که امکان پیادهسازی سرورهای COM درونفرایندی را با ctypes فراهم میکند. این تابع از تابع DllGetClassObject فراخوانی میشود که dll افزونه _ctypes آن را اکسپورت میکند.
دسترسپذیری: Windows
تلاش میکند فهرستی از مسیرهای کتابخانههای اشتراکی بارگذاریشده در فرایند جاری را ارائه کند. این مسیرها به هیچ وجه بههنجار یا پردازش نمیشوند. اگر APIهای پلتفرم زیرین شکست بخورند، این تابع ممکن است OSError را پرتاب کند. عملکرد دقیق آن وابسته به سیستم است.
در بیشتر پلتفرمها، اولین عنصر فهرست نمایانگر پرونده اجرایی جاری است. ممکن است یک رشته خالی باشد.
دسترسپذیری: Windows, macOS, iOS, glibc, BSD libc, musl
اضافه شده در نسخهی 3.14.
یک شرح متنی از کد خطای code را برمیگرداند. اگر کد خطایی مشخص نشده باشد، با فراخوانی تابع API ویندوز GetLastError() از آخرین کد خطا استفاده میشود.
دسترسپذیری: Windows
آخرین کد خطایی که ویندوز در نخ فراخواننده تنظیم کرده است را بازمیگرداند. این تابع مستقیماً تابع GetLastError() ویندوز را فراخوانی میکند و نسخهی خصوصی ctypes از کد خطا را بازنمیگرداند.
دسترسپذیری: Windows
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی errno را در نخ فراخوان برمیگرداند.
یک رویداد حسابرسی ctypes.get_errno را بدون آرگومان پرتاب میکند.
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی LastError را در نخ فراخوان برمیگرداند.
دسترسپذیری: Windows
یک رویداد حسابرسی ctypes.get_last_error را بدون آرگومان پرتاب میکند.
مشابه تابع memmove در کتابخانه استاندارد C: count بایت را از src به dst کپی میکند. dst و src باید اعداد صحیح یا نمونههای ctypes باشند که بتوان آنها را به اشارهگرها تبدیل کرد.
مشابه تابع کتابخانهای استاندارد memset در C: بلوک حافظه در نشانی dst را با count بایت به مقدار c پر میکند. dst باید یک عدد صحیح مشخصکننده یک نشانی، یا نمونهای از ctypes باشد.
یک نوع اشارهگر ctypes را ایجاد میکند یا بازمیگرداند. انواع اشارهگر بهصورت داخلی در نهانگاه ذخیره و دوباره استفاده میشوند، بنابراین فراخوانی مکرر این تابع کمهزینه است. type باید یک نوع ctypes باشد.
نوع اشارهگر حاصل در ویژگی __pointer_type__ از type بهعنوان نهانگاه ذخیره میشود. میتوان این ویژگی را پیش از نخستین فراخوانی POINTER تنظیم کرد تا یک نوع اشارهگر سفارشی تنظیم شود. با این حال، انجام این کار توصیه نمیشود: ایجاد دستی یک نوع اشارهگر مناسب بدون تکیه بر جزئیات پیادهسازی که ممکن است در نسخههای آینده پایتون تغییر کنند، دشوار است.
یک نمونه اشارهگر جدید، که به obj اشاره دارد، ایجاد کنید. شیء برگرداندهشده از نوع POINTER(type(obj)) است.
نکته: اگر فقط میخواهید یک اشارهگر به یک شیء را به یک فراخوانی تابع خارجی ارسال کنید، باید از byref(obj) استفاده کنید که بسیار سریعتر است.
این تابع بافر حافظهی داخلی obj را تغییر اندازه میدهد؛ obj باید نمونهای از یک نوع ctypes باشد. کوچک کردن بافر به کمتر از اندازهی بومی نوع شیء، همانطور که با sizeof(type(obj)) مشخص میشود، امکانپذیر نیست، اما بزرگ کردن بافر امکانپذیر است.
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی errno در نخ فراخواننده را برابر value قرار میدهد و مقدار قبلی را برمیگرداند.
یک رویداد حسابرسی ctypes.set_errno را با آرگومان errno پرتاب میکند.
مقدار فعلی نسخهی خصوصی ctypes از متغیر سیستمی LastError را در نخ فراخوان به value تنظیم میکند و مقدار قبلی را برمیگرداند.
دسترسپذیری: Windows
یک رویداد حسابرسی ctypes.set_last_error را با آرگومان error پرتاب میکند.
اندازهی یک نوع ctypes یا بافر حافظهی یک نمونه را بر حسب بایت برمیگرداند. همانند عملگر sizeof در C عمل میکند.
رشته بایتی در void *ptr را برمیگرداند. اگر size مشخص شده باشد، بهعنوان اندازه استفاده میشود، در غیر این صورت فرض میشود که رشته با صفر خاتمه یافته است.
یک رویداد حسابرسی ctypes.string_at را با آرگومانهای ptr و size پرتاب میکند.
یک نمونه از OSError ایجاد میکند. اگر code مشخص نشده باشد، برای تعیین کد خطا GetLastError() فراخوانی میشود. اگر descr مشخص نشده باشد، برای دریافت شرح متنی خطا FormatError() فراخوانی میشود.
دسترسپذیری: Windows
تغییر یافته در نسخهی 3.3: پیشتر نمونهای از WindowsError ایجاد میشد، که اکنون نام مستعاری برای OSError است.
رشته نویسههای پهن در void *ptr را برمیگرداند. اگر size تعیین شده باشد، از آن به عنوان تعداد نویسههای رشته استفاده میشود، در غیر این صورت فرض میشود که رشته با صفر پایان مییابد.
یک رویداد حسابرسی ctypes.wstring_at را با آرگومانهای ptr و size پرتاب میکند.
یک شیء 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.
این کلاس غیرعمومی، کلاس پایه مشترک تمام انواع داده ctypes است. در میان سایر موارد، تمام نمونههای نوع ctypes شامل یک بلوک حافظه هستند که دادههای سازگار با C را نگه میدارد؛ آدرس بلوک حافظه توسط تابع کمکی addressof() برگردانده میشود. متغیر نمونه دیگری نیز بهعنوان _objects در معرض قرار گرفته است؛ این متغیر شامل اشیاء پایتون دیگری است که در صورتی که بلوک حافظه حاوی اشارهگرها باشد، باید زنده نگه داشته شوند.
متدهای مشترک انواع داده ctypes، همگی متدهای کلاس هستند (بهطور دقیق، آنها متدهای فراکلاس هستند):
این متد یک نمونه ctypes برمیگرداند که بافر شیء source را به اشتراک میگذارد. شیء source باید از رابط بافر قابلنوشتن پشتیبانی کند. پارامتر اختیاری offset یک آفست درون بافر source را بر حسب بایت مشخص میکند؛ مقدار پیشفرض ۰ است. اگر بافر source بهقدر کافی بزرگ نباشد، یک ValueError پرتاب میشود.
یک رویداد حسابرسی ctypes.cdata/buffer را با آرگومانهای pointer، size، offset پرتاب میکند.
این متد یک نمونه ctypes ایجاد میکند و بافر را از بافر شیء source کپی میکند؛ بافر شیء source باید خواندنی باشد. پارامتر اختیاری offset، یک آفست در بافر منبع را بر حسب بایت مشخص میکند؛ مقدار پیشفرض ۰ است. اگر بافر منبع بهاندازه کافی بزرگ نباشد، یک ValueError پرتاب میشود.
یک رویداد حسابرسی ctypes.cdata/buffer را با آرگومانهای pointer، size، offset پرتاب میکند.
این متد یک نمونهی نوع ctypes را با استفاده از حافظهی مشخصشده توسط address برمیگرداند؛ address باید یک عدد صحیح باشد.
این متد، و سایر متدهایی که بهطور غیرمستقیم این متد را فراخوانی میکنند، یک رویداد حسابرسی ctypes.cdata را با آرگومان address پرتاب میکنند.
این متد obj را با یک نوع ctypes تطبیق میدهد. هنگامی که نوع در تاپل argtypes تابع خارجی وجود داشته باشد، این متد با شیء واقعی استفادهشده در یک فراخوانی تابع خارجی فراخوانی میشود؛ این متد باید یک شیء برگرداند که بتوان از آن بهعنوان پارامتر فراخوانی تابع استفاده کرد.
تمام انواع داده ctypes یک پیادهسازی پیشفرض از این classmethod دارند که بهطور معمول obj را، در صورتی که نمونهای از آن نوع باشد، برمیگرداند. برخی از انواع، اشیاء دیگر را نیز میپذیرند.
این متد یک نمونه از نوع ctypes را بازمیگرداند که توسط یک کتابخانه مشترک اکسپورت شده است. name نام نمادی است که دادهها را اکسپورت میکند، و library کتابخانه مشترک بارگذاریشده است.
متغیرهای کلاس مشترک انواع داده ctypes:
نوع اشارهگری که با فراخوانی POINTER() برای نوع دادهی متناظر در ctypes ایجاد شده است. اگر نوع اشارهگر هنوز ایجاد نشده باشد، این ویژگی وجود ندارد.
اضافه شده در نسخهی 3.14.
متغیرهای نمونه مشترک انواع داده ctypes:
گاهی نمونههای داده ctypes مالک بلوک حافظهای که در بر دارند، نیستند؛ بلکه بخشی از بلوک حافظه یک شیء پایه را به اشتراک میگذارند. عضو فقطخواندنی _b_base_ شیء ریشه ctypes است که مالک بلوک حافظه میباشد.
این متغیر فقطخواندنی زمانی درست است که نمونه داده ctypes خودش بلوک حافظه را تخصیص داده باشد، در غیر این صورت نادرست است.
این عضو یا None است یا یک دیکشنری حاوی اشیای پایتون که باید زنده نگه داشته شوند تا محتویات بلوک حافظه معتبر باقی بماند. این شیء فقط برای اشکالزدایی در دسترس است؛ هرگز محتویات این دیکشنری را تغییر ندهید.
این کلاس غیرعمومی، کلاس پایهی همهی انواع دادهی بنیادی ctypes است. این کلاس در اینجا ذکر شده است زیرا شامل ویژگیهای مشترک انواع دادهی بنیادی ctypes است. _SimpleCData یک زیرکلاس از _CData است، بنابراین متدها و ویژگیهای آنها را به ارث میبرد. اکنون میتوان انواع دادهی ctypes را که اشارهگر نیستند و شامل اشارهگرها نمیشوند، پیکل کرد.
نمونهها یک ویژگی دارند:
این ویژگی حاوی مقدار واقعی نمونه است. برای انواع عدد صحیح و اشارهگر، این مقدار یک عدد صحیح است، برای انواع نویسه، یک شیء bytes یا رشتهی تکنویسهای است، و برای انواع اشارهگر به نویسه، یک شیء bytes یا رشتهی پایتون است.
هنگامی که ویژگی value از یک نمونه ctypes بازیابی میشود، معمولاً هر بار یک شیء جدید برگردانده میشود. ctypes بازگشت شیء اصلی را پیادهسازی نمیکند، بلکه همیشه یک شیء جدید ساخته میشود. همین موضوع برای تمام نمونههای دیگر اشیاء ctypes نیز صادق است.
هر زیرکلاس دارای یک ویژگی کلاس است:
ویژگی کلاس که حاوی یک کد نوع داخلی بهصورت یک رشته تکنویسهای است. برای یک خلاصه، به انواع داده بنیادی مراجعه کنید.
انواعی که در خلاصه با * علامتگذاری شدهاند ممکن است (یا همواره) نامهای مستعار یک زیرکلاس دیگر از _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 هستند:
نوع دادهی signed char در C را نشان میدهد و مقدار را بهعنوان عدد صحیح کوچک تفسیر میکند. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچگونه بررسی سرریزی انجام نمیشود.
نشاندهندهی نوع دادهی char در C است و مقدار را بهعنوان یک نویسه تفسیر میکند. سازنده یک مقدارده اولیهی رشتهای اختیاری را میپذیرد، طول رشته باید دقیقاً ۱ نویسه باشد.
نمایانگر نوع دادهی C char* است، زمانی که به یک رشتهی پایانیافته با صفر اشاره میکند. برای یک اشارهگر نویسهای عمومی که ممکن است به دادههای دودویی نیز اشاره کند، باید از POINTER(c_char) استفاده شود. سازنده یک نشانی بهصورت عدد صحیح یا یک شیء bytes را میپذیرد.
نشاندهندهی نوع دادهی double در C است. سازنده یک مقدار اولیهی float اختیاری را میپذیرد.
نمایانگر نوع دادهی C long double است. سازنده یک مقدار اولیه float اختیاری را میپذیرد. در سکوهایی که sizeof(long double) == sizeof(double) باشد، این نام مستعاری برای c_double است.
نوع دادهی float در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع float را میپذیرد.
در صورت موجود بودن، نشاندهندهی نوع دادهی double complex در C است. سازنده یک مقداردهی اولیهی اختیاری از complex را میپذیرد.
اضافه شده در نسخهی 3.14.
نوع دادهی float complex در C را در صورت موجود بودن نشان میدهد. سازنده، یک مقدارده اولیه اختیاری از نوع complex را میپذیرد.
اضافه شده در نسخهی 3.14.
نشاندهندهی نوع دادهی long double complex در C است، در صورت موجود بودن. سازنده یک مقداردهی اولیهی اختیاری complex را میپذیرد.
اضافه شده در نسخهی 3.14.
نشاندهندهی نوع دادهی signed int در C است. سازنده یک مقدار اولیهی اختیاری عدد صحیح را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود. در سکوهایی که sizeof(int) == sizeof(long) باشد، این یک نام مستعار برای c_long است.
نشاندهندهی نوع دادهی signed int ۸ بیتی C است. این یک نام مستعار برای c_byte است.
نشاندهندهی نوع دادهی signed int ۱۶ بیتی در C است. معمولاً نام مستعاری برای c_short است.
نشاندهندهی نوع دادهی signed int ۳۲ بیتی C است. معمولاً نام مستعاری برای c_int است.
نشاندهندهی نوع دادهی signed int ۶۴ بیتی C است. معمولاً نام مستعاری برای c_longlong است.
نشاندهنده نوع داده signed long در C است. سازنده یک مقدار اولیه عدد صحیح اختیاری را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود.
نشاندهندهی نوع دادهی C signed long long است. سازندهی آن یک مقدار اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسی سرریز انجام نمیشود. در سکوهایی که در آنها sizeof(long long) == sizeof(long) باشد، این نوع نام مستعاری برای c_long است.
نشاندهندهی نوع دادهی signed short در C است. سازنده یک مقداردهی اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
نشاندهندهی نوع دادهی size_t در C است. معمولاً نام مستعاری برای نوع دیگری از عدد صحیح بدون علامت است.
نشاندهندهی نوع دادهی Py_ssize_t است. این یک نسخهی علامتدار از size_t است؛ یعنی نوع ssize_t در POSIX. معمولاً نام مستعاری برای یک نوع عدد صحیح دیگر است.
اضافه شده در نسخهی 3.2.
نشاندهندهی نوع دادهی time_t در C است. معمولاً نام مستعاری برای یک نوع عدد صحیح دیگر است.
اضافه شده در نسخهی 3.12.
نشاندهندهی نوع دادهی unsigned char در C است و مقدار را بهعنوان عدد صحیح کوچک تفسیر میکند. سازنده یک مقدارده اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
نشاندهندهی نوع دادهی unsigned int در C است. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود. در سکوهایی که sizeof(int) == sizeof(long) باشد، نام مستعاری برای c_ulong است.
نشاندهندهی نوع دادهی ۸بیتی unsigned int در C است. این یک نام مستعار برای c_ubyte است.
نشاندهندهی نوع دادهی unsigned int ۱۶ بیتی در C است. معمولاً نام مستعاری برای c_ushort است.
نشاندهندهی نوع دادهی ۳۲ بیتی unsigned int در C است. معمولاً نام مستعاری برای c_uint است.
نشاندهندهی نوع دادهی unsigned int ۶۴ بیتی در C است. معمولاً نام مستعاری برای c_ulonglong است.
نوع دادهی unsigned long در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح را میپذیرد؛ هیچ بررسیای برای سرریز انجام نمیشود.
نشاندهندهی نوع دادهی C unsigned long long است. سازنده یک مقدار اولیهی عدد صحیح اختیاری را میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود. در سکوهای که sizeof(long long) == sizeof(long) باشد، نام مستعاری برای c_long است.
نوع دادهی unsigned short در C را نشان میدهد. سازنده یک مقدار اولیهی اختیاری از نوع عدد صحیح میپذیرد؛ هیچ بررسی سرریزی انجام نمیشود.
نشاندهندهی نوع void* در C است. مقدار بهصورت عدد صحیح نشان داده میشود. سازنده یک مقدار اولیهی اختیاری عدد صحیح را میپذیرد.
نشاندهندهی نوع دادهی wchar_t در C است و مقدار را بهعنوان یک رشتهی یونیکد تکنویسهای تفسیر میکند. سازنده یک مقدار اولیهی رشتهای اختیاری میپذیرد؛ طول رشته باید دقیقاً ۱ نویسه باشد.
نشاندهندهی نوع دادهی C wchar_t* است، که باید اشارهگری به رشتهای از نویسههای پهن خاتمهیافته با صفر باشد. سازنده یک نشانی عدد صحیحی یا یک رشته را میپذیرد.
نشاندهندهی نوع دادهی bool در C (دقیقتر، _Bool از C99) است. مقدار آن میتواند True یا False باشد و سازندهی آن هر شیءای را که مقدار درستی داشته باشد میپذیرد.
بیانگر یک مقدار HRESULT است، که حاوی اطلاعات موفقیت یا خطا برای فراخوانی یک تابع یا متد است.
دسترسپذیری: Windows
نشاندهندهی نوع دادهی PyObject* در C است. فراخوانی این بدون آرگومان، یک اشارهگر NULL از نوع PyObject* ایجاد میکند.
تغییر یافته در نسخهی 3.14: py_object اکنون یک generic type است.
Source code: Lib/ctypes/wintypes.py
ماژول ctypes.wintypes انواعی را برای استفاده با APIهای ویندوز فراهم میکند. بیشتر آنها نامهای مستعاری برای انواع تعریفشده در ctypes هستند. برای تعاریف متناظر C، به مستندات مایکروسافت Windows data types مراجعه کنید. در پلتفرمهای غیر ویندوزی، اندازهها و طرحبندیهای این انواع ممکن است با همتایان ویندوزی خود متفاوت باشد.
تغییر یافته در نسخهی 3.14: اضافه شدن HCONV، HCONVLIST، HCURSOR، HDDEDATA، HDROP، HFILE، HRESULT و HSZ.
برای مثال:
>>> from ctypes import POINTER >>> from ctypes.wintypes import LPRECT, RECT, SHORT >>> SHORT(-1).value -1 >>> rect = RECT(0, 0, 100, 50) >>> rect.right, rect.bottom (100, 50) >>> LPRECT is POINTER(RECT) True
نامهای زیر نامهای مستعاری برای انواع بنیادی ctypes هستند:
انواع ویندوز |
نوع |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
کلاسهای LARGE_INTEGER و ULARGE_INTEGER یک ویژگی value از نوع عدد صحیح را بدون فیلدهای LowPart، HighPart یا QuadPart نمایش میدهند.
یک نام مستعار برای c_void_p که برای نمایش دستههای ویندوز استفاده میشود.
نامهای مستعاری برای HANDLE جهت استفاده با APIهای خاص ویندوز. HFILE نام مستعاری برای INT است و در میان انواع سادهی بالا فهرست شده است.
انواع دادهی زیر زیرکلاسهایی از ctypes.Structure هستند.
یک مستطیل با فیلدهای left، top، right و bottom که همگی از نوع دادهی LONG هستند.
یک مستطیل با فیلدهای Left، Top، Right و Bottom که همگی از نوع دادهی SHORT هستند.
یک نقطه با فیلدهای x و y که هر دو از نوع دادهی LONG هستند.
ابعاد با فیلدهای cx و cy که هر دو از نوع دادهی LONG هستند.
یک زمان پرونده با فیلدهای dwLowDateTime و dwHighDateTime که هر دو از نوع دادهی DWORD هستند.
یک پیام ویندوز با فیلدهای زیر:
فیلد |
نوع داده |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
ساختارهای مورد استفاده توسط توابع جستجوی پروندهی ویندوز، با فیلدهای زیر. کلاس WIN32_FIND_DATAA برای نامهای پروندهی خود از CHAR استفاده میکند و کلاس WIN32_FIND_DATAW از WCHAR بهره میبرد.
فیلدها |
نوع داده |
|---|---|
|
|
|
|
|
|
|
|
|
آرایهای از نویسههای |
|
آرایهای از ۱۴ نویسه |
نامهای زیر نامهای مستعاری برای ctypes.POINTER(T) هستند که در آن T نوع در ستون دوم است:
انواع اشارهگر |
نوع ارجاعدادهشده |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
مقدار 260 که بهعنوان اندازه آرایههای نام پرونده در WIN32_FIND_DATAA و WIN32_FIND_DATAW استفاده میشود.
کلاس پایه انتزاعی برای اجتماعها (unions) در ترتیب بایت بومی.
اجتماعها (Unions) ویژگیها و رفتار مشترکی با ساختارها دارند؛ برای جزئیات، مستندات Structure را ببینید.
کلاس پایه انتزاعی برای اجتماعها با ترتیب بایت بزرگاندیان.
اضافه شده در نسخهی 3.11.
کلاس پایه انتزاعی برای اجتماعها (union) با ترتیب بایتی کوچکاندیان (little endian).
اضافه شده در نسخهی 3.11.
کلاس پایهی انتزاعی برای ساختارهایی با ترتیب بایت بزرگاندیان (big endian).
کلاس پایه انتزاعی برای ساختارهایی با ترتیب بایت little endian.
ساختارها و اجتماعهایی که ترتیب بایت غیربومی دارند، نمیتوانند شامل فیلدهایی از نوع اشارهگر یا هر نوع داده دیگری باشند که حاوی فیلدهایی از نوع اشارهگر باشد.
کلاس پایه انتزاعی برای ساختارها با ترتیب بایت بومی.
انواع عینی ساختار و اجتماع (union) باید با زیرکلاسسازی از یکی از این انواع ایجاد شوند، و حداقل یک متغیر کلاس _fields_ را تعریف کنند. ctypes توصیفگرها (descriptors) را ایجاد میکند که امکان خواندن و نوشتن فیلدها را از طریق دسترسی مستقیم به ویژگیها فراهم میکنند. اینها
دنبالهای که فیلدهای ساختار را تعریف میکند. آیتمها باید تاپلهای دوتایی یا سهتایی باشند. اولین آیتم، نام فیلد است؛ دومین آیتم، نوع فیلد را مشخص میکند و میتواند هر نوع دادهای از 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_ تعریفشده در زیرزیرکلاس را، در صورت وجود، به ارث میبرند.
یک عدد صحیح کوچک اختیاری که امکان بازنویسی همترازی فیلدهای ساختار در نمونه را فراهم میکند.
این فقط برای چیدمان حافظه سازگار با MSVC پیادهسازی شده است (به _layout_ مراجعه کنید).
تنظیم _pack_ به ۰ مانند این است که اصلاً آن را تنظیم نکرده باشید. در غیر این صورت، مقدار باید توان مثبتی از ۲ باشد. این اثر معادل #pragma pack(N) در C است، با این تفاوت که ctypes ممکن است n بزرگتری را نسبت به آنچه کامپایلر میپذیرد مجاز بداند.
هنگامی که _fields_ انتساب مییابد، _pack_ باید از پیش تعریف شده باشد، در غیر این صورت هیچ اثری نخواهد داشت.
منسوخ شده از نسخهی 3.14, در نسخهی 3.19 حذف خواهد شد: به دلایل تاریخی، اگر _pack_ غیرصفر باشد، چیدمان سازگار با MSVC بهطور پیشفرض استفاده خواهد شد. در سکوهای غیرویندوزی، این پیشفرض منسوخ شده است و قرار است در پایتون 3.19 به یک خطا تبدیل شود. اگر این حالت مورد نظر است، _layout_ را بهصراحت روی 'ms' تنظیم کنید.
یک عدد صحیح کوچک اختیاری که امکان افزایش همترازی ساختار را هنگام بستهبندی یا واگشایی به/از حافظه فراهم میکند.
مقدار نباید منفی باشد. اثر آن معادل __attribute__((aligned(N))) در GCC یا #pragma align(N) در MSVC است، با این تفاوت که ctypes ممکن است مقادیری را بپذیرد که کامپایلر آنها را رد میکند.
_align_ فقط میتواند الزامات همترازی یک ساختار را افزایش دهد. تنظیم آن روی ۰ یا ۱ هیچ تأثیری ندارد.
استفاده از مقادیری که توانهای ۲ نیستند، توصیه نمیشود و ممکن است به رفتار غیرمنتظرهای منجر شود.
_align_ باید هنگامی که _fields_ انتساب داده میشود از قبل تعریفشده باشد، در غیر این صورت بیاثر خواهد بود.
اضافه شده در نسخهی 3.13.
یک رشته اختیاری برای نامگذاری چیدمان 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_ باید هنگامی که _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_ وجود ندارند، ویژگیهای جدیدی ایجاد میکنند.
توصیفگر فیلدهای 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 بهطور مستقیم در دسترس نبود.
نام فیلد، بهصورت یک رشته.
نوع فیلد، بهصورت یک کلاس ctypes.
آفست فیلد، بر حسب بایت.
برای فیلدهای بیتی (bitfields)، این آفست واحد ذخیرهسازی زیربنایی همتراز با بایت است؛ به bit_offset مراجعه کنید.
اندازهی فیلد، بر حسب بایت.
برای بیتفیلدها، این اندازهی واحد ذخیرهسازی زیربنایی است. معمولاً هماندازهی نوع بیتفیلد است.
برای فیلدهای غیربیتی، معادل byte_size است.
برای فیلدهای بیتی (bitfields)، این شامل یک مقدار بستهبندیشده بیتی سازگار با نسخههای پیشین است که bit_size و bit_offset را ترکیب میکند. ترجیحاً بهجای آن از ویژگیهای صریح استفاده کنید.
اگر این یک فیلد بیتی (bitfield) باشد، True است.
موقعیت یک فیلد بیتی (bitfield) در واحد ذخیرهسازی آن، یعنی در byte_size بایت از حافظه که از byte_offset آغاز میشود.
برای بهدست آوردن مقدار فیلد، واحد ذخیرهسازی را بهعنوان یک عدد صحیح بخوانید، آن را به اندازهی bit_offset شیفت چپ دهید و bit_size بیت کماهمیت را بگیرید.
برای فیلدهای غیربیتی، bit_offset صفر است و bit_size برابر است با byte_size * 8.
True اگر این فیلد ناشناس باشد، یعنی شامل زیرفیلدهای تودرتویی است که باید در ساختار یا union حاوی آن ادغام شوند.
کلاس پایه انتزاعی برای آرایهها.
راه توصیهشده برای ایجاد انواع آرایهی مشخص، ضرب کردن هر نوع دادهای از ctypes در یک عدد صحیح نامنفی است. بهعنوان جایگزین، میتوانید یک زیرکلاس از این نوع بسازید و متغیرهای کلاس _length_ و _type_ را تعریف کنید. عناصر آرایه را میتوان با استفاده از دسترسیهای استاندارد زیرنویسی و اسلایسی خواند و نوشت؛ در خواندنهای اسلایسی، شیء حاصل، خود یک Array نیست.
آرایهها نسبت به نوع عناصر خود عام هستند.
یک عدد صحیح مثبت که تعداد عناصر آرایه را مشخص میکند. زیرنویسهای خارج از محدوده منجر به IndexError میشوند. توسط len() برگردانده میشود.
نوع هر عنصر در آرایه را مشخص میکند.
سازندههای زیرکلاس آرایه، آرگومانهای جایگاهی را میپذیرند که برای مقداردهی اولیهی عناصر بهترتیب استفاده میشوند.
یک آرایه ایجاد میکند. معادل type * length است، که در آن type یک نوع داده از ctypes و length یک عدد صحیح است.
منسوخسازی نرم <Soft deprecated> از نسخهی 3.14: به نفع ضرب.
کلاس پایه خصوصی و انتزاعی برای اشارهگرها.
انواع اشارهگر مشخص با فراخوانی POINTER() با نوعی که به آن اشاره خواهد شد ایجاد میشوند؛ این کار بهطور خودکار توسط pointer() انجام میشود.
اگر یک اشارهگر به یک آرایه اشاره کند، میتوان عناصر آن را با استفاده از دسترسیهای استاندارد زیرنویسی و اسلایسی خواند و نوشت. اشیاء اشارهگر اندازهای ندارند، بنابراین len() TypeError را پرتاب خواهد کرد. زیرنویسهای منفی حافظهی پیش از اشارهگر را میخوانند (همانطور که در C نیز چنین است)، و اندیسهای خارج از محدوده احتمالاً با نقض دسترسی (access violation) دچار فروپاشی خواهند شد (اگر خوششانس باشید).
نوعی را که به آن اشاره شده است مشخص میکند.
شیءای را که اشارهگر به آن اشاره میکند، برمیگرداند. انتساب به این ویژگی، اشارهگر را طوری تغییر میدهد که به شیء انتسابیافته اشاره کند.
این استثنا زمانی پرتاب میشود که یک فراخوانی تابع خارجی نتواند یکی از آرگومانهای ارسالشده را تبدیل کند.
این استثنا زمانی پرتاب میشود که فراخوانی یک متد COM ناموفق باشد.
مقدار عدد صحیحی که کد خطا را نشان میدهد.
پیام خطا.
۵-تاییِ (descr, source, helpfile, helpcontext, progid).
descr توضیح متنی است. source ProgID وابسته به زبان برای کلاس یا برنامهای است که خطا را پرتاب کرده است. helpfile مسیر پرونده راهنما است. helpcontext شناسه زمینه راهنما است. progid ProgID رابطی است که خطا را تعریف کرده است.
دسترسپذیری: Windows
اضافه شده در نسخهی 3.14.