امروز با مقاله دیگری از سری مقاله های آموزش پایتون همراه شما هستیم، در این مقاله میخواهیم چگونگی افزونه نویسی در پایتون به کمک کدهای زبان C را مورد بررسی قرار دهیم..
به منظور نوشتن افزونه های اختصاصی جهت استفاده در اسکریپت های پایتون و اپلیکیشن های خود، لازم است به فایل های header پایتون دسترسی داشته باشید.در دستگاه هایی که سیستم عامل Unix بر روی آن نصب است، می بایست یک پکیج مختص توسعه دهنده (developer-specific) نظیر python2.5-dev را نصب نمایید.
کاربران ویندوز این فایل های header را به هنگام استفاده از binary Python installer به صورت یک پکیج دریافت می کنند. علاوه بر آن، برای درک مفاهیم این مبحث و نوشتن افزونه های اختصاصی خود جهت استفاده در اسکریپت های پایتون، لازم است آشنایی در سطح پیشرفته با زبان های C یا ++C داشته باشید.
کد ماژول و افزونه های پایتون، بایستی مانند زیر در چهار بخش سازمان دهی شود:
لازم است فایل Python.h را داخل فایلی که کدهای C شما را دربرمی گیرد (source file) قید نمایید. بدین وسیله شما به توابع کتابخانه ای درون ساخته ی پایتون (inteal Python API) که برای ادغام و معرفی ماژول مورد نظر در interpreter (hook کردن کد ماژول شما در مفسر) بکار می رود، دسترسی خواهید داشت. لازم است Python.h را قبل از هر فایل header مورد نیاز دیگری لحاظ نمایید.
اسم متد، نوع و تعداد پارامترهای ورودی (Signature) توابع اختصاصی شما و پیاده سازی آن، بایستی بر اساس یکی از الگوهای زیر انجام شود:
static PyObject *MyFunction( PyObject *self, PyObject *args );
static PyObject *MyFunctionWithKeywords(PyObject *self,
PyObject *args,
PyObject *kw);
static PyObject *MyFunctionWithNoArgs( PyObject *self );
هر یک از متدهای اعلان شده ی فوق، در خروجی خود یک آبجکت Python برمی گرداند. در پایتون مفهومی به نام تابع void (تابعی که خروجی ندارد یا مقداری را برنمی گرداند) وجود ندارد. اگر شما نمی خواهید که توابع مقدار خروجی داشته باشند، لازم است مقدار None را بازگردانی نمایید. header های پایتون یک macro (خط دستور) به نام Py_RETURN_NONE در خود به صورت از پیش تعریف شده دارند که این کار را انجام می دهند.
از آنجایی که اسم توابع C هیچگاه خارج از ماژول/افزونه قابل مشاهده و دسترسی نیستند، شما می توانید هر اسمی برای متدهای اختصاصی خود انتخاب کنید. لازم به ذکر است که این توابع با کلیدواژه ی static تعریف می شوند. اسم توابع C معمولا از ترکیبی از اسم ماژول و متد مورد نظر تشکیل می شود. در زیر نمونه ای را مشاهده می کنید:
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Do your stuff here. */
Py_RETURN_NONE;
}
کد حاضر یک تابع Python به نام func را تعریف می کند که داخل افزونه ی module کپسوله سازی شده است. حال شما به این توابع C داخل جدول نگاشت متد (method table) Pointer و اشاره گر تعریف می کنید که در بخش بعدی کد برنامه ی شما انجام می شود.
این جدول نگاشت متد (method table) یک آرایه ی ساده از structure های PyMethodDef است (PyMethodDef یک مدل برای تعریف متد است). این structure ساختاری مشابه زیر دارد:
struct PyMethodDef {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
};
در زیر هر یک از اعضای این ساختار شرح داده اند:
این جدول بایستی با یک sentinel که از NULL و 0 برای اعضای مرتبط تشکیل شده، خاتمه یابد.
مثال :
برای متد اعلان شده در بالا، از جدول نگاشت تابع (method mapping table) زیر استفاده می کنیم:
static PyMethodDef module_methods[ ] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ NULL, NULL, 0, NULL }
};
آخرین بخش ماژول یا افزونه ی اختصاصی شما بایستی تابع مقداردهنده ی اولیه (initialization function) را شامل شود. این تابع را مفسر پایتون زمانی که ماژول در حافظه بارگذاری می شود، فرامی خواند. لازم است اسم این تابع initModule انتخاب شود (Module اسم ماژول و init اسم خود تابع می باشد).
تابع مقداردهنده ی اولیه بایستی از کتابخانه که می نویسید export و خروجی گرفته شده باشد. header های Python با اعلان دستور PyMODINIT_FUNC امکان انجام این کار را در محیطی که اسکریپت ها در آن کامپایل می شوند را فراهم می آورد. کافی است به هنگام تعریف تابع مورد نظر از آن استفاده نمایید.
تابع مقداردهنده ی اولیه ی زبان C شما دارای ساختار کلی زیر می باشد:
PyMODINIT_FUNC initModule() {
Py_InitModule3(func, module_methods, "docstring...");
}
در زیر شرح هر یک از پارامترهای تابع Py_InitModule3 را به تفصیل مشاهده می کنید:
در زیر تمامی بخش های تشکیل دهنده یک افزونه استاندارد را یکجا مشاهده می کنید:
#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...");
}
< /python.h>
مثال :
نمونه کاربردی که کلیه مفاهیم فوق را به صورت عملی بکار می برد را در زیر مشاهده می کنید:
#include < python.h>
static PyObject* helloworld(PyObject* self)
{
retu 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!");
}
< /python.h>
دستور Py_BuildValue در مثال بالا، یک مقدار Python را build یا کامپایل می کند. کد مورد نظر را داخل فایل hello.c ذخیره نمایید. در زیر با نحوه ی کامپایل و نصب ماژول که از اسکریپت پایتون فراخوانی می شود، را خواهید آموخت.
پکیج distutils توزیع و نصب ماژول های پایتون، خواه ماژول های اصلی و خالص خود پایوتن باشد خواه ماژول های اختصاصی و تنظیم شده توسط توسعه دهنده، را با روشی استاندارد بسیار آسان می سازد. ماژول ها در همان قالب اولیه (source form) توزیع شده و در اختیار برنامه نویس قرار می گیرد. برنامه نویس سپس ماژول مورد نظر را با فراخوانی اسکریپت نصب (setup script) به نام setup.py ، نصب می نماید.
جهت نصب ماژول ذکر شده در بالا، بایستی اسکریپت setup.py را آماده نموده و به روش زیر اجرا نمایید:
from distutils.core import setup, Extension
setup(name='helloworld', version='1.0',
ext_modules=[Extension('helloworld', ['hello.c'])])
اکنون با فراخوانی دستور زیر، تمامی مراحل لازم نظیر کامپایل و آماده سازی (linking & compilation) کد را انجام دهید. کد زیر کلیه ی مراحل مورد نیاز کامپایل و لینک ماژول با کامپایلر، دستورات linker و flag های مناسب را انجام داده، متعاقبا خروجی (.dll) را در پوشه ی مربوطه جایگذاری (کپی) می کند.
$ python setup.py install
در سیستم های مبتنی بر Unix، لازم است این دستور را با حساب کاربری root اجرا نمایید تا امکان یا مجوز درج داده در پوشه ی site-packages را داشته باشید. در سیستم عامل ویندوز لازم به انجام این کار نیست.
پس از نصب افزونه ی دلخواه خود، می توانید آن را در اسکرپیت پایتون خود با دستور import وارد کرده و فراخوانی نمایید:
#!/usr/bin/python
import helloworld
print helloworld.helloworld()
خروجی زیر را تولید می کند:
Hello, Python extensions!!
در طول توسعه پروژه، گاه می بایست توابعی را اعلان و فراخوانی نمایید که پارامترهایی را به عنوان ورودی می پذیرد. از اینرو بایستی signature (اسم تابع + نوع، تعداد پارامتر ورودی) مربوطه را برای توابع C ماژول اختصاصی خود انتخاب نمایید. به طور مثال، تابع ذیل را در نظر بگیرید که تعدادی پارامتر به عنوان ورودی پذیرفته و بدین صورت اعلان می شود:
static PyObject *module_func(PyObject *self, PyObject *args) {
/* Parse args and do something interesting here. */
Py_RETURN_NONE;
}
متد table که تابع جدید را در خود کپسوله می کند، به صورت زیر خواهد بود:
static PyMethodDef module_methods[ ] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ "func", module_func, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
می توانید با استفاده از تابع کتابخانه ای PyArg_ParseTuple آرگومان های مورد نیاز را از متغیر اشاره گر (pointer) به PyObject که به عنوان آرگومان به تابع C ارسال شده، استخراج نمایید.
اولین آرگومان ارسالی به PyArg_ParseTuple، آرگومان args می باشد. این آرگومان همان آبجکتی است که باید parse یا تحلیل نحوی شود. پارامتر دوم یک رشته ی فرمت دهی (format string) است که آرگومان ها را به آن شکلی که مورد انتظار شما است، به نمایش می گذارد. به تعداد آرگومان ها، یک یا چند کاراکتر در رشته ی فرمت دهی وجود دارد که نشانگر آرگومان های مزبور می باشند.
static PyObject *module_func(PyObject *self, PyObject *args) {
int i;
double d;
char *s;
if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
retu 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)
در زیر تعداد و نوع ورودی های تابع را به شکل استاندارد (signature) PyArg_ParseTuple مشاهده می کنید:
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
در صورتی که عملیات با موفقیت انجام شود، مقداری غیر صفر و چنانچه عملیات ناموفق بوده و خطا رخ داد، مقدار 0 در خروجی بازگردانی می شود. tuple، آبجکت PyObject* بوده که همان آرگومان دوم ارسال شده به تابع C می باشد. آرگومان سوم، format، یک رشته ی C می باشد که نشانگر آرگومان های الزامی و اختیاری می باشد.
در زیر لیستی از کدهای فرمت دهی که به تابع PyArg_ParseTuple ارسال می شود همراه با شرح هر یک مشاهده می کنید:
تابع Py_BuildValue، درست مانند PyArg_ParseTuple ، یک رشته ی فرمت دهی (string format) به عنوان ورودی دریافت می کند. بجای ارسال آدرس مقادیری که کامپایل می کنید، بایستی خود مقادیر را به عنوان آرگومان به تابع مورد نظر بفرستید. در زیر مثالی از نحوه ی پیاده سازی یک تابع که عملیات جمع را انجام می دهد، تابع add، مشاهده می کنید:
static PyObject *foo_add(PyObject *self, PyObject *args) {
int a;
int b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
retu NULL;
}
retu Py_BuildValue("i", a + b);
}
معادل پیاده سازی آن در زبان پایتون به صورت زیر می باشد:
def add(a, b):
retu (a + b)
می توانید دو خروجی از این تابع بازگردانی نمایید. این عملیات در پایتون با یک لیست قابل پیاده سازی خواهد بود:
static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
int a;
int b;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
retu NULL;
}
retu Py_BuildValue("ii", a + b, a - b);
}
معادل پیاده سازی آن در زبان پایتون به صورت خواهد بود:
def add_subtract(a, b):
retu (a + b, a - b)
در زیر روش استاندارد تنظیم اسم تابع، نوع و تعداد پارامترهای ورودی آن که signature خوانده می شود را ویژه ی تابع Py_BuildValue مشاهده می کنید:
PyObject* Py_BuildValue(char* format,...)
پارامتر format، یک رشته ی C بوده و نشانگر آبجکت Python است که پارامتر حاضر باید نهایتا به آن کامپایل شود. آرگومان های زیر مقادیر C هستند که خروجی از آن ها ساخته و کامپایل می شود. نتیجه ی PyObject* یک اشاره گر (reference) جدید می باشد.
جدول زیر code string های پرکاربرد را با ذکر کارایی هر یک در اختیار شما قرار می دهد:
به طور مثال تابع Py_BuildValue("{issi}",23,"zig","zag",42) یک dictionary پایتون به صورت {23:'zig','zag':42} در خروجی تولید می کند.
با دیگر آموزش های ما در زمینه آموزش پایتون و دیگر زبان های برنامه نویسی همراه ما باشید...
آموزش برنامه نویسیآموزش پایتون,آموزش زبان برنامه نویسی پایتون,آموزش برنامه نویسی Python,دوره آموزش پایتون,آموزش افزونه نویسی برای پایتون با زبان C,...نویسنده : پیمان کلانتری بازدید : 253