खोज…


परिचय

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


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow