पायथन - सी के साथ एक्सटेंशन प्रोग्रामिंग

कोई भी कोड जिसे आप किसी भी संकलित भाषा का उपयोग करके लिखते हैं जैसे C, C ++, या Java को अन्य पायथन लिपि में एकीकृत या आयात किया जा सकता है। इस कोड को "एक्सटेंशन" माना जाता है।

एक पायथन एक्सटेंशन मॉड्यूल एक सामान्य सी लाइब्रेरी से अधिक कुछ नहीं है। यूनिक्स मशीनों पर, ये पुस्तकालय आमतौर पर समाप्त हो जाते हैं.so(साझा वस्तु के लिए)। विंडोज मशीनों पर, आप आमतौर पर देखते हैं.dll (गतिशील रूप से जुड़े पुस्तकालय के लिए)।

लेखन एक्सटेंशन के लिए पूर्व आवश्यक

अपना विस्तार लिखना शुरू करने के लिए, आपको पायथन हेडर फ़ाइलों की आवश्यकता होगी।

  • यूनिक्स मशीनों पर, इसे आमतौर पर डेवलपर-विशिष्ट पैकेज जैसे कि python2.5-dev स्थापित करने की आवश्यकता होती है ।

  • जब वे बाइनरी पायथन इंस्टॉलर का उपयोग करते हैं, तो विंडोज उपयोगकर्ताओं को ये हेडर पैकेज के हिस्से के रूप में मिलते हैं।

इसके अतिरिक्त, यह माना जाता है कि आपके पास C प्रोग्रामिंग का उपयोग करके किसी भी पायथन एक्सटेंशन को लिखने के लिए C या C ++ का अच्छा ज्ञान है।

सबसे पहले एक पायथन एक्सटेंशन को देखें

पायथन एक्सटेंशन मॉड्यूल को देखने के लिए, आपको अपने कोड को चार भाग में समूहित करने की आवश्यकता है -

  • हेडर फ़ाइल Python.h

  • सी फ़ंक्शन जिसे आप अपने मॉड्यूल से इंटरफ़ेस के रूप में उजागर करना चाहते हैं।

  • पायथन डेवलपर्स के रूप में आपके कार्यों के नाम मैप करने वाली एक तालिका उन्हें एक्सटेंशन मॉड्यूल के अंदर सी फ़ंक्शन को देखती है।

  • एक प्रारंभिक कार्य।

हेडर फ़ाइल पायथन.ह

आपको अपनी C स्रोत फ़ाइल में Python.h हेडर फ़ाइल शामिल करने की आवश्यकता है , जो आपको इंटरप्रिटर में अपने मॉड्यूल को हुक करने के लिए उपयोग किए जाने वाले आंतरिक पायथन एपीआई तक पहुंच प्रदान करता है।

पाइथन को शामिल करना सुनिश्चित करें। इससे पहले कि आप किसी अन्य हेडर की आवश्यकता हो। आपको उन कार्यों के साथ शामिल करने की आवश्यकता है जिन्हें आप पायथन से कॉल करना चाहते हैं।

सी कार्य

आपके कार्यों के C कार्यान्वयन के हस्ताक्षर हमेशा निम्नलिखित तीन रूपों में से एक लेते हैं -

static PyObject *MyFunction( PyObject *self, PyObject *args );

static PyObject *MyFunctionWithKeywords(PyObject *self,
                                 PyObject *args,
                                 PyObject *kw);

static PyObject *MyFunctionWithNoArgs( PyObject *self );

पूर्ववर्ती घोषणाओं में से प्रत्येक एक पायथन ऑब्जेक्ट देता है। पायथन में एक शून्य फ़ंक्शन के रूप में ऐसी कोई चीज नहीं है जैसा कि सी में है। यदि आप नहीं चाहते हैं कि आपके फ़ंक्शन मान वापस लौटाएं, तो पायथन के सी के बराबर लौटेंNoneमूल्य। पायथन हेडर एक मैक्रो, Py_RETURN_NONE को परिभाषित करते हैं, जो हमारे लिए ऐसा करता है।

आपके सी फ़ंक्शंस के नाम वे हो सकते हैं जो आपको पसंद हैं क्योंकि वे कभी भी विस्तार मॉड्यूल के बाहर नहीं देखे जाते हैं। उन्हें स्थिर कार्य के रूप में परिभाषित किया गया है।

आपके सी फ़ंक्शन आमतौर पर पायथन मॉड्यूल और फ़ंक्शन नामों को एक साथ जोड़कर नामित किए जाते हैं, जैसा कि यहां दिखाया गया है -

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

यह एक पायथन फ़ंक्शन है जिसे मॉड्यूल मॉड्यूल के अंदर फंक कहा जाता है । आप मॉड्यूल के लिए विधि तालिका में अपने सी फ़ंक्शंस में पॉइंटर्स डालेंगे जो आमतौर पर आपके स्रोत कोड में आता है।

विधि मानचित्रण तालिका

यह विधि तालिका PyMethodDef संरचनाओं का एक सरल सरणी है। वह संरचना कुछ इस तरह दिखती है -

struct PyMethodDef {
   char *ml_name;
   PyCFunction ml_meth;
   int ml_flags;
   char *ml_doc;
};

यहाँ इस संरचना के सदस्यों का वर्णन है -

  • ml_name - यह फ़ंक्शन का नाम है जब पायथन इंटरप्रेटर प्रस्तुत करता है जब इसका उपयोग पायथन कार्यक्रमों में किया जाता है।

  • ml_meth - यह उस फ़ंक्शन का पता होना चाहिए जिसमें पिछले सीज़न में वर्णित हस्ताक्षर में से कोई एक है।

  • ml_flags - यह दुभाषिया बताता है कि कौन सा तीन हस्ताक्षर ml_meth का उपयोग कर रहा है।

    • इस ध्वज में आमतौर पर METH_VARARGS का मान होता है।

    • यदि आप अपने फ़ंक्शन में कीवर्ड दलीलें देना चाहते हैं तो यह ध्वज METH_KEYWORDS के साथ बिट या ऑर्ड किया जा सकता है।

    • इसमें METH_NOARGS का मान भी हो सकता है जो यह दर्शाता है कि आप किसी भी तर्क को स्वीकार नहीं करना चाहते हैं।

  • ml_doc - यह फंक्शन के लिए डॉकस्ट्रिंग है, जो आपको एक लिखने का मन नहीं होने पर NULL हो सकता है।

इस तालिका को एक प्रहरी के साथ समाप्त करने की आवश्यकता है जिसमें पूर्ण सदस्य के लिए NULL और 0 मान शामिल हैं।

उदाहरण

उपरोक्त परिभाषित फ़ंक्शन के लिए, हमारे पास निम्नलिखित विधि मानचित्रण तालिका है -

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

प्रारंभिक समारोह

आपके एक्सटेंशन मॉड्यूल का अंतिम भाग इनिशियलाइज़ेशन फंक्शन है। मॉड्यूल लोड होने पर इस फ़ंक्शन को पायथन इंटरप्रेटर द्वारा बुलाया जाता है। यह आवश्यक है कि फ़ंक्शन का नाम दिया जाएinitModule, जहां मॉड्यूल मॉड्यूल का नाम है।

आरंभीकरण फ़ंक्शन को आपके द्वारा बनाए जा रहे पुस्तकालय से निर्यात करने की आवश्यकता है। पायथन हेडर PyMODINIT_FUNC को परिभाषित करते हैं ताकि उस विशेष वातावरण के लिए जो हम संकलित कर रहे हैं, उसके लिए उपयुक्त अवतरण शामिल करें। आपको फ़ंक्शन को परिभाषित करते समय इसका उपयोग करना होगा।

आपके सी आरंभीकरण समारोह में आम तौर पर निम्नलिखित समग्र संरचना होती है -

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

यहाँ Py_InitModule3 फ़ंक्शन का वर्णन है -

  • func - यह निर्यात किया जाने वाला कार्य है।

  • module_methods - यह ऊपर परिभाषित मानचित्रण तालिका नाम है।

  • docstring - यह वह टिप्पणी है जिसे आप अपने विस्तार में देना चाहते हैं।

यह सब एक साथ रखना निम्नलिखित की तरह दिखता है -

#include <Python.h>

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

उदाहरण

एक सरल उदाहरण जो उपरोक्त सभी अवधारणाओं का उपयोग करता है -

#include <Python.h>

static PyObject* helloworld(PyObject* self) {
   return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] =
   "helloworld( ): Any message you want to put here!!\n";

static PyMethodDef helloworld_funcs[] = {
   {"helloworld", (PyCFunction)helloworld, 
      METH_NOARGS, helloworld_docs},
      {NULL}
};

void inithelloworld(void) {
   Py_InitModule3("helloworld", helloworld_funcs,
                  "Extension module example!");
}

यहाँ Py_BuildValue फ़ंक्शन का उपयोग Python मान बनाने के लिए किया जाता है। ऊपर दिए गए कोड को hello.c फाइल में सेव करें। हम देखेंगे कि पायथन स्क्रिप्ट से इस मॉड्यूल को कैसे संकलित और स्थापित किया जाए।

एक्सटेंशन का निर्माण और स्थापना

Distutils पैकेज यह बहुत आसान अजगर मॉड्यूल, दोनों शुद्ध पायथन और विस्तार माड्यूल वितरित करने, एक मानक तरीके से करता है। मॉड्यूल को स्रोत रूप में वितरित किया जाता है और सेटअप स्क्रिप्ट के माध्यम से बनाया और स्थापित किया जाता है, जिसे आमतौर पर सेटअप ओरोमो कहा जाता है।

उपरोक्त मॉड्यूल के लिए, आपको निम्नलिखित सेटअप स्क्रिप्ट तैयार करने की आवश्यकता है -

from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',  \
      ext_modules=[Extension('helloworld', ['hello.c'])])

अब, निम्न कमांड का उपयोग करें, जो सभी आवश्यक संकलन और लिंकिंग चरणों का प्रदर्शन करेगा, सही कंपाइलर और लिंकर कमांड और झंडे के साथ, और परिणामस्वरूप डायनेमिक लाइब्रेरी को एक उपयुक्त निर्देशिका में कॉपी करता है -

$ python setup.py install

यूनिक्स-आधारित प्रणालियों पर, आपको साइट-पैकेज निर्देशिका में लिखने के लिए अनुमतियाँ रखने के लिए मूल रूप से इस आदेश को चलाने की आवश्यकता होगी। यह आमतौर पर विंडोज पर कोई समस्या नहीं है।

आयात का विस्तार

एक बार जब आप अपना एक्सटेंशन स्थापित कर लेते हैं, तो आप उस एक्सटेंशन को अपनी पाइथन स्क्रिप्ट में निम्नानुसार आयात और कॉल कर सकेंगे -

#!/usr/bin/python
import helloworld

print helloworld.helloworld()

यह निम्नलिखित परिणाम का उत्पादन करेगा -

Hello, Python extensions!!

पासिंग फंक्शन पैरामीटर्स

जैसा कि आप सबसे अधिक तर्क को स्वीकार करने वाले कार्यों को परिभाषित करना चाहते हैं, आप अपने सी कार्यों के लिए किसी अन्य हस्ताक्षर का उपयोग कर सकते हैं। उदाहरण के लिए, निम्नलिखित फ़ंक्शन, जो कुछ मापदंडों को स्वीकार करता है, इस तरह परिभाषित किया जाएगा -

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Parse args and do something interesting here. */
   Py_RETURN_NONE;
}

नए फ़ंक्शन के लिए प्रविष्टि वाली विधि तालिका इस तरह दिखाई देगी -

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { "func", module_func, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
};

अपने C फ़ंक्शन में दिए गए एक PyObject पॉइंटर से तर्क निकालने के लिए आप API PyArg_ParseTuple फ़ंक्शन का उपयोग कर सकते हैं ।

PyArg_ParseTuple का पहला तर्क आर्ग्स तर्क है। यह वह वस्तु है जिसे आप पार्स करेंगे । दूसरा तर्क तर्कों का वर्णन करने वाला एक प्रारूप है जैसा कि आप उनसे प्रकट होने की अपेक्षा करते हैं। प्रत्येक तर्क प्रारूप स्ट्रिंग में एक या एक से अधिक वर्णों द्वारा दर्शाया गया है।

static PyObject *module_func(PyObject *self, PyObject *args) {
   int i;
   double d;
   char *s;

   if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
      return NULL;
   }
   
   /* Do something interesting here. */
   Py_RETURN_NONE;
}

अपने मॉड्यूल के नए संस्करण को संकलित करना और इसे आयात करना आपको किसी भी प्रकार के तर्कों के साथ नए फ़ंक्शन को लागू करने में सक्षम बनाता है -

module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)

आप शायद और भी विविधताओं के साथ आ सकते हैं।

PyArg_ParseTuple समारोह

यहाँ के लिए मानक हस्ताक्षर है PyArg_ParseTuple कार्य -

int PyArg_ParseTuple(PyObject* tuple,char* format,...)

यह फ़ंक्शन त्रुटियों के लिए 0, और सफलता के लिए 0 के बराबर मान नहीं देता है। tuple PyObject * है जो C फ़ंक्शन का दूसरा तर्क था। यहां प्रारूप एक सी स्ट्रिंग है जो अनिवार्य और वैकल्पिक तर्कों का वर्णन करता है।

यहाँ प्रारूप कोड की एक सूची है PyArg_ParseTuple कार्य -

कोड C प्रकार जिसका अर्थ है
सी चार एक पायथन स्ट्रिंग की लंबाई 1 एक सी चार हो जाती है।
दोहरा एक पायथन फ्लोट सी डबल हो जाता है।
नाव एक पायथन फ्लोट सी फ्लोट बन जाता है।
मैं पूर्णांक एक पायथन इंट एक C int बन जाता है।
एल लंबा एक पायथन इंट एक C लंबा हो जाता है।
एल लम्बा लम्बा एक पायथन इंट एक लंबे लंबे सी बन जाता है
हे PyObject * पायथन तर्क के लिए गैर-पूर्ण उधार लिया संदर्भ प्राप्त होता है।
रों चार * सी चार * के लिए एम्बेडेड नल के बिना पायथन स्ट्रिंग।
रों # चार * + पूर्णांक सी पते और लंबाई के लिए कोई भी पायथन स्ट्रिंग।
टी # चार * + पूर्णांक सी-एड्रेस और लंबाई तक केवल-एकल-खंड बफ़र।
यू Py_UNICODE * सी के लिए एम्बेडेड नल के बिना पायथन यूनिकोड।
यू # Py_UNICODE * + पूर्णांक कोई पायथन यूनिकोड सी पता और लंबाई।
डब्ल्यू # चार * + पूर्णांक सी-एड्रेस और लंबाई तक एकल-खंड बफर पढ़ें / लिखें।
जेड चार * एस की तरह, भी कोई नहीं स्वीकार करता है (सी चार * NULL को सेट करता है)।
z # चार * + पूर्णांक # की तरह, कोई भी स्वीकार नहीं करता है (सी चार * को NULL सेट करता है)।
(...) के अनुसार ... एक पायथन अनुक्रम को प्रति आइटम एक तर्क के रूप में माना जाता है।
|   निम्नलिखित तर्क वैकल्पिक हैं।
:   प्रारूप अंत, त्रुटि संदेशों के लिए फ़ंक्शन नाम के बाद।
;   संपूर्ण त्रुटि संदेश पाठ के बाद प्रारूप अंत,।

लौटाने का मूल्य

Py_BuildValue एक स्वरूप स्ट्रिंग में लेता है जैसे PyArg_ParseTuple करता है। आपके द्वारा बनाए जा रहे मूल्यों के पते में पारित होने के बजाय, आप वास्तविक मूल्यों में पास होते हैं। यहाँ एक उदाहरण दिखाया गया है कि ऐड फंक्शन कैसे लागू किया जाता है -

static PyObject *foo_add(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("i", a + b);
}

यदि पायथन में इसे लागू किया जाता है तो यह कैसा होगा -

def add(a, b):
   return (a + b)

आप अपने फ़ंक्शन से दो मान निम्नानुसार लौटा सकते हैं, यह पायथन में एक सूची का उपयोग करके विचलित किया जाएगा।

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("ii", a + b, a - b);
}

यदि पायथन में इसे लागू किया जाता है तो यह कैसा होगा -

def add_subtract(a, b):
   return (a + b, a - b)

Py_BuildValue समारोह

यहाँ के लिए मानक हस्ताक्षर है Py_BuildValue कार्य -

PyObject* Py_BuildValue(char* format,...)

यहां प्रारूप एक सी स्ट्रिंग है जो निर्माण के लिए पायथन ऑब्जेक्ट का वर्णन करता है। Py_BuildValue के निम्नलिखित तर्क C मान हैं जिनसे परिणाम बनाया गया है। PyObject * परिणाम एक नया संदर्भ है।

निम्नलिखित तालिका सूची में आमतौर पर उपयोग किए जाने वाले कोड स्ट्रिंग्स हैं, जिनमें से शून्य या अधिक को स्ट्रिंग प्रारूप में शामिल किया गया है।

कोड C प्रकार जिसका अर्थ है
सी चार एसी चार लंबाई का पायथन स्ट्रिंग बन जाता है।
दोहरा एसी डबल पायथन फ्लोट बन जाता है।
नाव एसी फ्लोट एक पायथन फ्लोट बन जाता है।
मैं पूर्णांक एसी इंट पायथन इंट बन जाता है।
एल लंबा एसी लॉन्ग पायथन इंट बन जाता है।
एन PyObject * एक पायथन ऑब्जेक्ट पास करता है और एक संदर्भ चोरी करता है।
हे PyObject * एक पायथन ऑब्जेक्ट को पास करता है और इसे सामान्य रूप से बढ़ाता है।
ओ एंड धर्मांतरित + शून्य * मनमाना रूपांतरण
रों चार * सी 0-टर्मिनेटेड चार * से पायथन स्ट्रिंग, या कोई भी नहीं।
रों # चार * + पूर्णांक C char * और लंबाई Python string, या NULL to none।
यू Py_UNICODE * सी-वाइड, शून्य-टर्मिनेटेड स्ट्रिंग टू पाइथन यूनिकोड, या कोई भी नहीं।
यू # Py_UNICODE * + पूर्णांक सी-वाइड स्ट्रिंग और पाइथन यूनिकोड की लंबाई, या कोई भी नहीं है।
डब्ल्यू # चार * + पूर्णांक सी-एड्रेस और लंबाई तक एकल-खंड बफर पढ़ें / लिखें।
जेड चार * एस की तरह, भी कोई नहीं स्वीकार करता है (सी चार * NULL को सेट करता है)।
z # चार * + पूर्णांक # की तरह, कोई भी स्वीकार नहीं करता है (सी चार * को NULL सेट करता है)।
(...) के अनुसार ... C मान से पायथन टपल बनाता है।
[...] के अनुसार ... C मानों से पायथन सूची बनाता है।
{...} के अनुसार ... सी मूल्यों, वैकल्पिक कुंजी और मूल्यों से पायथन शब्दकोश बनाता है।

कोड {...} सी मानों की एक समान संख्या से शब्दकोष बनाता है, वैकल्पिक रूप से कुंजी और मान। उदाहरण के लिए, Py_BuildValue ("{issi}", 23, "zig", "zag", 42) Python के {23: 'zig', 'zag': 42} जैसे शब्दकोश देता है।