اتصالات پایتون: فراخوانی C یا ++C از پایتون


آیا شما یک توسعه دهنده پایتون با کتابخانه C یا C++ هستید که می خواهید از پایتون استفاده کنید؟ اگر چنین است، پیوندهای پایتون به شما امکان می دهند توابع را فراخوانی کنید و داده ها را از پایتون به C یا C++ منتقل کنید و به شما امکان می دهد از نقاط قوت هر دو زبان استفاده کنید. در طول این آموزش، مروری بر برخی از ابزارهایی که می توانید برای ایجاد اتصالات پایتون استفاده کنید، مشاهده خواهید کرد.

در این آموزش با موارد زیر آشنا خواهید شد:

  • چرا می خواهید C یا C++ را از پایتون فراخوانی کنید؟
  • نحوه انتقال داده بین C و پایتون
  • چه ابزارها و متدهایی می توانند به شما در ایجاد اتصالات پایتون کمک کنند؟

این آموزش برای توسعه دهندگان متوسط پایتون طراحی شده است. دانش اولیه پایتون و درک برخی از توابع و انواع داده در C یا C++ را فرض می کند. با کلیک بر روی لینک زیر می توانید تمام کدهای نمونه ای را که در این آموزش مشاهده می کنید دریافت کنید:

بیایید به بررسی اتصالات پایتون بپردازیم!

بررسی اتصالات پایتون

قبل از اینکه به نحوه فراخوانی C از پایتون بپردازید، خوب است که مدتی را صرف دلیل آن کنید. چندین موقعیت وجود دارد که ایجاد اتصالات پایتون برای فراخوانی کتابخانه C ایده خوبی است:

  1. شما در حال حاضر یک کتابخانه بزرگ، آزمایش شده و پایدار دارید که به زبان C++ نوشته شده است و می خواهید از آن در پایتون استفاده کنید. این ممکن است یک کتابخانه ارتباطی یا یک کتابخانه برای صحبت با یک قطعه سخت افزاری خاص باشد. کاری که انجام می دهد مهم نیست.

  2. شما می خواهید با تبدیل یک بخش مهم به C، بخش خاصی از کد پایتون خود را افزایش دهید. C نه تنها سرعت اجرای سریع تری دارد، بلکه به شما امکان می دهد از محدودیت های GIL رها شوید، به شرطی که مراقب باشید.

  3. شما می خواهید از ابزارهای تست پایتون برای انجام تست در مقیاس بزرگ سیستم های آنها استفاده کنید.

همه موارد فوق دلایل خوبی برای یادگیری ایجاد اتصالات پایتون برای ارتباط با کتابخانه C شما هستند.

بیا شروع کنیم!

مارشال کردن انواع داده

صبر کن! قبل از شروع نوشتن اتصالات پایتون، نگاهی بیندازید که پایتون و C چگونه داده ها را ذخیره می کنند و چه نوع مشکلاتی ایجاد می کند. ابتدا، بیایید مارشال را تعریف کنیم. این مفهوم توسط ویکی پدیا به شرح زیر تعریف شده است:

فرآیند تبدیل نمایش حافظه یک شی به فرمت داده مناسب برای ذخیره سازی یا انتقال. (منبع)

برای اهداف شما، مارشال کاری است که اتصالات پایتون هنگام آماده سازی داده ها برای انتقال آن از پایتون به C یا بالعکس انجام می دهند. اتصالات پایتون باید مارشال را انجام دهند زیرا پایتون و C داده ها را به روش های مختلف ذخیره می کنند. C داده ها را به فشرده ترین شکل ممکن در حافظه ذخیره می کند. اگر از uint8_t استفاده می کنید، در مجموع فقط از 8 بیت حافظه استفاده می کند.

از سوی دیگر، در پایتون، همه چیز یک شی است. این بدان معنی است که هر عدد صحیح از چندین بایت در حافظه استفاده می کند. تعداد آنها به نسخه پایتون، سیستم عامل شما و سایر عوامل بستگی دارد. این بدان معنی است که اتصالات پایتون شما باید یک عدد صحیح C را به یک عدد صحیح پایتون برای هر عدد صحیح عبور شده از مرز تبدیل کند.

انواع داده های دیگر روابط مشابهی بین دو زبان دارند. بیایید به هر کدام به نوبه خود نگاه کنیم:

  • اعداد صحیح اعداد شمارش را ذخیره می کنند. پایتون اعداد صحیح را با دقت دلخواه ذخیره می کند، به این معنی که می توانید اعداد بسیار بسیار زیادی را ذخیره کنید. C اندازه دقیق اعداد صحیح را مشخص می کند. هنگام جابجایی بین زبان ها باید از اندازه داده ها آگاه باشید تا از سرریز شدن مقادیر صحیح پایتون از متغیرهای صحیح C جلوگیری کنید.

  • اعداد ممیز شناور اعدادی با رقم اعشار هستند. پایتون می تواند اعداد ممیز شناور بسیار بزرگتر (و بسیار کوچکتر) را نسبت به C ذخیره کند. این بدان معناست که شما همچنین باید به آن مقادیر توجه کنید تا مطمئن شوید که آنها در محدوده باقی می مانند.

  • اعداد مختلط اعدادی با یک قسمت خیالی هستند. در حالی که پایتون دارای اعداد مختلط داخلی است و C دارای اعداد مختلط است، هیچ روش داخلی برای سازماندهی بین آنها وجود ندارد. برای مارشال اعداد مختلط، باید یک ساختار یا کلاس در کد C بسازید تا آنها را مدیریت کنید.

  • رشته ها دنباله ای از کاراکترها هستند. به دلیل اینکه یک نوع داده رایج است، رشته ها هنگام ایجاد اتصالات پایتون نسبتا مشکل خواهند بود. مانند سایر انواع داده، پایتون و C رشته ها را در فرمت های کاملا متفاوتی ذخیره می کنند. (برخلاف سایر انواع داده ها، این منطقه ای است که C و C++ نیز با هم متفاوت هستند، که به سرگرمی می افزاید!) هر یک از راه حل هایی که بررسی می کنید روش های کمی متفاوت برای برخورد با رشته ها دارند.

  • متغیرهای بولی فقط می توانند دو مقدار داشته باشند. از آنجایی که آنها در C پشتیبانی می شوند، سازماندهی آنها نسبتا ساده خواهد بود.

علاوه بر تبدیل نوع داده، مسائل دیگری نیز وجود دارد که باید هنگام ساخت اتصالات پایتون خود به آنها فکر کنید. بیایید به کاوش در آنها ادامه دهیم.

درک مقادیر تغییرپذیر و تغییرناپذیر

علاوه بر همه این انواع داده، باید از اینکه چگونه آبجکت های پایتون می توانند تغییرپذیر یا غیرقابل تغییر باشند، آگاه باشید. C مفهوم مشابهی با پارامترهای تابع در هنگام صحبت در مورد عبور از مقدار یا گذر از ارجاع دارد. در C، همه پارامترها به صورت گذر به مقدار هستند. اگر می خواهید به یک تابع اجازه دهید یک متغیر را در تماس گیرنده تغییر دهد، باید یک اشاره گر را به آن متغیر ارسال کنید.

ممکن است از خود بپرسید که آیا می توانید با عبور دادن یک شی تغییرناپذیر به C با استفاده از یک نشانگر، محدودیت تغییرناپذیر را دور بزنید. مگر اینکه به افراط زشت و غیر قابل حمل بروید، پایتون به شما اشاره گر به یک شی نمی دهد، بنابراین این کار نمی کند. اگر می خواهید یک شی پایتون را در C تغییر دهید، باید اقدامات بیشتری را برای رسیدن به این هدف انجام دهید. این مراحل بستگی به ابزارهایی دارد که استفاده می کنید، همانطور که در زیر خواهید دید.

بنابراین، می توانید تغییرناپذیری را به چک لیست آیتم های خود اضافه کنید تا هنگام ایجاد اتصالات پایتون در نظر بگیرید. آخرین توقف شما در تور بزرگ ایجاد این چک لیست این است که چگونه روش های مختلفی را که پایتون و C با مدیریت حافظه سروکار دارند، مدیریت کنید.

مدیریت حافظه

C و Python حافظه را به طور متفاوتی مدیریت می کنند. در C، توسعه دهنده باید تمام تخصیص حافظه ها را مدیریت کند و اطمینان حاصل کند که آنها فقط یک بار آزاد می شوند. پایتون با استفاده از جمع آوری زباله از این کار برای شما مراقبت می کند.

در حالی که هر یک از این رویکردها مزایای خود را دارند، اما یک چین و چروک اضافی به ایجاد اتصالات پایتون اضافه می کند. شما باید از جایی که حافظه برای هر شی اختصاص داده شده است آگاه باشید و اطمینان حاصل کنید که فقط در همان طرف مانع زبان آزاد می شود.

به عنوان مثال، یک شی پایتون زمانی ایجاد می شود که x=3 را تنظیم می کنید. حافظه این کار در سمت پایتون اختصاص داده شده است و باید زباله جمع آوری شود. خوشبختانه، با آبجکت های پایتون، انجام هر کار دیگری بسیار دشوار است. نگاهی به معکوس در C بیندازید، جایی که مستقیما یک بلوک حافظه را اختصاص می دهید:

int* iPtr = (int*)malloc(sizeof(int));

وقتی این کار را انجام می دهید، باید مطمئن شوید که این نشانگر در C آزاد شده است. این ممکن است به معنای افزودن دستی کد به اتصالات پایتون برای انجام این کار باشد.

این چک لیست موضوعات کلی شما را تکمیل می کند. بیایید شروع به راه اندازی سیستم خود کنیم تا بتوانید کمی کد بنویسید!

راه اندازی محیط شما

برای این آموزش، شما می خواهید از کتابخانه های C و C++ از قبل موجود از مخزن واقعی پایتون GitHub برای نشان دادن تست هر ابزار استفاده کنید. هدف این است که شما می توانید از این ایده ها برای هر کتابخانه C استفاده کنید. برای دنبال کردن همه نمونه های اینجا، باید موارد زیر را داشته باشید:

  • کتابخانه C++ نصب شده و آگاهی از مسیر فراخوانی خط فرمان
  • ابزارهای توسعه پایتون:

    • برای لینوکس، بسته به توزیع شما، این بسته python3-dev یا python3-devel است.
    • برای ویندوز، گزینه های متعددی وجود دارد.
  • پایتون 3.6 یا بالاتر
  • یک محیط مجازی (توصیه می شود، اما الزامی نیست)
  • ابزار فراخوانی

آخرین مورد ممکن است برای شما جدید باشد، بنابراین بیایید نگاهی دقیق تر به آن بیندازیم.

استفاده از ابزار فراخوانی

invoke ابزاری است که در این آموزش برای ساخت و تست اتصالات پایتون خود استفاده خواهید کرد. هدف مشابهی برای ساخت دارد اما به جای Makefiles از پایتون استفاده می کند. شما باید invoke را در محیط مجازی خود با استفاده از pip نصب کنید:

python3 -m pip install invoke

برای اجرای آن، فراخوانی را تایپ می کنید و سپس وظیفه ای را که می خواهید اجرا کنید، تایپ کنید:

invoke build-cmult
==================================================
= Building C Library
* Complete

برای اینکه ببینید کدام وظایف در دسترس هستند، از گزینه --list استفاده کنید:

invoke --list
Available tasks:

  all              Build and run all tests
  build-cffi       Build the CFFI Python bindings
  build-cmult      Build the shared library for the sample C code
  build-cppmult    Build the shared library for the sample C++ code
  build-cython     Build the cython extension module
  build-pybind11   Build the pybind11 wrapper library
  clean            Remove any built objects
  test-cffi        Run the script to test CFFI
  test-ctypes      Run the script to test ctypes
  test-cython      Run the script to test Cython
  test-pybind11    Run the script to test PyBind11

توجه داشته باشید که وقتی به فایل tasks.py نگاه می کنید که وظایف فراخوانی در آن تعریف شده است، می بینید که نام کار دوم فهرست شده build_cffi است. با این حال، خروجی از --list آن را به صورت build-cffi نشان می دهد. علامت منفی (-) را نمی توان به عنوان بخشی از نام پایتون استفاده کرد، بنابراین فایل به جای آن از زیرخط (_) استفاده می کند.

برای هر یک از ابزارهایی که بررسی خواهید کرد، یک کار ساخت و یک تست تعریف شده است. به عنوان مثال، برای اجرای کد CFFI، می توانید invoke build-cffi test-cffi را تایپ کنید. یک استثنا ctypes است، زیرا هیچ مرحله ساخت برای ctypes وجود ندارد. علاوه بر این، دو کار ویژه برای راحتی اضافه شده است:

  • فراخوانی همه وظایف ساخت و تست را برای همه ابزارها اجرا می کند.
  • Invoke Clean فایل های تولید شده را حذف می کند.

اکنون که احساسی در مورد نحوه اجرای کد دارید، بیایید نگاهی به کد C که قبل از رفتن به نمای کلی ابزارها بسته بندی می کنید، بیندازیم.

منبع C یا C++

در هر یک از بخش های مثال زیر، اتصالات پایتون را برای همان تابع در C یا C++ ایجاد خواهید کرد. این بخش ها در نظر گرفته شده اند تا به شما طعم شکل هر متد را بدهند، نه یک آموزش عمیق در مورد آن ابزار، بنابراین عملکردی که بسته بندی می کنید کوچک است. تابعی که اتصالات پایتون را برای آن ایجاد می کنید، یک int و یک float را به عنوان پارامتر ورودی می گیرد و یک شناور را برمی گرداند که حاصل ضرب دو عدد است:

// cmult.c
float cmult(int int_param, float float_param) {
    float return_value = int_param * float_param;
    printf("    In cmult : int: %d float %.1f returning  %.1f\n", int_param,
            float_param, return_value);
    return return_value;
}

توابع C و C++ تقریبا یکسان هستند و تفاوت های جزئی نام و رشته بین آنها وجود دارد. با کلیک بر روی لینک زیر می توانید یک کپی از تمام کدها را دریافت کنید:

اکنون مخزن را شبیه سازی کرده اید و ابزارهای خود را نصب کرده اید، می توانید ابزارها را بسازید و آزمایش کنید. پس بیایید به هر بخش زیر شیرجه بزنیم!

Ctypes

شما با ctypes شروع خواهید کرد که ابزاری در کتابخانه استاندارد برای ایجاد اتصالات پایتون است. این یک مجموعه ابزار سطح پایین برای بارگذاری کتابخانه های مشترک و جمع آوری داده ها بین پایتون و C فراهم می کند.

نحوه نصب آن

یکی از مزایای بزرگ ctypes این است که بخشی از کتابخانه استاندارد پایتون است. در نسخه 2.5 پایتون اضافه شده است، بنابراین به احتمال زیاد قبلا آن را دارید. شما می توانید آن را درست مانند ماژول های sys یا time وارد کنید.

فراخوانی تابع

تمام کدهای بارگذاری کتابخانه C و فراخوانی تابع در برنامه پایتون شما خواهد بود. این عالی است زیرا هیچ مرحله اضافی در فرآیند شما وجود ندارد. شما فقط برنامه خود را اجرا می کنید و همه چیز مراقبت می شود. برای ایجاد اتصالات پایتون خود در ctypes، باید این مراحل را انجام دهید:

  1. کتابخانه خود را بارگیری کنید.
  2. برخی از پارامترهای ورودی خود را بپیچید.
  3. نوع بازگشت عملکرد خود را به ctypes بگویید.

شما به هر یک از اینها به نوبه خود نگاه خواهید کرد.

بارگذاری کتابخانه

Ctypes راه های مختلفی را برای بارگذاری یک کتابخانه مشترک در اختیار شما قرار می دهد که برخی از آنها مختص پلتفرم هستند. برای مثال خود، یک ctypes ایجاد خواهید کرد. شی CDLL مستقیما با عبور از مسیر کامل به کتابخانه مشترک مورد نظر شما:

ctypes_test.py
import ctypes
import pathlib

if __name__ == "__main__":
    # Load the shared library into ctypes
    libname = pathlib.Path().absolute() / "libcmult.so"
    c_lib = ctypes.CDLL(libname)

این برای مواردی که کتابخانه مشترک در همان دایرکتوری اسکریپت پایتون شما قرار دارد کار می کند، اما زمانی که می خواهید کتابخانه هایی را که از بسته هایی غیر از اتصالات پایتون شما هستند بارگذاری کنید، مراقب باشید. جزئیات زیادی برای بارگذاری کتابخانه ها و یافتن مسیرها در مستندات ctypes وجود دارد که پلتفرم و موقعیت خاص هستند.

اکنون که کتابخانه را در پایتون بارگذاری کرده اید، می توانید آن را فراخوانی کنید!

فراخوانی تابع شما

به یاد داشته باشید که نمونه اولیه تابع برای تابع C شما به شرح زیر است:

// cmult.h
float cmult(int int_param, float float_param);

شما باید یک عدد صحیح و یک شناور را ارسال کنید و می توانید انتظار داشته باشید که یک شناور برگردانده شود. اعداد صحیح و شناورها هم در پایتون و هم در C پشتیبانی بومی دارند، بنابراین انتظار دارید که این مورد برای مقادیر معقول کار کند.

هنگامی که کتابخانه را در اتصالات پایتون خود بارگذاری کردید، تابع یک ویژگی c_lib خواهد بود که شی CDLL است که قبلا ایجاد کرده اید. می توانید سعی کنید آن را اینگونه بنامید:

x, y = 6, 2.3
answer = c_lib.cmult(x, y)

اوه! این کار نمی کند. این خط در مخزن مثال توضیح داده شده است زیرا شکست می خورد. اگر بخواهید با آن تماس اجرا کنید، پایتون با خطا شکایت می کند:

invoke test-ctypes
Traceback (most recent call last):
  File "ctypes_test.py", line 16, in <module>
    answer = c_lib.cmult(x, y)
ctypes.ArgumentError: argument 2: <class 'TypeError'>: Don't know how to convert parameter 2

به نظر می رسد که شما باید در مورد هر پارامتری که عدد صحیح نیستند به ctypes بگویید. Ctypes هیچ دانشی از عملکرد ندارد مگر اینکه شما به صراحت آن را بگویید. هر پارامتری که در غیر این صورت علامت گذاری نشده باشد، یک عدد صحیح فرض می شود. ctypes نمی داند چگونه مقدار 2.3 ذخیره شده در y را به یک عدد صحیح تبدیل کند، بنابراین شکست می خورد.

برای رفع این مشکل، باید یک c_float از شماره ایجاد کنید. می توانید این کار را در خطی که تابع را فراخوانی می کنید انجام دهید:

ctypes_test.py
answer = c_lib.cmult(x, ctypes.c_float(y))
print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

اکنون، وقتی این کد را اجرا می کنید، حاصل ضرب دو عددی را که ارسال کرده اید برمی گرداند:

invoke test-ctypes
    In cmult : int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 48.0

یک دقیقه صبر کنید... 6 ضرب در 2.3 48.0 نیست!

به نظر می رسد که ، مانند پارامترهای ورودی ، ctypes فرض می کند که تابع شما یک int را برمی گرداند. در واقع، تابع شما یک شناور را برمی گرداند که به اشتباه مارشال می شود. درست مانند پارامتر ورودی، باید به ctypes بگویید که از نوع دیگری استفاده کنند. نحو در اینجا کمی متفاوت است:

ctypes_test.py
c_lib.cmult.restype = ctypes.c_float
answer = c_lib.cmult(x, ctypes.c_float(y))
print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

این باید ترفند را انجام دهد. بیایید کل هدف test-ctypes را اجرا کنیم و ببینیم چه چیزی دارید. به یاد داشته باشید، اولین بخش خروجی قبل از اینکه تغییر نوع تابع را به عنوان شناور ثابت کنید، است:

invoke test-ctypes
==================================================
= Building C Library
* Complete
==================================================
= Testing ctypes Module
    In cmult : int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 48.0

    In cmult : int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 13.8

این بهتر است! در حالی که نسخه اول تصحیح نشده مقدار اشتباهی را برمی گرداند، نسخه ثابت شما با تابع C موافق است. C و Python هر دو نتیجه یکسانی می گیرند! اکنون که کار می کند، نگاهی بیندازید که چرا ممکن است بخواهید از ctypes استفاده کنید یا نخواهید.

نقاط قوت و ضعف

بزرگترین مزیتی که ctypes نسبت به سایر ابزارهایی که در اینجا بررسی خواهید کرد این است که در کتابخانه استاندارد تعبیه شده است. همچنین نیازی به مراحل اضافی ندارد، زیرا تمام کارها به عنوان بخشی از برنامه پایتون شما انجام می شود.

علاوه بر این، مفاهیم مورد استفاده سطح پایینی هستند، که تمریناتی مانند آنچه انجام دادید را قابل مدیریت می کند. با این حال، کارهای پیچیده تر با عدم اتوماسیون دست و پا گیر می شوند. در بخش بعدی، ابزاری را مشاهده خواهید کرد که مقداری اتوماسیون را به فرآیند اضافه می کند.

CFFI

CFFI رابط تابع خارجی C برای پایتون است. برای تولید اتصالات پایتون رویکرد خودکار تری لازم است. CFFI راه های متعددی دارد که می توانید اتصالات پایتون خود را بسازید و از آنها استفاده کنید. دو گزینه مختلف برای انتخاب وجود دارد که چهار حالت ممکن را در اختیار شما قرار می دهد:

  • ABI در مقابل API: حالت API از یک کامپایلر C برای تولید یک ماژول کامل پایتون استفاده می کند، در حالی که حالت ABI کتابخانه مشترک را بارگیری می کند و مستقیما با آن تعامل دارد. بدون اجرای کامپایلر، درست کردن ساختارها و پارامترها مستعد خطا است. مستندات به شدت استفاده از حالت API را توصیه می کنند.

  • درون خطی در مقابل خارج از خط: تفاوت بین این دو حالت مبادله بین سرعت و راحتی است:

    • حالت درون خطی هر بار که اسکریپت شما اجرا می شود، اتصالات پایتون را کامپایل می کند. این راحت است، زیرا نیازی به مرحله ساخت اضافی ندارید. با این حال، برنامه شما را کند می کند.
    • حالت خارج از خط به یک مرحله اضافی نیاز دارد تا اتصالات پایتون را یک بار تولید کند و سپس هر بار که برنامه اجرا می شود از آنها استفاده می کند. این بسیار سریعتر است، اما ممکن است برای برنامه شما مهم نباشد.

برای این مثال، از حالت خارج از خط API استفاده خواهید کرد که سریع ترین کد را تولید می کند و به طور کلی شبیه به سایر اتصالات پایتون است که بعدا در این آموزش ایجاد خواهید کرد.

نحوه نصب آن

از آنجایی که CFFI بخشی از کتابخانه استاندارد نیست، باید آن را روی دستگاه خود نصب کنید. توصیه می شود برای این کار یک محیط مجازی ایجاد کنید. خوشبختانه، CFFI با pip نصب می شود:

python3 -m pip install cffi

با این کار بسته در محیط مجازی شما نصب می شود. اگر قبلا از requirements.txt نصب کرده اید، باید از این موضوع مراقبت شود. با دسترسی به مخزن در لینک زیر می توانید نگاهی به requirements.txt بیندازید:

اکنون که CFFI را نصب کرده اید، وقت آن است که آن را به چرخش برسانید!

فراخوانی تابع

برخلاف ctypes، با CFFI شما یک ماژول کامل پایتون ایجاد می کنید. شما می توانید ماژول را مانند هر ماژول دیگری در کتابخانه استاندارد وارد کنید. کارهای اضافی وجود دارد که باید برای ساخت ماژول پایتون خود انجام دهید. برای استفاده از اتصالات CFFI پایتون خود، باید مراحل زیر را انجام دهید:

  • مقداری کد پایتون بنویسید که اتصالات را توصیف می کند.
  • آن کد را برای تولید یک ماژول قابل بارگذاری اجرا کنید.
  • کد تماس را برای وارد کردن و استفاده از ماژول جدید ایجاد شده خود تغییر دهید.

این ممکن است کار زیادی به نظر برسد، اما شما هر یک از این مراحل را طی خواهید کرد و خواهید دید که چگونه کار می کند.

نوشتن اتصالات

CFFI روش هایی را برای خواندن یک فایل هدر C ارائه می دهد تا بیشتر کارها را هنگام تولید اتصالات پایتون انجام دهد. در مستندات CFFI، کد انجام این کار در یک فایل پایتون جداگانه قرار می گیرد. برای این مثال، آن کد را مستقیما در فراخوانی ابزار ساخت قرار می دهید که از فایل های پایتون به عنوان ورودی استفاده می کند. برای استفاده از CFFI، با ایجاد یک cffi شروع می کنید. FFI که سه روش مورد نیاز شما را فراهم می کند:

tasks.py
import cffi
...
""" Build the CFFI Python bindings """
print_banner("Building CFFI Module")
ffi = cffi.FFI()

هنگامی که FFI را دریافت کردید، از .cdef() برای پردازش خودکار محتویات فایل هدر استفاده خواهید کرد. این توابع wrapper را برای شما ایجاد می کند تا داده ها را از پایتون مارشال کنید:

tasks.py
this_dir = pathlib.Path().absolute()
h_file_name = this_dir / "cmult.h"
with open(h_file_name) as h_file:
    ffi.cdef(h_file.read())

خواندن و پردازش فایل هدر اولین قدم است. پس از آن، باید از .set_source() برای توصیف فایل منبعی که CFFI تولید می کند استفاده کنید:

tasks.py
ffi.set_source(
    "cffi_example",
    # Since you're calling a fully-built library directly, no custom source
    # is necessary. You need to include the .h files, though, because behind
    # the scenes cffi generates a .c file that contains a Python-friendly
    # wrapper around each of the functions.
    '#include "cmult.h"',
    # The important thing is to include the pre-built lib in the list of
    # libraries you're linking against:
    libraries=["cmult"],
    library_dirs=[this_dir.as_posix()],
    extra_link_args=["-Wl,-rpath,."],
)

در اینجا تفکیک پارامترهایی است که در آن ارسال می کنید:

  • "cffi_example" نام پایه فایل منبعی است که در سیستم فایل شما ایجاد می شود. CFFI یک فایل .c تولید می کند، آن را در یک فایل .o کامپایل می کند و آن را به یک فایل .<system-description>.so یا .<system-description>.dll پیوند می دهد.

  • '#include "cmult.h"' کد منبع C سفارشی است که قبل از کامپایل در منبع تولید شده گنجانده می شود. در اینجا، شما فقط فایل .h را که برای آن اتصال ایجاد می کنید، اضافه می کنید، اما می توان از آن برای برخی از سفارشی سازی های جالب استفاده کرد.

  • libraries=["cmult"] نام کتابخانه C از قبل موجود شما را به پیوند دهنده می گوید. این یک لیست است، بنابراین در صورت لزوم می توانید چندین کتابخانه را مشخص کنید.

  • library_dirs=[this_dir.as_posix(),] لیستی از دایرکتوری ها است که به پیوند دهنده می گوید کجا لیست کتابخانه های بالا را جستجو کند.

  • extra_link_args=['-Wl,-rpath,.'] مجموعه ای از گزینه ها است که یک شی مشترک ایجاد می کند که در مسیر فعلی (.) به دنبال کتابخانه های دیگری است که باید بارگیری شود.

ساخت اتصالات پایتون

فراخوانی .set_source() اتصالات پایتون را ایجاد نمی کند. این فقط ابرداده ها را برای توصیف آنچه تولید می شود تنظیم می کند. برای ساخت اتصالات پایتون، باید .compile() را فراخوانی کنید:

tasks.py
ffi.compile()

این کار با تولید فایل .c، فایل .o و کتابخانه مشترک همه چیز را جمع بندی می کند. وظیفه فراخوانی که به تازگی از آن عبور کردید را می توان در خط فرمان برای ساخت اتصالات پایتون اجرا کرد:

invoke build-cffi
==================================================
= Building C Library
* Complete
==================================================
= Building CFFI Module
* Complete

شما اتصالات CFFI Python خود را دارید، بنابراین وقت آن است که این کد را اجرا کنید!

فراخوانی تابع شما

پس از تمام کارهایی که برای پیکربندی و اجرای کامپایلر CFFI انجام دادید، استفاده از اتصالات پایتون تولید شده درست مانند استفاده از هر ماژول پایتون دیگری به نظر می رسد:

cffi_test.py
import cffi_example

if __name__ == "__main__":
    # Sample data for your call
    x, y = 6, 2.3

    answer = cffi_example.lib.cmult(x, y)
    print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

شما ماژول جدید را وارد می کنید و سپس می توانید cmult() را مستقیما فراخوانی کنید. برای آزمایش آن، از وظیفه test-cffi استفاده کنید:

invoke test-cffi
==================================================
= Testing CFFI Module
    In cmult : int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 13.8

این برنامه cffi_test.py شما را اجرا می کند، که اتصالات جدید پایتون را که با CFFI ایجاد کرده اید آزمایش می کند. این بخش مربوط به نوشتن و استفاده از اتصالات CFFI پایتون شما را تکمیل می کند.

نقاط قوت و ضعف

ممکن است به نظر برسد که ctypes نسبت به مثال CFFI که دیدید به کار کمتری نیاز دارد. در حالی که این برای این مورد استفاده صادق است، CFFI به دلیل اتوماسیون بسیاری از بسته بندی عملکردها، به پروژه های بزرگتر بسیار بهتر از ctypes مقیاس می شود.

CFFI همچنین تجربه کاربری کاملا متفاوتی را ایجاد می کند. ctypes به شما امکان می دهد یک کتابخانه C از قبل موجود را مستقیما در برنامه پایتون خود بارگذاری کنید. از سوی دیگر، CFFI یک ماژول پایتون جدید ایجاد می کند که می تواند مانند سایر ماژول های پایتون بارگذاری شود.

علاوه بر این، با روش خارج از خط API که در بالا استفاده کردید، جریمه زمانی برای ایجاد اتصالات پایتون یک بار در هنگام ساخت آن انجام می شود و هر بار که کد خود را اجرا می کنید، اتفاق نمی افتد. برای برنامه های کوچک، این ممکن است مشکل بزرگی نباشد، اما CFFI از این طریق به پروژه های بزرگتر نیز مقیاس بهتری می دهد.

مانند ctypes، استفاده از CFFI فقط به شما امکان می دهد مستقیما با کتابخانه های C ارتباط برقرار کنید. کتابخانه های C++ برای استفاده نیاز به کار زیادی دارند. در بخش بعدی، یک ابزار اتصال پایتون را مشاهده خواهید کرد که بر روی ++C تمرکز دارد.

پای بایند11

PyBind11 رویکرد کاملا متفاوتی برای ایجاد اتصالات پایتون دارد. علاوه بر تغییر تمرکز از C به C++، از C++ برای مشخص کردن و ساخت ماژول نیز استفاده می کند و به آن اجازه می دهد از ابزارهای فرابرنامه نویسی در C++ استفاده کند. مانند CFFI، اتصالات پایتون تولید شده از PyBind11 یک ماژول کامل پایتون است که می تواند مستقیما وارد و استفاده شود.

PyBind11 از کتابخانه Boost::P ython مدل سازی شده است و رابط کاربری مشابهی دارد. با این حال، استفاده از آن را به C++11 و جدیدتر محدود می کند، که به آن اجازه می دهد تا در مقایسه با Boost که از همه چیز پشتیبانی می کند، کارها را ساده و سرعت بخشد.

نحوه نصب آن

بخش اولین مراحل مستندات PyBind11 شما را با نحوه دانلود و ساخت موارد تست برای PyBind11 آشنا می کند. در حالی که به نظر نمی رسد این کار کاملا مورد نیاز باشد، کار کردن از طریق این مراحل اطمینان حاصل می کند که ابزارهای C++ و پایتون مناسب را راه اندازی کرده اید.

شما می خواهید این ابزار را در محیط مجازی خود نصب کنید:

python3 -m pip install pybind11

PyBind11 یک کتابخانه تمام هدر است، شبیه به بسیاری از Boost. این به pip اجازه می دهد تا منبع واقعی C++ را برای کتابخانه مستقیما در محیط مجازی شما نصب کند.

فراخوانی تابع

قبل از اینکه به آن بپردازید، لطفا توجه داشته باشید که به جای فایل C که برای مثال های قبلی استفاده کرده اید، از یک فایل منبع C++ متفاوتی cppmult.cpp استفاده می کنید. عملکرد اساسا در هر دو زبان یکسان است.

نوشتن صحافی ها

مشابه CFFI، شما باید کدی ایجاد کنید تا به ابزار بگویید که چگونه اتصالات پایتون خود را بسازد. برخلاف CFFI، این کد به جای پایتون در C++ خواهد بود. خوشبختانه، حداقل مقدار کد مورد نیاز است:

// pybind11_wrapper.cpp
#include <pybind11/pybind11.h>
#include <cppmult.hpp>

PYBIND11_MODULE(pybind11_example, m) {
    m.doc() = "pybind11 example plugin"; // Optional module docstring
    m.def("cpp_function", &cppmult, "A function that multiplies two numbers");
}

بیایید هر بار به این قطعه نگاه کنیم، زیرا PyBind11 اطلاعات زیادی را در چند خط بسته بندی می کند.

دو خط اول شامل فایل pybind11.h و فایل هدر برای کتابخانه C++ شما، cppmult.hpp است. پس از آن، ماکرو PYBIND11_MODULE را دارید. این به یک بلوک از کد C++ گسترش می یابد که به خوبی در منبع PyBind11 توضیح داده شده است:

این ماکرو نقطه ورودی را ایجاد می کند که زمانی فراخوانی می شود که مفسر پایتون یک ماژول برنامه افزودنی را وارد می کند. نام ماژول به عنوان اولین آرگومان داده شده است و نباید در گیومه باشد. آرگومان ماکرو دوم متغیری از نوع py::module را تعریف می کند که می تواند برای مقداردهی اولیه ماژول استفاده شود. (منبع)

این برای شما به این معنی است که برای این مثال، شما در حال ایجاد یک ماژول به نام pybind11_example هستید و بقیه کد از m به عنوان نام شی py::module استفاده می کند. در خط بعدی، در داخل تابع C++ که تعریف می کنید، یک docstring برای ماژول ایجاد می کنید. در حالی که این اختیاری است، اما لمس خوبی است که ماژول شما پایتونی تر شود.

در نهایت، شما تماس m.def() را دارید. این تابعی را تعریف می کند که توسط اتصالات جدید پایتون شما صادر می شود، به این معنی که از پایتون قابل مشاهده خواهد بود. در این مثال، شما سه پارامتر را عبور می دهید:

  • cpp_function نام صادر شده تابعی است که در پایتون استفاده خواهید کرد. همانطور که این مثال نشان می دهد، نیازی به مطابقت با نام تابع C++ ندارد.
  • &cppmult آدرس تابعی را که باید صادر شود می گیرد.
  • "یک عملکرد... " یک docstring اختیاری برای تابع است.

اکنون که کد اتصالات پایتون را دارید، نگاهی بیندازید که چگونه می توانید آن را در یک ماژول پایتون بسازید.

ساخت اتصالات پایتون

ابزاری که برای ساخت اتصالات پایتون در PyBind11 استفاده می کنید، خود کامپایلر C++ است. ممکن است لازم باشد پیش فرض های کامپایلر و سیستم عامل خود را تغییر دهید.

برای شروع، باید کتابخانه C++ را بسازید که برای آن اتصالات ایجاد می کنید. به عنوان مثالی به این کوچک، می توانید کتابخانه cppmult را مستقیما در کتابخانه اتصالات پایتون بسازید. با این حال، برای اکثر نمونه های دنیای واقعی، شما یک کتابخانه از قبل موجود خواهید داشت که می خواهید آن را بسته بندی کنید، بنابراین کتابخانه cppmult را جداگانه خواهید ساخت. ساخت یک فراخوانی استاندارد به کامپایلر برای ساخت یک کتابخانه مشترک است:

tasks.py
invoke.run(
    "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC cppmult.cpp "
    "-o libcppmult.so "
)

اجرای آن با invoke build-cppmult libcppmult.so تولید می کند:

invoke build-cppmult
==================================================
= Building C++ Library
* Complete

از سوی دیگر، ساخت اتصالات پایتون به جزئیات خاصی نیاز دارد:

tasks.py
invoke.run(
    "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC "
    "`python3 -m pybind11 --includes` "
    "-I /usr/include/python3.7 -I .  "
    "{0} "
    "-o {1}`python3.7-config --extension-suffix` "
    "-L. -lcppmult -Wl,-rpath,.".format(cpp_name, extension_name)
)

بیایید این خط به خط را مرور کنیم. خط 3 شامل پرچم های کامپایلر C++ نسبتا استاندارد است که چندین جزئیات را نشان می دهد، از جمله اینکه شما می خواهید همه هشدارها گرفته شوند و به عنوان خطا در نظر گرفته شوند، اینکه شما یک کتابخانه مشترک می خواهید و از C++11 استفاده می کنید.

خط 4 اولین قدم جادو است. ماژول pybind11 را فراخوانی می کند تا مسیرهای مناسب را برای PyBind11 تولید کند. می توانید این دستور را مستقیما روی کنسول اجرا کنید تا ببینید چه کاری انجام می دهد:

python3 -m pybind11 --includes
-I/home/jima/.virtualenvs/realpython/include/python3.7m
-I/home/jima/.virtualenvs/realpython/include/site/python3.7

خروجی شما باید مشابه باشد اما مسیرهای متفاوتی را نشان دهد.

در خط 5 فراخوانی کامپایل خود، می توانید ببینید که مسیر را به توسعه دهنده پایتون نیز اضافه می کنید. در حالی که توصیه می شود با خود کتابخانه پایتون پیوند ندهید، منبع به کدی از Python.h نیاز دارد تا جادوی خود را انجام دهد. خوشبختانه، کدی که استفاده می کند در نسخه های پایتون نسبتا پایدار است.

خط 5 همچنین از -I. برای افزودن دایرکتوری فعلی به لیست مسیرهای شامل استفاده می کند. این اجازه می دهد تا خط #include <cppmult.hpp> در کد بسته بندی شما حل شود.

خط 6 نام فایل منبع شما را مشخص می کند که pybind11_wrapper.cpp است. سپس، در خط 7 می بینید که جادوی ساخت دیگری اتفاق می افتد. این خط نام فایل خروجی را مشخص می کند. پایتون ایده های خاصی در مورد نامگذاری ماژول دارد که شامل نسخه پایتون، معماری ماشین و سایر جزئیات است. پایتون همچنین ابزاری را برای کمک به این کار به نام python3.7-config ارائه می دهد:

python3.7-config --extension-suffix
.cpython-37m-x86_64-linux-gnu.so

اگر از نسخه دیگری از پایتون استفاده می کنید، ممکن است لازم باشد دستور را تغییر دهید. اگر از نسخه دیگری از پایتون استفاده می کنید یا از سیستم عامل دیگری استفاده می کنید، احتمالا نتایج شما تغییر خواهد کرد.

خط پایانی دستور build شما، خط 8، پیوند دهنده را به کتابخانه libcppmult که قبلا ساخته اید نشان می دهد. بخش rpath به پیوند دهنده می گوید که اطلاعاتی را به کتابخانه مشترک اضافه کند تا به سیستم عامل کمک کند libcppmult را در زمان اجرا پیدا کند. در نهایت، متوجه خواهید شد که این رشته با cpp_name و extension_name فرمت شده است. هنگامی که ماژول اتصالات پایتون خود را با Cython در بخش بعدی می سازید، دوباره از این تابع استفاده خواهید کرد.

این دستور را برای ساخت اتصالات خود اجرا کنید:

invoke build-pybind11
==================================================
= Building C++ Library
* Complete
==================================================
= Building PyBind11 Module
* Complete

خودشه! شما اتصالات پایتون خود را با PyBind11 ساخته اید. وقت آن است که آن را آزمایش کنید!

فراخوانی تابع شما

مشابه مثال CFFI در بالا، هنگامی که کارهای سنگین ایجاد اتصالات پایتون را انجام دادید، فراخوانی تابع خود مانند کد معمولی پایتون به نظر می رسد:

pybind11_test.py
import pybind11_example

if __name__ == "__main__":
    # Sample data for your call
    x, y = 6, 2.3

    answer = pybind11_example.cpp_function(x, y)
    print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

از آنجایی که از pybind11_example به عنوان نام ماژول خود در ماکرو PYBIND11_MODULE استفاده کرده اید، این نامی است که وارد می کنید. در فراخوانی m.def() به PyBind11 گفتید که تابع cppmult را به صورت cpp_function صادر کند، بنابراین این همان چیزی است که برای فراخوانی آن از پایتون استفاده می کنید.

می توانید آن را با فراخوانی نیز آزمایش کنید:

invoke test-pybind11
==================================================
= Testing PyBind11 Module
    In cppmul: int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 13.8

این چیزی است که PyBind11 به نظر می رسد. در مرحله بعد، خواهید دید که چه زمانی و چرا PyBind11 ابزار مناسبی برای این کار است.

نقاط قوت و ضعف

PyBind11 به جای C بر روی C++ متمرکز شده است که آن را از ctypes و CFFI متمایز می کند. دارای چندین ویژگی است که آن را برای کتابخانه های C++ بسیار جذاب می کند:

  • از کلاس ها پشتیبانی می کند.
  • زیر طبقه بندی چند شکلی را مدیریت می کند.
  • این به شما امکان می دهد ویژگی های پویا را به اشیاء پایتون و بسیاری از ابزارهای دیگر اضافه کنید، که انجام آن از ابزارهای مبتنی بر C که بررسی کرده اید بسیار دشوار است.

همانطور که گفته شد، تنظیمات و پیکربندی زیادی وجود دارد که باید انجام دهید تا PyBind11 راه اندازی و راه اندازی شود. نصب و ساخت صحیح می تواند کمی دشوار باشد، اما پس از انجام این کار، نسبتا محکم به نظر می رسد. همچنین، PyBind11 نیاز دارد که حداقل از C++11 یا جدیدتر استفاده کنید. بعید است که این یک محدودیت بزرگ برای اکثر پروژه ها باشد، اما ممکن است برای شما در نظر گرفته شود.

در نهایت، کد اضافی که برای ایجاد اتصالات پایتون باید بنویسید در C++ است و نه پایتون. این ممکن است برای شما مشکل ساز باشد یا نباشد، اما با سایر ابزارهایی که در اینجا به آنها نگاه کرده اید متفاوت است. در بخش بعدی، به سراغ Cython می روید که رویکرد کاملا متفاوتی به این مشکل دارد.

سیتون

رویکردی که Cython برای ایجاد اتصالات پایتون در پیش می گیرد، از یک زبان شبیه پایتون برای تعریف اتصالات استفاده می کند و سپس کد C یا C++ را تولید می کند که می تواند در ماژول کامپایل شود. روش های مختلفی برای ساخت اتصالات پایتون با Cython وجود دارد. رایج ترین آنها استفاده از تنظیمات از distutils است. برای این مثال، شما به ابزار فراخوانی پایبند خواهید بود، که به شما امکان می دهد با دستورات دقیقی که اجرا می شوند بازی کنید.

نحوه نصب آن

Cython یک ماژول پایتون است که می تواند از PyPI در محیط مجازی شما نصب شود:

python3 -m pip install cython

باز هم، اگر فایل requirements.txt را در محیط مجازی خود نصب کرده باشید، از قبل وجود خواهد داشت. با کلیک بر روی لینک زیر می توانید یک کپی از requirements.txt بگیرید:

این باید شما را برای کار با Cython آماده کند!

فراخوانی تابع

برای ساخت اتصالات پایتون با Cython، مراحل مشابهی را با مواردی که برای CFFI و PyBind11 استفاده کرده اید دنبال خواهید کرد. شما پیوندها را می نویسید، آنها را می سازید و سپس کد پایتون را اجرا می کنید تا آنها را فراخوانی کنید. Cython می تواند از C و C ++ پشتیبانی کند. برای این مثال، از کتابخانه cppmult استفاده خواهید کرد که برای مثال PyBind11 در بالا استفاده کردید.

نوشتن اتصالات

رایج ترین شکل اعلام یک ماژول در Cython استفاده از یک فایل .pyx است:

cython_example.pyx
""" Example cython interface definition """

cdef extern from "cppmult.hpp":
    float cppmult(int int_param, float float_param)

def pymult( int_param, float_param ):
    return cppmult( int_param, float_param )

در اینجا دو بخش وجود دارد:

  1. خطوط 3 و 4 به Cython می گویند که شما از cppmult() از cppmult.hpp استفاده می کنید.
  2. خطوط 6 و 7 یک تابع wrapper به نام pymult() ایجاد می کنند تا cppmult() را فراخوانی کنند.

زبان مورد استفاده در اینجا ترکیب خاصی از C، C++ و پایتون است. با این حال، برای توسعه دهندگان پایتون نسبتا آشنا به نظر می رسد، زیرا هدف آسان تر کردن فرآیند است.

بخش اول با cdef extern... به Cython می گوید که اعلامیه های تابع زیر نیز در فایل cppmult.hpp یافت می شوند. این برای اطمینان از اینکه اتصالات پایتون شما بر اساس همان اعلان های کد C++ شما ساخته شده اند مفید است. بخش دوم شبیه یک تابع معمولی پایتون به نظر می رسد - زیرا اینطور است! این بخش یک تابع پایتون ایجاد می کند که به تابع C++ cppmult دسترسی دارد.

اکنون که اتصالات پایتون را تعریف کرده اید، وقت آن است که آنها را بسازید!

ساخت اتصالات پایتون

فرآیند ساخت Cython شباهت هایی به آنچه برای PyBind11 استفاده کردید دارد. ابتدا Cython را روی فایل .pyx اجرا می کنید تا یک فایل .cpp ایجاد کنید. هنگامی که این کار را انجام دادید، آن را با همان تابعی که برای PyBind11 استفاده کردید کامپایل می کنید:

tasks.py
def compile_python_module(cpp_name, extension_name):
    invoke.run(
        "g++ -O3 -Wall -Werror -shared -std=c++11 -fPIC "
        "`python3 -m pybind11 --includes` "
        "-I /usr/include/python3.7 -I .  "
        "{0} "
        "-o {1}`python3.7-config --extension-suffix` "
        "-L. -lcppmult -Wl,-rpath,.".format(cpp_name, extension_name)
    )

def build_cython(c):
    """ Build the cython extension module """
    print_banner("Building Cython Module")
    # Run cython on the pyx file to create a .cpp file
    invoke.run("cython --cplus -3 cython_example.pyx -o cython_wrapper.cpp")

    # Compile and link the cython wrapper library
    compile_python_module("cython_wrapper.cpp", "cython_example")
    print("* Complete")

شما با اجرای cython در فایل .pyx خود شروع می کنید. چند گزینه وجود دارد که در این دستور استفاده می کنید:

  • --cplus به کامپایلر می گوید که به جای فایل C، یک فایل C++ تولید کند.
  • -3 Cython را تغییر می دهد تا نحو پایتون 3 را به جای پایتون 2 تولید کند.
  • -o cython_wrapper.cpp نام فایلی را که باید تولید شود مشخص می کند.

هنگامی که فایل C++ تولید شد، از کامپایلر C++ برای تولید اتصالات پایتون استفاده می کنید، درست همانطور که برای PyBind11 انجام دادید. توجه داشته باشید که فراخوانی برای تولید مسیرهای اضافی با استفاده از ابزار pybind11 هنوز در آن تابع است. در اینجا به چیزی آسیب نمی رساند، زیرا منبع شما به آنها نیاز نخواهد داشت.

اجرای این وظیفه در فراخوانی این خروجی را تولید می کند:

invoke build-cython
==================================================
= Building C++ Library
* Complete
==================================================
= Building Cython Module
* Complete

می بینید که کتابخانه cppmult را می سازد و سپس ماژول cython را برای بسته بندی آن می سازد. اکنون اتصالات Cython Python را دارید. (سعی کنید سریع بگویید...) وقت آن است که آن را آزمایش کنید!

فراخوانی تابع شما

کد پایتون برای فراخوانی اتصالات جدید پایتون کاملا شبیه به چیزی است که برای آزمایش ماژول های دیگر استفاده کردید:

cython_test.py
import cython_example

Sample data for your call
x, y = 6, 2.3

answer = cython_example.pymult(x, y)
print(f"    In Python: int: {x} float {y:.1f} return val {answer:.1f}")

خط 2 ماژول اتصالات پایتون جدید شما را وارد می کند و شما pymult() را در خط 7 فراخوانی می کنید. به یاد داشته باشید که فایل .pyx یک wrapper پایتون را در اطراف cppmult() ارائه کرد و نام آن را به pymult تغییر داد. استفاده از فراخوانی برای اجرای تست موارد زیر را ایجاد می کند:

invoke test-cython
==================================================
= Testing Cython Module
    In cppmul: int: 6 float 2.3 returning  13.8
    In Python: int: 6 float 2.3 return val 13.8

شما همان نتیجه قبلی را می گیرید!

نقاط قوت و ضعف

Cython یک ابزار نسبتا پیچیده است که می تواند سطح عمیقی از کنترل را هنگام ایجاد اتصالات پایتون برای C یا C++ در اختیار شما قرار دهد. اگرچه در اینجا به طور عمیق به آن پرداخته اید، اما یک روش پایتون برای نوشتن کد ارائه می دهد که به صورت دستی GIL را کنترل می کند، که می تواند به طور قابل توجهی انواع خاصی از مشکلات را تسریع کند.

با این حال، این زبان پایتون کاملا پایتون نیست، بنابراین زمانی که می خواهید بفهمید کدام قسمت های C و پایتون در کجا قرار می گیرند، یک منحنی یادگیری جزئی وجود دارد.

راه حل های دیگر

در حین تحقیق در مورد این آموزش، با چندین ابزار و گزینه مختلف برای ایجاد اتصالات پایتون مواجه شدم. در حالی که من این نمای کلی را به برخی از گزینه های رایج تر محدود کردم، چندین ابزار دیگر نیز وجود دارد که به طور تصادفی با آنها برخورد کردم. لیست زیر جامع نیست. اگر یکی از ابزارهای بالا با پروژه شما مطابقت نداشته باشد، صرفا نمونه ای از احتمالات دیگر است.

PyBindGen

PyBindGen اتصالات پایتون را برای C یا C++ تولید می کند و در پایتون نوشته می شود. هدف آن تولید کد C یا C++ قابل خواندن است که باید مشکلات اشکال زدایی را ساده کند. مشخص نیست که آیا این اخیرا به روز شده است یا خیر، زیرا مستندات پایتون 3.4 را به عنوان آخرین نسخه آزمایش شده فهرست می کند. با این حال، در چند سال گذشته نسخه های سالانه منتشر شده است.

تقویت پایتون

Boost.Python دارای رابطی مشابه PyBind11 است که در بالا مشاهده کردید. این تصادفی نیست، زیرا PyBind11 بر اساس این کتابخانه بود! Boost.Python به طور کامل C++ نوشته شده است و از اکثر نسخه های C++ در اکثر پلتفرم ها پشتیبانی می کند. در مقابل، PyBind11 خود را به C++ مدرن محدود می کند.

Sip

SIP مجموعه ابزاری برای تولید اتصالات پایتون است که برای پروژه PyQt توسعه یافته است. همچنین توسط پروژه wxPython برای تولید اتصالات آنها نیز استفاده می شود. دارای یک ابزار تولید کد و یک ماژول پایتون اضافی است که توابع پشتیبانی را برای کد تولید شده ارائه می دهد.

سی پی پی

CPPYY ابزار جالبی است که هدف طراحی کمی متفاوت از آنچه تاکنون دیده اید دارد. به گفته نویسنده بسته:

"ایده اصلی پشت cppyy (که به سال 2001 برمی گردد) ، این بود که به برنامه نویسان پایتون که در دنیای C ++ زندگی می کنند اجازه دسترسی به آن بسته های C ++ را بدهند ، بدون اینکه مستقیما C ++ را لمس کنند (یا منتظر بمانند تا توسعه دهندگان C ++ بیایند و اتصالات را ارائه دهند)." (منبع)

شیبوکن

Shiboken ابزاری برای تولید اتصالات پایتون است که برای پروژه PySide مرتبط با پروژه Qt توسعه یافته است. در حالی که به عنوان ابزاری برای آن پروژه طراحی شده است، مستندات نشان می دهد که نه مختص Qt است و نه PySide و برای پروژه های دیگر قابل استفاده است.

سوئیگ

SWIG یک ابزار متفاوت از هر یک از ابزارهای دیگر است که در اینجا ذکر شده است. این یک ابزار کلی است که برای ایجاد اتصال به برنامه های C و C++ برای بسیاری از زبان های دیگر، نه فقط پایتون، استفاده می شود. این توانایی برای ایجاد اتصال برای زبان های مختلف می تواند در برخی از پروژه ها بسیار مفید باشد. البته تا آنجا که به پیچیدگی مربوط می شود، هزینه ای دارد.

نتیجه

Congrats! اکنون یک نمای کلی از چندین گزینه مختلف برای ایجاد اتصالات پایتون دارید. شما در مورد جمع آوری داده ها و مسائلی که باید هنگام ایجاد اتصالات در نظر بگیرید، یاد گرفته اید. شما دیده اید که برای فراخوانی یک تابع C یا C++ از پایتون با استفاده از ابزارهای زیر چه چیزی لازم است:

  • Ctypes
  • CFFI
  • پای بایند11
  • سیتون

اکنون می دانید که در حالی که ctypes به شما امکان می دهد یک DLL یا کتابخانه مشترک را مستقیما بارگذاری کنید، سه ابزار دیگر یک قدم اضافی برمی دارند، اما همچنان یک ماژول کامل پایتون ایجاد می کنند. به عنوان یک امتیاز، شما همچنین کمی با ابزار فراخوانی برای اجرای وظایف خط فرمان از پایتون بازی کرده اید. با کلیک بر روی لینک زیر می توانید تمام کدهایی را که در این آموزش مشاهده کردید دریافت کنید:

اکنون ابزار مورد علاقه خود را انتخاب کنید و شروع به ساخت آن اتصالات پایتون کنید! با تشکر ویژه از Loic Domaigne برای بررسی فنی اضافی این آموزش.