C++ sang Python

Bất kỳ mã nào bạn viết bằng bất kỳ ngôn ngữ được biên dịch nào như C, C++ hoặc Java đều có thể được tích hợp hoặc nhập vào tập lệnh Python khác. Mã này được coi như là một "phần mở rộng. "

Một mô-đun mở rộng Python không gì khác hơn là một thư viện C bình thường. Trên các máy Unix, các thư viện này thường kết thúc bằng. vì vậy (đối với đối tượng được chia sẻ). Trên các máy Windows, bạn thường thấy. dll (đối với thư viện được liên kết động)

Điều kiện tiên quyết để viết phần mở rộng

Để bắt đầu viết phần mở rộng của bạn, bạn sẽ cần các tệp tiêu đề Python

  • Trên các máy Unix, điều này thường yêu cầu cài đặt gói dành riêng cho nhà phát triển, chẳng hạn như python2. 5 nhà phát triển

  • Người dùng Windows nhận các tiêu đề này như một phần của gói khi họ sử dụng trình cài đặt Python nhị phân

Ngoài ra, giả định rằng bạn có kiến ​​thức tốt về C hoặc C++ để viết bất kỳ Tiện ích mở rộng Python nào bằng lập trình C

Trước tiên hãy xem Tiện ích mở rộng Python

Để có cái nhìn đầu tiên về mô-đun mở rộng Python, bạn cần nhóm mã của mình thành bốn phần -

  • Tệp tiêu đề Python. h

  • Các hàm C bạn muốn hiển thị dưới dạng giao diện từ mô-đun của mình

  • Một bảng ánh xạ tên các hàm của bạn khi các nhà phát triển Python nhìn thấy chúng với các hàm C bên trong mô-đun mở rộng

  • Hàm khởi tạo

Tệp tiêu đề Python. h

Bạn cần bao gồm Python. h trong tệp nguồn C của bạn, tệp này cho phép bạn truy cập vào API Python nội bộ được sử dụng để nối mô-đun của bạn vào trình thông dịch

Đảm bảo bao gồm Python. h trước bất kỳ tiêu đề nào khác mà bạn có thể cần. Bạn cần làm theo các hàm bạn muốn gọi từ Python

Hàm C

Chữ ký của việc triển khai C các chức năng của bạn luôn có một trong ba dạng sau -

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

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

static PyObject *MyFunctionWithNoArgs( PyObject *self );

Mỗi một trong các khai báo trước đó trả về một đối tượng Python. Không có thứ gọi là hàm void trong Python như trong C. Nếu bạn không muốn các hàm của mình trả về một giá trị, hãy trả về giá trị C tương đương với giá trị Không có của Python. Các tiêu đề Python xác định một macro, Py_RETURN_NONE, thực hiện việc này cho chúng tôi

Tên của các hàm C của bạn có thể là bất cứ thứ gì bạn thích vì chúng không bao giờ được nhìn thấy bên ngoài mô-đun mở rộng. Chúng được định nghĩa là hàm tĩnh

Các hàm C của bạn thường được đặt tên bằng cách kết hợp tên hàm và mô-đun Python với nhau, như được hiển thị ở đây -

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

Đây là một hàm Python được gọi là func bên trong mô-đun mô-đun. Bạn sẽ đặt các con trỏ tới các hàm C của mình vào bảng phương thức cho mô-đun thường xuất hiện tiếp theo trong mã nguồn của bạn

Bảng ánh xạ phương pháp

Bảng phương thức này là một mảng đơn giản gồm các cấu trúc PyMethodDef. Cấu trúc đó trông giống như thế này -

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

Dưới đây là mô tả về các thành viên của cấu trúc này -

  • ml_name - Đây là tên của hàm khi trình thông dịch Python trình bày khi nó được sử dụng trong các chương trình Python

  • ml_meth − Đây phải là địa chỉ của một hàm có bất kỳ chữ ký nào được mô tả trong phần trước

  • ml_flags - Điều này cho trình thông dịch biết chữ ký nào trong số ba chữ ký mà ml_meth đang sử dụng

    • Cờ này thường có giá trị là METH_VAARGS

    • Cờ này có thể được xếp theo bit OR'ed với METH_KEYWORDS nếu bạn muốn cho phép các đối số từ khóa vào hàm của mình

    • Điều này cũng có thể có giá trị METH_NOARGS cho biết bạn không muốn chấp nhận bất kỳ đối số nào

  • ml_doc − Đây là chuỗi tài liệu cho hàm, có thể là NULL nếu bạn không muốn viết

Bảng này cần được kết thúc bằng một trọng điểm bao gồm các giá trị NULL và 0 cho các thành viên thích hợp

Ví dụ

Đối với hàm được xác định ở trên, chúng ta có bảng ánh xạ phương thức sau -

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

Chức năng khởi tạo

Phần cuối cùng của mô-đun mở rộng của bạn là chức năng khởi tạo. Hàm này được gọi bởi trình thông dịch Python khi mô-đun được tải. Hàm bắt buộc phải được đặt tên là initModule, trong đó Mô-đun là tên của mô-đun

Hàm khởi tạo cần được xuất từ ​​thư viện bạn sẽ xây dựng. Các tiêu đề Python xác định PyMODINIT_FUNC để bao gồm các câu thần chú thích hợp để điều đó xảy ra đối với môi trường cụ thể mà chúng tôi đang biên dịch. Tất cả những gì bạn phải làm là sử dụng nó khi xác định chức năng

Hàm khởi tạo C của bạn thường có cấu trúc tổng thể như sau -

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

Đây là mô tả về hàm Py_InitModule3 -

  • func − Đây là chức năng được xuất

  • module_methods - Đây là tên bảng ánh xạ được xác định ở trên

  • docstring - Đây là nhận xét bạn muốn đưa ra trong tiện ích mở rộng của mình

Đặt tất cả những thứ này lại với nhau trông giống như sau -

#include 

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...");
}

Ví dụ

Một ví dụ đơn giản sử dụng tất cả các khái niệm trên -

#include 

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!");
}

Ở đây, hàm Py_BuildValue được sử dụng để tạo giá trị Python. Lưu mã trên vào xin chào. tập tin c. Chúng ta sẽ xem cách biên dịch và cài đặt mô-đun này để được gọi từ tập lệnh Python

Xây dựng và cài đặt tiện ích mở rộng

Gói distutils giúp dễ dàng phân phối các mô-đun Python, cả Python thuần túy và các mô-đun mở rộng, theo cách tiêu chuẩn. Các mô-đun được phân phối ở dạng nguồn và được xây dựng và cài đặt thông qua tập lệnh thiết lập thường được gọi là thiết lập. py như sau

Đối với mô-đun trên, bạn cần chuẩn bị thiết lập sau. tập lệnh py -

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

Bây giờ, hãy sử dụng lệnh sau, lệnh này sẽ thực hiện tất cả các bước biên dịch và liên kết cần thiết, với các lệnh và cờ của trình biên dịch và trình liên kết phù hợp, đồng thời sao chép thư viện động kết quả vào một thư mục thích hợp -

________số 8_______

Trên các hệ thống dựa trên Unix, rất có thể bạn sẽ cần chạy lệnh này với quyền root để có quyền ghi vào thư mục gói trang web. Đây thường không phải là vấn đề trên Windows

Nhập tiện ích mở rộng

Khi bạn đã cài đặt tiện ích mở rộng của mình, bạn có thể nhập và gọi tiện ích mở rộng đó trong tập lệnh Python của mình như sau -

#!/usr/bin/python
import helloworld

print helloworld.helloworld()

Điều này sẽ tạo ra kết quả sau -

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

Truyền tham số hàm

Vì rất có thể bạn sẽ muốn xác định các hàm chấp nhận đối số, bạn có thể sử dụng một trong các chữ ký khác cho các hàm C của mình. Ví dụ, hàm sau, chấp nhận một số tham số, sẽ được định nghĩa như sau -

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

Bảng phương thức chứa một mục nhập cho chức năng mới sẽ trông như thế này -

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

Bạn có thể sử dụng hàm API PyArg_ParseTuple để trích xuất các đối số từ một con trỏ PyObject được chuyển vào hàm C của bạn

Đối số đầu tiên cho PyArg_ParseTuple là đối số args. Đây là đối tượng bạn sẽ phân tích cú pháp. Đối số thứ hai là một chuỗi định dạng mô tả các đối số mà bạn muốn chúng xuất hiện. Mỗi đối số được đại diện bởi một hoặc nhiều ký tự trong chuỗi định dạng như sau

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

Biên dịch phiên bản mới của mô-đun và nhập nó cho phép bạn gọi hàm mới với bất kỳ số lượng đối số thuộc bất kỳ loại nào –

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

Bạn có thể có thể đưa ra nhiều biến thể hơn nữa

Hàm PyArg_ParseTuple

Đây là chữ ký tiêu chuẩn cho hàm PyArg_ParseTuple -

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

Hàm này trả về 0 nếu có lỗi và giá trị khác 0 nếu thành công. tuple là PyObject* là đối số thứ hai của hàm C. Định dạng ở đây là một chuỗi C mô tả các đối số bắt buộc và tùy chọn

Đây là danh sách các mã định dạng cho hàm PyArg_ParseTuple -

CodeC typeMeaningccharMột chuỗi Python có độ dài 1 trở thành ký tự C. ddoubleA Python float trở thành C double. ffloatA Python float trở thành C float. iintA Python int trở thành C int. llongA Python int trở thành C dài. Llong longA Python int trở thành C long longOPyObject*Nhận tham chiếu mượn non-NULL cho đối số Python. schar*Python string không nhúng null vào C char*. s#char*+intBất kỳ chuỗi Python nào tới địa chỉ C và độ dài. t#char*+intBộ đệm một đoạn chỉ đọc tới địa chỉ C và độ dài. uPy_UNICODE*Python Unicode không nhúng null vào C. u#Py_UNICODE*+intAny địa chỉ Python Unicode C và độ dài. w#char*+intRead/ghi bộ đệm một đoạn vào địa chỉ C và độ dài. zchar*Like s, cũng chấp nhận Không (đặt C char* thành NULL). z#char*+intLike s#, cũng chấp nhận Không (đặt C char* thành NULL). (. )theo. Chuỗi Python được coi là một đối số cho mỗi mục. Các đối số sau đây là tùy chọn. Kết thúc định dạng, theo sau là tên chức năng cho các thông báo lỗi. ;Định dạng kết thúc, theo sau là toàn bộ văn bản thông báo lỗi

Giá trị trả về

Py_BuildValue nhận một chuỗi định dạng giống như PyArg_ParseTuple. Thay vì chuyển địa chỉ của các giá trị bạn đang xây dựng, bạn chuyển các giá trị thực. Đây là một ví dụ cho thấy cách triển khai chức năng thêm -

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

Đây là giao diện nếu được triển khai trong Python -

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

Bạn có thể trả về hai giá trị từ hàm của mình như sau, giá trị này sẽ được ghi lại bằng danh sách trong Python

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

Đây là giao diện nếu được triển khai trong Python -

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

Hàm Py_BuildValue

Đây là chữ ký tiêu chuẩn cho hàm Py_BuildValue -

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

Định dạng ở đây là một chuỗi C mô tả đối tượng Python để xây dựng. Các đối số sau của Py_BuildValue là các giá trị C mà từ đó kết quả được xây dựng. Kết quả PyObject* là một tài liệu tham khảo mới

Bảng sau liệt kê các chuỗi mã thường được sử dụng, trong đó không hoặc nhiều chuỗi được nối thành định dạng chuỗi

CodeC typeMeaningccharA C char trở thành chuỗi Python có độ dài 1. ddoubleA C double trở thành một float Python. ffloatA C float trở thành float Python. iintA C int trở thành Python int. llongA C long trở thành Python int. NPyObject*Truyền một đối tượng Python và đánh cắp một tham chiếu. OPyObject*Truyền một đối tượng Python và INCREFs nó như bình thường. O&convert+void*Arbitrary conversionschar*C 0-terminated char* thành chuỗi Python hoặc NULL thành Không. s#char*+intC char* và độ dài thành chuỗi Python hoặc NULL thành Không. uPy_UNICODE*Chuỗi rộng C, kết thúc null thành Python Unicode hoặc NULL thành Không. u#Py_UNICODE*+chuỗi rộng intC và độ dài thành Python Unicode hoặc NULL thành Không. w#char*+intRead/ghi bộ đệm một đoạn vào địa chỉ C và độ dài. zchar*Like s, cũng chấp nhận Không (đặt C char* thành NULL). z#char*+intLike s#, cũng chấp nhận Không (đặt C char* thành NULL). (. )theo. Xây dựng bộ dữ liệu Python từ các giá trị C. [. ]theo. Xây dựng danh sách Python từ các giá trị C. {. }theo. Xây dựng từ điển Python từ các giá trị C, các khóa và giá trị xen kẽ

Mã số {. } xây dựng từ điển từ một số chẵn các giá trị C, luân phiên các khóa và giá trị. Ví dụ: Py_BuildValue("{issi}",23,"zig","zag",42) trả về một từ điển giống như {23 của Python. 'zig', 'zag'. 42}

Mã C có thể chạy bằng Python không?

Việc triển khai mặc định của python được viết bằng lập trình C và được gọi là CPython. Vì vậy việc sử dụng các hàm C trong chương trình python không phải là hiếm .

Cython có tốt hơn Python không?

Hiệu suất và tốc độ . Nó cải thiện đáng kể tốc độ thực thi mã Python bằng cách biên dịch mã Python thành mã C. Quá trình biên dịch tiếp tục giúp các nhà phát triển chạy các chương trình Python một cách trơn tru mà không cần triển khai các tài nguyên máy tính bổ sung. Cython is much faster than Python. It improves Python code execution speed significantly by compiling Python code into C code. The compilation further helps developers to run the Python programs smoothly without deploying additional computing resources.

Cython có dễ học không?

Nếu bạn đã biết Python và có hiểu biết cơ bản về C hoặc C++, bạn sẽ có thể nhanh chóng học Cython . Bạn không cần phải học thêm một ngôn ngữ giao diện nào nữa. Chúng ta có thể coi Cython là hai dự án trong một. Nếu biên dịch Python thành C là âm của Cython, thì giao tiếp C hoặc C++ với Python là dương của nó.

Cython có nhanh bằng C không?

Cython có cùng tốc độ với chương trình C/C++ được điều chỉnh cẩn thận; . Tôi đã thực hiện nhiều điểm chuẩn của mã số cấp thấp khi triển khai SageMath (sử dụng Cython cho một số dòng mã 100K)