توسعه پایتون به کمک کدهای ++C/C — راهنمای مقدماتی

۵۹۴ بازدید
آخرین به‌روزرسانی: ۰۴ مهر ۱۴۰۲
زمان مطالعه: ۵ دقیقه
توسعه پایتون به کمک کدهای ++C/C — راهنمای مقدماتی

آیا تاکنون به این فکر کرده‌اید که چگونه برخی محاسبات (برای مثال کتابخانه Numpy) می‌توانند در پایتون چنین سریع اجرا شوند، در حالی که پایتون به عنوان یک زبان تفسیری، چندان به خاطر سرعت بالا مشهور نیست. دلیل اصلی این مسئله آن است که هنگام توسعه پایتون کد اصلی درون یک کتابخانه در اغلب موارد به زبان C یا ++C نوشته شده است.

در این راهنمای کوتاه به توضیح چگونگی ایجاد یک ماژول (یا بسته) پایتون با نوشتن کدهای C یا ++C می‌پردازیم. منبع اصلی این نوشته مستندات پایتون بوده است. به عنوان مثال یک ماژول ریاضیاتی می‌سازیم که متدی برای محاسبه فاکتوریل در زبان C دارد:

n!=n*(n-1)*(n-2)…

به این منظور به دو فایل نیاز داریم که یکی فایل پایتون به نام setup.py و دیگری فایل C به نام cmath.c خواهد بود.

فایل setup.py برای ساختن یک برنامه C به طور صحیح استفاده می‌شود. در این مسیر از کامپایل کردن، یافتن کتابخانه‌های مناسب پایتون و تفکر بهره گرفته‌ایم. برای این که بتوانیم برنامه یا ماژول خود را در زمان فراخوانی در یک اسکریپت پایتون اجرا کنیم، این ماژول به ارتباط مناسب با مفسر پایتون به نام CPython نیاز دارد. از این رو باید آن را باید با استفاده از چند شیء از python.h یعنی فایل هدر به ساختار صحیحی درآوریم. بدین ترتیب کارهایی که قرار است انجام دهیم این است که یک پوشش پیرامون متد اصلی C خود می‌سازیم که می‌تواند از مفسر پایتون فراخوانی شود، به طوری که می‌توانیم آن را در یک اسکریپت پایتون مانند یک تابع معمولی پایتون فراخوانی کنیم. ابتدا باید با وارد کردن دستور زیر فایل هدر را در فایل cmath.c خود بگنجانیم:

#include Python.h

همه اشیا و تابع‌هایی که مورد استفاده قرار می‌دهیم، برنامه ما را به ساختار صحیحی برای مفسر پایتون درمی‌آورند که در هدر پایتون اعلان شده و با پیشوند py آغاز می‌شود. شیء اصلی C که می‌تواند هر نوع شیء پایتون را نمایش دهد به نام PyObject شناخته می‌شود. اما پیش از استفاده از این اشیا باید متد خود را برای محاسبه فاکتوریل بنویسیم:

1int fastfactorial(int n){
2 if(n<=1)
3 return 1;
4 else
5 return n * fastfactorial(n-1);
6}

پس از آن باید یک پوشش پیرامون تابع بنویسیم. این پوشش یک اشاره‌گر به آرگومان‌ها می‌گیرد که بعدتر به اسکریپت پایتون ارسال می‌شوند و به صورت یک PyObject نمایش یافته و یک اشاره‌گر به نتیجه محاسبه بازگشت می‌دهد که آن نیز به صورت یک PyObject نمایش می‌یابد. به این منظور باید متد پوشش زیر را ایجاد کنیم:

1static PyObject* factorial(PyObject* self, PyObject* args){
2int n;
3if (!PyArg_ParseTuple(args,"i",&n))
4  return NULL;
5int result = fastfactorial(n);
6return Py_BuildValue("i",result);
7}

این متدهای پوششی همواره به یک اشاره‌گر PyObject برای اشاره self به خود شیء ماژول و اشاره‌گر PyObject برای اشاره args به آرگومان‌هایی که بعدتر در اسکریپت پایتون ارسال خواهند شد نیاز دارند. ما این آرگومان‌ها را به وسیله متد PyArg_ParseTuple تجزیه و اعلان می‌کنیم که به دنبال یک عدد صحیح به صورت i در آرگومان دوم هستیم. سپس مقدار تجزیه شده در متغیر n ذخیره می‌شود. پس از آن متد فاکتوریل (fastfactorial(n فراخوانی می‌شود و نتیجه با استفاده از متد Py_BuildValue از هدر پایتون دوباره به یک شیء PyObject* تبدیل می‌شود. در نهایت شیء نتیجه از سوی متد پوششی بازگشت می‌یابد.

اینک که متد پوششی پیرامون تابع فاکتوریل واقعی ما ساخته شده است، باید یک وهله از strcut-ی به نام PyModuleDef بسازیم که آن نیز در Python.h اعلان شده است. این struct هر آنچه را که مفسر پایتون برای دانستن در مورد یک ماژول نیاز دارد تعریف می‌کند. یک بخش از یک ماژول تعاریف همه متدها هستند. این کار از طریق struct دیگری به نام PyMethodDef صورت می‌گیرد و یا این که از آرایه‌ای از این struct–ها استفاده می‌شود که همه متدهای ماژول در آن جمع‌بندی می‌شوند. ما در این مثال از اعلانی به شکل زیر استفاده می‌کنیم:

1static PyMethodDef mainMethods[] = {
2 {"factorial",factorial,METH_VARARGS,"Calculate the factorial of n"},
3 {NULL,NULL,0,NULL}
4};

که آرایه struct-های PyMethodDef را مقداردهی می‌کند. علاوه بر یک struct از مقادیر Null که همواره باید در آرایه‌ای که به متد پوششی اضافه می‌کنیم گنجانده شود و factoriel نام دارد، اعلام می‌کنیم که این متد در واقع آرگومان‌ها را از طریق ثابت METH_VARARGS دریافت می‌کند. اینک که همه متدها را مقداردهی کردیم و آن‌ها در ماژول پایتون قرار دارند، می‌توانیم یک وهله از PyModuleDef بسازیم. این struct-ی است که کل ماژول پایتون ما را نمایش می‌دهد. در این مثال این کار به روش زیر صورت گرفته است:

1static PyModuleDef cmath = {
2 PyModuleDef_HEAD_INIT,
3 "cmath","Factorial Calculation",
4 -1,
5 mainMethods
6};

در این کد ماژول خود با نام cmath را داریم و آرایه‌ای از متدهای mainMethods را ارسال می‌کنیم. علاوه بر آن یک مستندسازی به ماژول می‌توان اضافه کرد. آخرین گام، ایجاد متدی است که وقتی برنامه پایتون از سوی یک اسکریپت برای ایمپورت کردن ماژول فراخوانی می‌شود اجرا خواهد شود. این کار به صورت زیر انجام می‌شود:

1PyMODINIT_FUNC PyInit_cmath(void){
2 return PyModule_Create(&cmath);
3}

نوع بازگشتی PyMODINIT_FUNC اعلان می‌کند که متد در عمل یک اشاره‌گر PyObject بازمی‌گرداند. این یک اشاره‌گر به خود ماژول پایتون است که در نهایت آن نیز PyObject است و از سوی PyModule_Create ایجاد شده است. زمانی که یک ماژول در اسکریپت پایتون ایمپورت شود، این متد فراخوانی می‌شود و اشاره‌گری به کل ماژول بازگشت می‌دهد که شامل همه متدهای آن است.

اکنون فایل C ما آماده است و همه متدها و ساختار مورد نیاز مفسر پایتون برای بارگذاری ماژول و اجرای متد فاکتوریل را دارد. از این رو می‌توانیم کار خود را ادامه داده و آن را build کنیم. برای ساختن برنامه نهایی، فایل C باید کامپایل شود و به کتابخانه‌های مناسب لینک گردد. منظور از کتابخانه‌های مناسب در این مورد کتابخانه‌هایی هستند که تعاریف متدها و شیءها در هدر Python اعلان شده باشند. برای ساده‌تر کردن فرایند ساخت، می‌توان از متدهای setup و Extension از ماژول distutils.core استفاده کرد. متد setup اساساً وظیفه کل فرایند build را بر عهده دارد. ما هر دو متد را از فایل setup.py ایمپورت می‌کنیم که باید در همان پوشه cmath.c قرار داشته باشد. فایل setup باید به صورت زیر باشد:

1from distutils.core import setup, Extension
2factorial_module = Extension('cmath',sources = ['cmath.c'])
3setup(name = 'MathExtension',version='1.0',description = 'This is a math package',ext_modules = [factorial_module])

ابتدا factorial_module را به صورت یک اکستنشن C با فایل C به عنوان منبع اعلان می‌کنیم. این وضعیت برای این که متد setup بداند کدام فایل را باید build کند ضروری است. سپس تابع setup را که در آن یک نام بسته (در این مورد MathExtension) یک عدد نسخه و یک توضیح کوتاه در مورد بسته ارائه شده است فراخوانی کرده و در نهایت اکستنشن/ماژول‌هایی که باید گنجانده شوند را اعلام می‌کنیم. اینک کار ما با فایل setup.py پایان یافته است.

در نهایت می‌توانیم فایل setup.py را یا با گزینه build که تنها ماژول را ایجاد کرده و آن را در پوشه build مناسب زیر پوشه جاری قرار می‌دهد بسازیم و یا از گزینه install کمک بگیریم که کتابخانه کامپایل شده را در مسیری که برنامه پایتون به آن دسترسی دارد قرار می‌دهد. گزینه build را با وارد کردن دستور زیر در Shell اجرا می‌کنیم:

python setup.py build

این دستور در نهایت کتابخانه ما را به صورت یک اکستنشن so. ساخته و آن رادر پوشه build ذخیره می‌کند. این کتابخانه می‌تواند از سوی برنامه پایتون برای اجرای متد فاکتوریل فراخوانی شود. برای تست کردن آن یک فایل test.py درست در پوشه‌ای که کتابخانه so. قرار دارد، ایجاد می‌کنیم فایل test.py به صورت زیر است:

1from cmath import factorial
2print(factorial(6))

و مقدار 720 را در خروجی ارائه می‌کند. اینک ما موفق شده‌ایم یک اکستنشن C برای پایتون بسازیم و آن را با موفقیت ایمپورت کرده و اجرا کنیم.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
matthiasbitzer94
۱ دیدگاه برای «توسعه پایتون به کمک کدهای ++C/C — راهنمای مقدماتی»

هیچی نفهمیدم✌?

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *