Python Language
ctypes
खोज…
परिचय
ctypes
एक अंतर्निहित पुस्तकालय है जो देशी संकलित पुस्तकालयों से निर्यात कार्यों को आमंत्रित करता है।
नोट: चूंकि यह लाइब्रेरी संकलित कोड को संभालती है, यह अपेक्षाकृत ओएस पर निर्भर है।
मूल उपयोग
चलो कहते हैं कि हम का उपयोग करना चाहते libc
के ntohl
कार्य करते हैं।
सबसे पहले, हमें libc.so
लोड करना होगा:
>>> from ctypes import *
>>> libc = cdll.LoadLibrary('libc.so.6')
>>> libc
<CDLL 'libc.so.6', handle baadf00d at 0xdeadbeef>
फिर, हमें फंक्शन ऑब्जेक्ट मिलता है:
>>> ntohl = libc.ntohl
>>> ntohl
<_FuncPtr object at 0xbaadf00d>
और अब, हम केवल फ़ंक्शन को लागू कर सकते हैं:
>>> ntohl(0x6C)
1811939328
>>> hex(_)
'0x6c000000'
जो ठीक वही करता है जिसकी हम अपेक्षा करते हैं।
आम नुकसान
किसी फ़ाइल को लोड करने में विफल
लाइब्रेरी लोड करने में पहली संभव त्रुटि विफल हो रही है। उस स्थिति में एक OSError आमतौर पर उठाया जाता है।
यह या तो इसलिए है क्योंकि फ़ाइल मौजूद नहीं है (या OS द्वारा नहीं पाया जा सकता है):
>>> cdll.LoadLibrary("foobar.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
return self._dlltype(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
self._handle = _dlopen(self._name, mode)
OSError: foobar.so: cannot open shared object file: No such file or directory
जैसा कि आप देख सकते हैं, त्रुटि स्पष्ट और सुंदर संकेत है।
दूसरा कारण यह है कि फ़ाइल मिली है, लेकिन सही प्रारूप की नहीं है।
>>> cdll.LoadLibrary("libc.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
return self._dlltype(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
self._handle = _dlopen(self._name, mode)
OSError: /usr/lib/i386-linux-gnu/libc.so: invalid ELF header
इस स्थिति में, फ़ाइल एक स्क्रिप्ट फ़ाइल है, न कि .so
फ़ाइल। यह तब भी हो सकता है जब लिनक्स मशीन पर एक .dll
फ़ाइल खोलने की कोशिश की जा रही हो या 32 बिट पायथन डिस्प्रटर पर 64 बिट फ़ाइल हो। जैसा कि आप देख सकते हैं, इस मामले में त्रुटि थोड़ी अधिक अस्पष्ट है, और चारों ओर कुछ खुदाई की आवश्यकता है।
एक समारोह का उपयोग करने में विफल
मान लें कि हमने .so
फ़ाइल को सफलतापूर्वक लोड किया है, .so
हमें अपने फ़ंक्शन तक पहुंचने की आवश्यकता है जैसे हमने पहले उदाहरण पर किया है।
जब एक गैर-मौजूदा फ़ंक्शन का उपयोग किया जाता है, तो एक AttributeError
उठाया जाता है:
>>> libc.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 360, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 365, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /lib/i386-linux-gnu/libc.so.6: undefined symbol: foo
मूल ctypes ऑब्जेक्ट
सबसे बुनियादी वस्तु एक int है:
>>> obj = ctypes.c_int(12)
>>> obj
c_long(12)
अब, obj
स्मृति का एक हिस्सा है जिसका मान 12 है।
उस मूल्य को सीधे पहुँचा जा सकता है, और संशोधित भी किया जा सकता है:
>>> obj.value
12
>>> obj.value = 13
>>> obj
c_long(13)
चूँकि obj
मेमोरी का एक हिस्सा है, इसलिए हम इसका आकार और स्थान भी obj
सकते हैं:
>>> sizeof(obj)
4
>>> hex(addressof(obj))
'0xdeadbeef'
ctypes सरणियाँ
जैसा कि कोई भी अच्छा सी प्रोग्रामर जानता है, एक भी मूल्य आपको वह नहीं मिलेगा। क्या वास्तव में हमें जा रहे हैं arrays हो जाएगा!
>>> c_int * 16
<class '__main__.c_long_Array_16'>
यह एक वास्तविक सरणी नहीं है, लेकिन यह बहुत करीब है! हमने एक वर्ग बनाया है जो 16 int
s के एक सरणी को दर्शाता है।
अब हमें केवल इसे शुरू करना है:
>>> arr = (c_int * 16)(*range(16))
>>> arr
<__main__.c_long_Array_16 object at 0xbaddcafe>
अब arr
एक वास्तविक सरणी है जिसमें 0 से 15 तक की संख्याएं हैं।
उन्हें किसी भी सूची की तरह ही एक्सेस किया जा सकता है:
>>> arr[5]
5
>>> arr[5] = 20
>>> arr[5]
20
और किसी अन्य ctypes
ऑब्जेक्ट की तरह, इसका एक आकार और एक स्थान भी है:
>>> sizeof(arr)
64 # sizeof(c_int) * 16
>>> hex(addressof(arr))
'0xc000l0ff'
Ctypes के लिए रैपिंग फ़ंक्शंस
कुछ मामलों में, C फ़ंक्शन फ़ंक्शन पॉइंटर को स्वीकार करता है। जैसे ही उपयोगकर्ताओं को ctypes
, हम उन कार्यों का उपयोग करना चाहते हैं, और यहां तक कि तर्क के रूप में अजगर फ़ंक्शन भी पास कर सकते हैं।
चलो एक फ़ंक्शन को परिभाषित करते हैं:
>>> def max(x, y):
return x if x >= y else y
अब, वह फ़ंक्शन दो तर्क लेता है और उसी प्रकार का परिणाम देता है। उदाहरण के लिए, मान लेते हैं कि प्रकार एक int है।
जैसे हमने ऐरे उदाहरण पर किया, हम उस ऑब्जेक्ट को परिभाषित कर सकते हैं जो उस प्रोटोटाइप को दर्शाता है:
>>> CFUNCTYPE(c_int, c_int, c_int)
<CFunctionType object at 0xdeadbeef>
वह प्रोटोटाइप एक फ़ंक्शन को दर्शाता है जो एक c_int
(पहला तर्क) लौटाता है, और दो c_int
तर्क (अन्य तर्क) को स्वीकार करता है।
अब इस फंक्शन को रैप करते हैं:
>>> CFUNCTYPE(c_int, c_int, c_int)(max)
<CFunctionType object at 0xdeadbeef>
फ़ंक्शन प्रोटोटाइप का अधिक उपयोग होता है: वे ctypes
फ़ंक्शन (जैसे libc.ntohl
) को लपेट सकते हैं और सत्यापित कर सकते हैं कि फ़ंक्शन को लागू करते समय सही तर्क का उपयोग किया जाता है।
>>> libc.ntohl() # garbage in - garbage out
>>> CFUNCTYPE(c_int, c_int)(libc.ntohl)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: this function takes at least 1 argument (0 given)
जटिल उपयोग
चलो ऊपर के सभी उदाहरणों को एक जटिल परिदृश्य में lfind
हैं: libc
के lfind
फ़ंक्शन का उपयोग करते हुए।
फ़ंक्शन के बारे में अधिक जानकारी के लिए, मैन पेज पढ़ें। मैं आपसे आग्रह करता हूं कि आप इसे जाने से पहले पढ़ें।
सबसे पहले, हम उचित प्रोटोटाइप को परिभाषित करेंगे:
>>> compar_proto = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>> lfind_proto = CFUNCTYPE(c_void_p, c_void_p, c_void_p, POINTER(c_uint), c_uint, compar_proto)
फिर, आइए चर बनाते हैं:
>>> key = c_int(12)
>>> arr = (c_int * 16)(*range(16))
>>> nmemb = c_uint(16)
और अब हम तुलना फ़ंक्शन को परिभाषित करते हैं:
>>> def compar(x, y):
return x.contents.value - y.contents.value
ध्यान दें कि x
, और y
POINTER(c_int)
, इसलिए हमें उन्हें POINTER(c_int)
और उनके मूल्यों को लेने की आवश्यकता है ताकि वास्तव में स्मृति में संग्रहीत मूल्य की तुलना की जा सके।
अब हम सब कुछ एक साथ जोड़ सकते हैं:
>>> lfind = lfind_proto(libc.lfind)
>>> ptr = lfind(byref(key), byref(arr), byref(nmemb), sizeof(c_int), compar_proto(compar))
ptr
लौटाया गया शून्य सूचक है। यदि key
arr
में नहीं मिली थी, तो मूल्य None
होगा, लेकिन इस मामले में हमें एक वैध मूल्य मिला है।
अब हम इसे रूपांतरित कर सकते हैं और मूल्य तक पहुँच सकते हैं:
>>> cast(ptr, POINTER(c_int)).contents
c_long(12)
इसके अलावा, हम देख सकते हैं कि ptr
अंदर ptr
सही मान की ओर arr
:
>>> addressof(arr) + 12 * sizeof(c_int) == ptr
True