تم إعداد ترجمة للمقال خصيصًا لطلاب دورة مطوري Python .
ملحوظة : الكود الموجود في هذه المقالة مرخص بموجب GNU AGPLv3 .لقد كتبت هذا الدليل لأنني لم أجد دليلاً يجمع بين جميع الأشياء المفيدة حول الأنواع. آمل أن تجعل هذه المقالة حياة شخص ما أسهل كثيرًا.المحتوى:- التحسينات الأساسية
- الأنماط
- تجميع بايثون
- الهياكل في بيثون
- اتصل برمزك في C
- تبا
التحسينات الأساسية
قبل إعادة كتابة شفرة مصدر Python في C ، فكر في طرق التحسين الأساسية في Python.هياكل البيانات المضمنة
يتم كتابة هياكل البيانات المضمنة في Python ، مثل set و dict ، في C. تعمل بشكل أسرع بكثير من هياكل البيانات الخاصة بك المكتوبة على شكل فئات Python. يتم وصف هياكل البيانات الأخرى إلى جانب المجموعة القياسية والإملاء والقائمة والمجموعة في وثائق وحدة المجموعات .سرد التعبيرات
بدلاً من إضافة عناصر إلى القائمة باستخدام الطريقة القياسية ، استخدم تعبيرات القائمة.
mapped = []
for value in originallist:
mapped.append(myfunc(value))
mapped = [myfunc(value) in originallist]
الأنواع
تسمح لك وحدة ctypes بالتفاعل مع كود C من Python دون استخدام وحدة subprocess
أو وحدة أخرى مماثلة لبدء عمليات أخرى من CLI.هناك جزءان فقط: تجميع كود C للتحميل في الجودة shared object
وإعداد هياكل البيانات في كودPython لتعيينها على الأنواع C. في هذه المقالة سأقوم بتوصيل رمز Python الخاص بي بـ lcs.c ، والذي يجد أطول مدة متضمنة في جزئين قوائم السلاسل. أريد أن يعمل ما يلي في Python:list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
إحدى المشاكل هي أن هذه الدالة C المحددة هي توقيع دالة تأخذ قوائم السلاسل كنوع وسيطة وتعيد نوعًا ليس له طول ثابت. أحل هذه المشكلة ببنية تسلسلية تحتوي على مؤشرات وطول.تجميع كود C في Python
أولاً ، يتم تجميع شفرة مصدر C ( lcs.c ) lcs.so
للتحميل في Python.gcc -c -Wall -Werror -fpic -O3 lcs.c -o lcs.o
gcc -shared -o lcs.so lcs.o
- - سيعرض الجدار جميع التحذيرات ؛
- - W Will التفاف جميع التحذيرات في الأخطاء.
- - ستنشئ fpic تعليمات مستقلة عن الموقع ستحتاجها إذا كنت تريد استخدام هذه المكتبة في Python ؛
- - O3 يزيد من التحسين ؛
والآن سنبدأ في كتابة رمز Python باستخدام ملف الكائن المشترك الناتج .الهياكل في بيثون
فيما يلي هيكلان للبيانات يتم استخدامها في رمز C الخاص بي.struct Sequence
{
char **items;
int length;
};
struct Cell
{
int index;
int length;
struct Cell *prev;
};
وهنا ترجمة هذه الهياكل إلى بيثون.import ctypes
class SEQUENCE(ctypes.Structure):
_fields_ = [('items', ctypes.POINTER(ctypes.c_char_p)),
('length', ctypes.c_int)]
class CELL(ctypes.Structure):
pass
CELL._fields_ = [('index', ctypes.c_int), ('length', ctypes.c_int),
('prev', ctypes.POINTER(CELL))]
بعض الملاحظات:- جميع الهياكل هي الطبقات التي ترث من
ctypes.Structure
. - الحقل الوحيد
_fields_
هو قائمة بالصفوف. كل صف هو ( <variable-name>
، <ctypes.TYPE>
). - هناك
ctypes
أنواع متشابهة في c_char (char) و c_char_p (* char) . - هناك
ctypes
أيضًا واحد POINTER()
يُنشئ مؤشر نوع من كل نوع يتم تمريره إليه. - إذا كان لديك تعريف عودي مثل في
CELL
، يجب عليك تمرير الإعلان الأولي ، ثم إضافة الحقول من _fields_
أجل الحصول على رابط لنفسك لاحقًا. - نظرًا لأنني لم أستخدم
CELL
Python في الكود الخاص بي ، لم أكن بحاجة إلى كتابة هذه البنية ، ولكن لها خاصية مثيرة للاهتمام في المجال العودي.
اتصل برمزك في C
بالإضافة إلى ذلك ، كنت بحاجة إلى بعض التعليمات البرمجية لتحويل أنواع Python إلى بنى جديدة في C. يمكنك الآن استخدام وظيفة C الجديدة لتسريع كود Python.def list_to_SEQUENCE(strlist: List[str]) -> SEQUENCE:
bytelist = [bytes(s, 'utf-8') for s in strlist]
arr = (ctypes.c_char_p * len(bytelist))()
arr[:] = bytelist
return SEQUENCE(arr, len(bytelist))
def lcs(s1: List[str], s2: List[str]) -> List[str]:
seq1 = list_to_SEQUENCE(s1)
seq2 = list_to_SEQUENCE(s2)
common = lcsmodule.lcs(ctypes.byref(seq1), ctypes.byref(seq2))[0]
ret = []
for i in range(common.length):
ret.append(common.items[i].decode('utf-8'))
lcsmodule.freeSequence(common)
return ret
lcsmodule = ctypes.cdll.LoadLibrary('lcsmodule/lcs.so')
lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
list1 = ['My', 'name', 'is', 'Sam', 'Stevens', '!']
list2 = ['My', 'name', 'is', 'Alex', 'Stevens', '.']
common = lcs(list1, list2)
print(common)
بعض الملاحظات:**char
(قائمة السلاسل) تتطابق مباشرة مع قائمة وحدات البايت في Python.- هناك
lcs.c
وظيفة lcs()
باستخدام Sequence * lcs (بنية التسلسل * s1 ، تسلسل الهيكل * s2) . لإعداد نوع الإرجاع ، أستخدم lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE)
. - لإجراء مكالمة مع الإشارة إلى هيكل التسلسل ، أستخدم
ctypes.byref()
واحدًا يعيد "مؤشر ضوئي" إلى كائنك (أسرع من ctypes.POINTER()
). common.items
- هذه قائمة بالبايت ، يمكن فك تشفيرها للحصول ret
على شكل قائمة str
.- lcsmodule.freeSequence (عام) يحرر الذاكرة المرتبطة بالعام . هذا مهم لأن جامع القمامة (AFAIK) لن يقوم بجمعه تلقائيًا.
رمز Python المحسن هو رمز كتبته في C ولفه في Python.شيء أكثر: PyPy
انتباه: أنا نفسي لم أستخدم PyPy أبدًا.أحد أبسط التحسينات هو تشغيل برامجك في وقت تشغيل PyPy ، الذي يحتوي على مترجم JIT (في الوقت المناسب) يعمل على تسريع عمل الحلقات ، وتجميعها في رمز الجهاز من أجل التنفيذ المتكرر.إذا كان لديك تعليقات أو تريد مناقشة شيء ما ، فاكتب إلي (samuel.robert.stevens@gmail.com).هذا كل شئ. نراكم في الدورة !